Django: добавление свойства в модель пользователя после создания модели на основе абстрактного класса

У меня есть нормальная модель и абстрактная модель, например, так:

class TaggedSubject(models.Model):
    user = models.ForeignKey(User, null=True, blank=True)
    category = models.CharField(max_length=200)
    foo = models.CharField(max_length=50)
    bar = models.CharField(max_length=50)
    # etc

    content_type = models.ForeignKey(ContentType)
    content_object_pk = models.CharField(max_length=255)
    content_object = generic.GenericForeignKey("content_type", "content_object_pk")

    def __unicode__(self):
        if self.user:
            return "%s" % (self.user.get_full_name() or self.user.username)
        else:
            return self.label

class Taggable(models.Model):
    tagged_subjects = generic.GenericRelation(TaggedSubject, content_type_field='content_type', object_id_field='content_object_pk')

    @property
    def tagged_users(self):
        return User.objects.filter(pk__in=self.tagged_subjects.filter(user__isnull=False).values("user"))

    class Meta:
        abstract = True

Taggable Класс абстрактной модели затем используется так:

class Photo(Taggable):
    image = models.ImageField(upload_to="foo")
    # ... etc

Итак, если у нас есть фото-объект:

photo = Photo.objects.all()[0]

Я могу всех пользователей отмеченных на фото с помощью photo.tagged_users.all()

Я хочу добавить обратное отношение к объекту пользователя, чтобы при наличии пользователя:

user = User.objects.filter(pk__in=TaggedSubject.objects.exclude(user__isnull=True).values("user"))[0]

Я могу назвать что-то вроде user.tagged_photo_set.all() и пусть он вернет все фото объекты.

Я подозреваю, что поскольку TaggedSubject подключается к модели Taggable по общему признаку, его нельзя будет использовать как through модель с ManyToMany поле.

Предполагая, что это правда, я полагаю, что эту функцию мне нужно (как-то) добавить в модель User:

def tagged_photo_set(self):
    Photo.objects.filter(pk__in=TaggedSubject.objects.filter(user=self, content_type=ContentType.objects.get_for_model(Photo))

Мне интересно, возможно ли настроить его так, чтобы каждый раз, когда новый класс модели создавался на основе Taggable, он создавал версию функции выше и добавлял ее (в идеале как функцию, которая ведет себя как свойство!) Для пользователя,

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

Наконец, если есть третий, даже более прохладный вариант, который я не вижу, я, безусловно, открыт для него.

1 ответ

Решение

Вы могли бы использовать add_to_class и class_prepared сигнал для некоторой постобработки, когда настроены модели, подклассифицирующие ваш базовый класс:

def add_to_user(sender, **kwargs):
    def tagged_FOO_set(self):
        return sender.objects.filter(pk__in=TaggedSubject.objects.filter(
            user=self,
            content_type=ContentType.objects.get_for_model(sender)))

    if issubclass(sender, MyAbstractClass):
        method_name = 'tagged_{model}_set'.format(model=sender.__name__.lower())
        User.add_to_class(method_name, property(tagged_FOO_set))

class_prepared.connect(add_to_user)
Другие вопросы по тегам