Django:在基于抽象类创建模型后向User模型添加属性

时间:2012-06-08 17:38:34

标签: django generic-relations

我有一个普通的模型和一个像这样的抽象模型:

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 个答案:

答案 0 :(得分:4)

您可以使用add_to_classclass_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)