简体   繁体   中英

Django many-to-many generic relationship

I think I need to create a 'many-to-many generic relationship'.

I have two types of Participants:

class MemberParticipant(AbstractParticipant):
    class Meta:
        app_label = 'participants'


class FriendParticipant(AbstractParticipant):
    """
    Abstract participant common information shared for all rewards.
    """
    pass

These Participants can have 1 or more rewards of 2 different kinds (rewards model is from another app):

class SingleVoucherReward(AbstractReward):
    """
    Single-use coupons are coupon codes that can only be used once
    """
    pass

class MultiVoucherReward(AbstractReward):
    """
    A multi-use coupon code is a coupon code that can be used unlimited times.
    """

So now I need to link these all up. This is how I was thinking of creating the relationship (see below) would this work, any issues you see?

Proposed linking model below:

class ParticipantReward(models.Model):


    participant_content_type = models.ForeignKey(ContentType, editable=False,
                                                        related_name='%(app_label)s_%(class)s_as_participant',
                                                        )
    participant_object_id = models.PositiveIntegerField()
    participant = generic.GenericForeignKey('participant_content_type', 'participant_object_id')


    reward_content_type = models.ForeignKey(ContentType, editable=False,
                                                        related_name='%(app_label)s_%(class)s_as_reward',
                                                        )
    reward_object_id = models.PositiveIntegerField()
    reward = generic.GenericForeignKey('reward_content_type', 'reward_object_id')

Note: I'm using Django 1.6

Your approach is exactly the right way to do it given your existing tables. While there's nothing official ( this discussion , involving a core developer in 2007, appears not to have gone anywhere), I did find this blog post which takes the same approach (and offers it in a third-party library), and there's also a popular answer here which is similar, except only one side of the relationship is generic.

I'd say the reason this functionality has never made it into django's trunk is that while it's a rare requirement, it's fairly easy to implement using the existing tools. Also, the chance of wanting a custom "through" table is probably quite high so most end-user implementations are going to involve a bit of custom code anyway.

The only other potentially simpler approach would be to have base Participant and Reward models, with the ManyToMany relationship between those, and then use multi-table inheritance to extend these models as Member/Friend etc.

Ultimately, you'll just need to weigh up the complexity of a generic relation versus that of having your object's data spread across two models.

Late reply, but I found this conversation when looking for a way to implement generic m2m relations and felt my 2 cents would be helpful for future googlers.

As Greg says, the approach you chose is a good way to do it.

However, I would not qualify generic many to many as 'easy to implement using existing tools' when you want to use features such as reverse relations or prefetching.

The 3rd party app django-genericm2m is nice but has several shortcomings in my opinion (the fact that the 'through' objects are all in the same database table by default and that you don't have 'add' / 'remove' methods - and therefore bulk add/remove).

With that in view, because I needed something to implement generic many-to-many relations 'the django way' and also because I wanted to learn a little bit about django internals, I recently released django-gm2m . It has a very similar API to django's built-in GenericForeignKey and ManyToManyField (with prefetching, through models ...) and adds deletion behavior customisation. The only thing it lacks for the moment is a suitable django admin interface.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM