Home > Back-end >  How can I add a related field to serializer instance?
How can I add a related field to serializer instance?

Time:04-20

I have the following model serializer where I send a post request to create a new model instance. The user sending the post request is related to a Company which I want to pass as related model instance to the serializer.

But how to actually define this instance and attach it to the serializer instance within the post view?

# views.py

class OfferList(APIView):
    """
    List all Offers or create a new Offer related to the authenticated user
    """
    def get(self, request):
        offers = Offer.objects.filter(company__userprofile__user=request.user)
        serializer = OfferSerializer(offers, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = OfferSerializer(data=request.data)
        
        # Add related company instance
        company = Company.objects.get(userprofile__user=request.user)
        serializer['company'] = company # this doesn't work
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data,
                            status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# offer/models.py

class Offer(models.Model):
    """
    A table to store Offer instances
    """

    # Relations
    company = models.ForeignKey(Company, on_delete=models.CASCADE)

..
# serializers.py

class OfferSerializer(serializers.ModelSerializer):
    class Meta:
        model = Offer
        fields = '__all__'
user/models.py

class UserProfile(models.Model):
    """
    Extends Base User via 1-1 for profile information
    """
    # Relations
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    company = models.ForeignKey(Company, on_delete=models.CASCADE, null=True)

CodePudding user response:

One simple way is to pass your company instance through your serializer. So maybe changing your post method to something like:

from rest_framework.generics import get_object_or_404

def post(self, request):
    serializer = OfferSerializer(data=request.data)
    
    # Add related company instance
    
    if serializer.is_valid():
        company = get_object_or_404(Company, userprofile__user=request.user)  # raising 404 exception if related company does not exist, 
        # and you're sure that there is one and only one company for this user not more!
        
        serializer.save(company=company)
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

and then change your serializer fields to exclude company field from them (because you are already sending this data through your serializer):

class OfferSerializer(serializers.ModelSerializer):
    class Meta:
        model = Offer
        fields = ["some_field", "other_field"]  # Do not include company in your fields.
        # also note that since I didn't know your Offer's fields I used ["some_field", "other_field"] for fields
    

hope this solves your problem.

CodePudding user response:

Hmm, I think the use of the context for serializers will be a nice way to solve your case.

# serializers.py

class OfferSerializer(serializers.ModelSerializer):
    class Meta:
        model = Offer
        # now that we do not want company from the request body
        exclude = ["company"]

    def create(self, validated_data):
        # add company to the validated data from the context
        # we can feed the context from the APIView
        validated_data["company"] = self.context.get("company", None)
        ...
        return super().create(validated_data)
# views.py

class OfferList(APIView):
    def post(self, request):
        # imo, we do not have to query for UserProfile
        # if the company is assigned to the UserProfile instance for the requestor
        # then, one2one relation can give us this
        context = {"company": request.user.userprofile.company}
        serializer = OfferSerializer(data=request.data, context=context)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  • Related