Django - ManyToMany through synthetic relation fields

I have such models in my application:

class User(AbstractUser):
    pass


class MyObject(models.Model):
    name = models.CharField(max_length=200, unique=False, db_index=True)

    related_users = models.ManyToManyField(
        User, through='RelatedUsers', related_name='related_users'
    )


class RelatedUsers(models.Model):
    my_object = models.ForeignKey(
        MyObject, related_name='my_object_related_users'
    )
    user = models.ForeignKey(User)
    type = models.CharField(
        max_length=100,
        choices=RelatedUsersTypes.choices()
    )

    class Meta:
        unique_together = ('user', 'my_object', 'type')


class FunctionalityRelatedUsersTypes(BaseChoiceEnum):
    TYPE_1 = 'TYPE 1'
    TYPE_2 = 'TYPE 2'
    TYPE_3 = 'TYPE 3'
    TYPE_4 = 'TYPE 4'
    TYPE_5 = 'TYPE 5'

I'm wondering if there is an option to create some kind of synthetic relations on MyObject. I would like to be able to get users by type using one field, example: related_users_type_1. I'd like to use it in DRF serializer as well (so I can pass just List of ids, and relation will create a Proxy object with the corresponding type).

Pseudocode:

related_users_type_1 = models.RelationField(RelatedUsers, filter={'type': 'TYPE_1'})

Sample payload I want to send:

{
    "related_users_type_1": [1, 2, 3],
    "related_users_type_2": [3]
}

Expected result:

  • 3 RelatedUsers with TYPE_1
  • 1 RelatedUser with TYPE_2

1 answer

  • answered 2018-11-08 08:05 robotHamster

    From the comments we've had, I think your problem can be solved with models as such:

    # just for completeness. should probably use an actual user model
    class User (models.Model):
        name = models.CharField(max_length=64)
    
    class MyObject(models.Model):
        name = models.CharField(max_length=64)
        users = models.ManyToMany(User, through='MyObjectUsers', symmetrical=False)
    
        def add_relations(self, relations):
            for key, list in relations.items():
                for id in list:
                    users.add(user=User.objects.get(pk=id), rel_type=key)
    
    
    
    class MyObjectUsers(models.Model):
        REL_TYPE_CHOICES=(
            ('TYPE1','TYPE1'),
            ('TYPE2','TYPE2')
        )
        myobject = models.ForeignKey('MyObject', null=True, on_delete=models.SET_NULL)
        user = models.ForeignKey('User', null=True, on_delete=models.SET_NULL)
        rel_type = models.CharField(max_length=32, choices = REL_TYPE_CHOICES)
    

    The django documentation shows you how to traverse the relationship.

    With the above implementation of MyObject, you should be able to to call .add_relations({'TYPE1':[1,2,3]}) on a MyObject instance to create the relationships you need.

    I wrote this on my phone so I don't really know if it runs (will test tomorrow)