2

I have a normal model and an abstract model like so:

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

The Taggable abstract model class then gets used like so:

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

So if we have a photo object:

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

I can all the users tagged in the photo with photo.tagged_users.all()

I want to add the inverse relation to the user object, so that if I have a user:

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

I can call something like user.tagged_photo_set.all() and have it return all the photo objects.

I suspect that since TaggedSubject connects to the Taggable model on a generic relation that it won't be possible to use it as a through model with a ManyToMany field.

Assuming this is true, this is the function I believe I'd need to add (somehow) to the User model:

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

I'm wondering if it's possible to set it up so that each time a new model class is created based on Taggable, it creates a version of the function above and adds it (ideally as a function that behaves like a property!) to User.

Alternatively, if it is somehow possible to do ManyToMany field connections on a generic relation (which I highly doubt), that would work too.

Finally, if there is a third even cooler option that I am not seeing, I'm certainly open to it.

4

1 に答える 1

4

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)
于 2012-06-08T18:56:43.927 に答える