Django: целочисленное поле, которое является счетчиком поля ManyToMany - PullRequest
0 голосов
/ 06 ноября 2018

У меня есть модель с полем

countries = models.ManyToManyField(Country, blank=True)

Я хочу новое поле

count = models.IntegerField()

- это число стран, которые также обновляются при добавлении и удалении стран. Я знаю, что есть метод .count(), но я не уверен, как установить в моем поле счета значение.

Ответы [ 3 ]

0 голосов
/ 06 ноября 2018

Я смог сделать это так:

class User(AbstractUser):
    countries = models.ManyToManyField(Country, blank=True)
    count = models.IntegerField(blank=True, default=0)

    def save(self, *args, **kwargs):
        self.count = self.countries.count()
        super(User, self).save(*args, **kwargs)
0 голосов
/ 07 ноября 2018

Ваш ответ будет обновляться только при сохранении самого пользователя, а не только при обновлении отношения m2m. То, что вам нужно, это измененный м2м сигнал .

Я отключил AbstractUser для models.Model, чтобы избежать дополнительной работы здесь, но он протестирован и работает.

from django.db import models
from django.db.models.signals import m2m_changed


class Country(models.Model):
    # ...
    pass

class User(models.Model):
    countries = models.ManyToManyField(Country, related_name="users", blank=True)
    country_count = models.IntegerField(blank=True, default=0)
    # ...

def countries_changed(sender, instance, action, **kwargs):
    # add, remove, and clear all have pre_ and post_ 
    # events that trigger this signal; we only want to 
    # run this -after- one of those events has completed, 
    # to get the final count.
    if action.startswith("post_"):
        instance.country_count = instance.countries.count()
        instance.save()

m2m_changed.connect(countries_changed, sender=User.countries.through)

Пример (запустить в интерактивном режиме ./manage.py shell):

from eh.models import User, Country


user = User()
user.save()

print(user.country_count)
# 0

countries = (Country.objects.create() for i in range(10))

for c in countries:
    user.countries.add(c)
    print(user.country_count)
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# 10

user.countries.remove(user.countries.first())
print(user.country_count)
# 9

user.countries.clear()
print(user.country_count)
# 0

Обратите внимание, однако, что этот не будет отлавливать случаи, когда Страна удаляется (для этого можно дополнительно использовать сигнал pre_delete, связанный с моделью Страны).

0 голосов
/ 06 ноября 2018

Ваш models должен быть примерно таким:

class Country(models.Model):
    count = models.IntegerField()

В вашем views.py всякий раз, когда вы создаете новый объект Country, перед сохранением объекта вы можете сделать:

object.country.count += 1
object.save()
...