16
\$\begingroup\$

I'm new to Django and have just started to learn the basics. I have some questions about how to structure the code, how relationships should be written, and where the business logic should live.

I have written a simple app that should do the following:

  1. Store Group and Member entities in a database.
  2. Maintain a many-to-many relationship between groups and members, as well as member role specific to each group.
  3. To create or update a group it should be possible send a request with the json structure shown below.

    {
      "name": "My group 1",
      "members": [
        { "id": 1, "role": "Leader" },
        { "id": 2, "role": "Regular" }
      ]
    }
    

Questions about the code

  1. Am I re-inventing the wheel with the custom update/create code in the GroupSerializer? Does what I'm trying to do already exist in Django and I have just not found the right approach?
  2. Is the update/create logic in the GroupSerializers misplaced? Should this be in the views.py file?
  3. In serializers.py, line 23, is there a better way than using "source=groupmember_set". I feel dirty hardcoding this string. In additional comments are of course welcome.

I appreciate any feedback that can improve the code and my knowledge further!

models.py

from __future__ import unicode_literals
from django.db import models

class Member(models.Model):
    name = models.CharField(max_length=50)
    def __unicode__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=50)
    members = models.ManyToManyField(Member, blank=True, related_name='groups', through="GroupMember")
    def __unicode__(self):
        return self.name

class GroupMember(models.Model):
    member = models.ForeignKey(Member, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    role = models.CharField(max_length=40)
    def __unicode__(self):
        return self.member.name + " " + self.group.name + " - " + self.role

serializers.py

from rest_framework import serializers
from models import Group, Member, GroupMember
from django.db import transaction


class MemberSerializer(serializers.ModelSerializer):

    class Meta:
        model = Member
        fields = ('id', 'name', 'groups')


class GroupMemberSerializer(serializers.ModelSerializer):
    id = serializers.ReadOnlyField(source='member.id')
    name = serializers.ReadOnlyField(source='member.name')

    class Meta:
        model = GroupMember
        fields = ('id', 'name', 'role')


class GroupSerializer(serializers.ModelSerializer):
    members = GroupMemberSerializer(source="groupmember_set", many=True, read_only=True)

    class Meta:
        model = Project
        fields = '__all__'
        depth = 1

    @transaction.atomic
    def update(self, instance, validated_data):
        # Ignore the fact that i delete and replace. Will diff in the future
        GroupMember.objects.filter(group=instance).delete()
        members = self.initial_data.get("members")
        for member in members:
            id = member.get("id")
            role = member.get("role")
            new_member = Member.objects.get(pk=id)
            GroupMember(group=instance, member=new_member, role=role).save()

        instance.__dict__.update(**validated_data)
        instance.save()
        return instance

    @transaction.atomic
    def create(self, validated_data):
        group = Group.objects.create(**validated_data)
        if "members" in self.initial_data:
            members = self.initial_data.get("members")
            for member in members:
                id = member.get("id")
                role = member.get("role")
                member_instance = Member.objects.get(pk=id)
                GroupMember(group=group, member=member_instance, role=role).save()
        group.save()
        return project

views.py

from rest_framework import viewsets
from models import Group, Member
from serializers import GroupSerializer, MemberSerializer


class GroupViewSet(viewsets.ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer


class MemberViewSet(viewsets.ModelViewSet):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer
\$\endgroup\$
  • 2
    \$\begingroup\$ Thanks for posting this here. It's the first working example I've been able to find on the net for posting nested data with a through model. Mostly working anyway. ;-) \$\endgroup\$ – shanemgrey Dec 19 '17 at 22:40
  • 1
    \$\begingroup\$ Thank you so much for this code snippet! I have been wondering what I am doing wrong for hours until I found this example! \$\endgroup\$ – Ratul Mar 19 '18 at 7:05
  • \$\begingroup\$ You don't have to handle nested representations. \$\endgroup\$ – x-yuri Jan 16 at 18:18
3
\$\begingroup\$

Django-organizations is a great way to do this, if you didn't want to reinvent the wheel. Comes with many things I assume you would need in the future out of the box.

\$\endgroup\$
  • \$\begingroup\$ Thank you for the suggestion but it wasn't the organization domain specifically that I needed a solution for. More how to implement the relation in general. \$\endgroup\$ – Matt Jan 16 at 8:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.