Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 407
GrantU Avatar asked Nov 04 '13 16:11

GrantU


People also ask

How does Django handle many-to-many relationships?

Django will automatically generate a table to manage many-to-many relationships. You might need a custom “through” model. The most common use for this option is when you want to associate extra data with a many-to-many relationship.

What is many-to-many field in Django?

A ManyToMany field is used when a model needs to reference multiple instances of another model. Use cases include: A user needs to assign multiple categories to a blog post. A user wants to add multiple blog posts to a publication.

What is generic relationship Django?

To define a generic relationship you will need three fields. A ForeignKey field to ContentType . In the above example statement this will be 2, which is the table number. A field to store the primary key of the related object: This will usually be a PositiveIntegerField to match Django's automatic primary key field.


2 Answers

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.

like image 154
Greg Avatar answered Oct 11 '22 21:10

Greg


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.

like image 29
ouk Avatar answered Oct 11 '22 21:10

ouk