Updating ManyToMany of a custom user DRF best practice

243 views Asked by At

I've been struggling to understand how one would update a M2M-field in the django rest framework that is between a custom user and some random field. I should mention I'm using Djoser as authentication.

Lets say I have a custom user

Models:

class CustomUser(AbstractUser):
    username = None
    email = models.EmailField(_('email address'), unique=True)
    paying_user = models.BooleanField(default=False)
    subscribed_companies = models.ManyToManyField('myapp.Company')

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserAccountManager()

    def __str__(self):
        return f"{self.email}' account"

class Company(models.Model):
    name = models.CharField(max_length=150)

    def __str__(self):
        return self.name
    
    class Meta:
        ordering = ['name']

My serializers

Imports - serializers.py:
    
        from django.contrib.auth import get_user_model
        from djoser.serializers import UserCreateSerializer
        from rest_framework import serializers
        from apartments.models.company_model import Company
    
        User = get_user_model()

class UserCreateSerializer(UserCreateSerializer):
    class Meta(UserCreateSerializer.Meta):
        model = User
        fields = ('email','password', 'paying_user', 'subscribed_companies')


class UserCompanyListSerializer(serializers.ModelSerializer):
    #Is this a reasonable way to serialize a M2M-field?
    subscribed_company_ids = serializers.PrimaryKeyRelatedField(many=True, read_only=False, 
    queryset=Company.objects.all(), source='subscribed_companies')

    class Meta:
        model = User
        fields = [
            'subscribed_company_ids'
        ]

class CompanySerializer(serializers.ModelSerializer):
    class Meta:
        model = Company
        fields = ('name',)

As you can see, I've attached a M2M-field on the custom user itself, instead of using a OneToOne-field where I store custom data. I'm not sure this is the best way to do it.

The idea is that a user should be able to, on the front end, have a list of companies it wants to subscribe to once they are logged in. That means I'll have many users that can subscribe to many companies.

Where I'm really doubting myself is how I handled the class based views. Since I can retrieve the ID from request.user.id and since I want to replace the entire list of companies, I don't need the PK which identifies a specific company. Therefore, in the put method, I removed the PK parameter. This works.

So my question is - Is there a more clean way to do it? Looking at posts at stackoverflow I couldn't find a decent answer that involved authentication. Am I approaching it wrong?

class UserCompanies(APIView):
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    def get(self, request):
        user_id = request.user.id
        instance = CustomUser.objects.get(id=user_id)
        serializer = UserCompanyListSerializer(instance)
        return Response(serializer.data)

    def put(self, request, format=None):
        user_id = request.user.id
        instance = CustomUser.objects.get(id=user_id)
        serializer = UserCompanyListSerializer(instance, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

How a GET request response would look to localhost:8000/usercompanies/:

{
    "subscribed_company_ids": [
        2,
        1,
        3
    ]
}

How a PUT request response would look to localhost:8000/usercompanies/:

{
    "subscribed_company_ids": [
        2,
        1,
        3,
        5,
        4,
    ]
}

Feedback would be much appreciated, I'm a total DRF newbie.

0

There are 0 answers