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)