Django Rest Framework - Post Foreign Key
Olivia Zamora
I am new to Django Rest Framework and checked some tutorials. Now I am trying to create my own structure which is like following. I want to create a user which is OK, then create a profile seperately.
models.py
class User(models.Model): name = models.CharField(max_length=32) surname = models.CharField(max_length=32) facebook_id = models.TextField(null=True) is_sms_verified = models.BooleanField(default=False) created = models.DateTimeField(default=timezone.now) updated = models.DateTimeField(default=timezone.now) status = models.BooleanField(default=1) def __str__(self): return self.name+" "+self.surname
class Profile(models.Model): user = models.ForeignKey('User',on_delete=models.CASCADE) email = models.CharField(max_length=32) birthday = models.DateField(null=True) bio = models.TextField(null=True) points = models.IntegerField(default=0) created = models.DateTimeField(default=timezone.now) updated = models.DateTimeField(default=timezone.now) def __str__(self): return self.user.name+ " " + self.user.surnameserializers.py
class UserSerializer(serializers.ModelSerializer): class Meta: model=User fields = ('id','name','surname','facebook_id','is_sms_verified',) read_only_fields = ('created','updated')
class ProfileSerializer(serializers.ModelSerializer): user = UserSerializer(read_only=True) class Meta: model=Profile fields=('id','user','email','birthday','bio','points') read_only_fields = ('created','updated')views.py
@api_view(['POST'])
def profile_create(request): serializer = ProfileSerializer(data=request.data) if serializer.is_valid(): serializer.save() return JsonResponse(serializer.data, status = status.HTTP_201_CREATED) return JsonResponse(serializer.errors , status= status.HTTP_400_BAD_REQUEST)data I'm trying to post
{ "user_id": { "id": 2 }, "email": "", "birthday": "1991-05-28", "bio": "qudur", "points": 31
}The error I get;
NOT NULL constraint failed: core_profile.user_idWhere am I doing wrong? Thanks!
2 Answers
Your ProfileSerializer has user as readonly. So you need to change that. I would suggest doing it like this
class ProfileSerializer(serializers.ModelSerializer): class Meta: model=Profile fields=('id','user','email','birthday','gender','bio','points') read_only_fields = ('created','updated') def to_representation(self, instance): self.fields['user'] = UserSerializer(read_only=True) return super(ProfileSerializer, self).to_representation(instance)If you do it this you could provide your user as plain id for POST
{ "user": 2, "email": "", "birthday": "1991-05-28", "bio": "qudur", "points": 31
}And when you will read data it will look like this
{ "user": { "id": 2, "name": "Name", "surname": "Surname", ... }, "email": "", "birthday": "1991-05-28", "bio": "qudur", "points": 31
} 3 I've noticed Super() throws an error the way it's mentioned above in the awnser:
return super(ProfileSerializer,self).to_representation(instance)
Error: Type error, object must be an instance or subtype of type
Try the Following:
Models.py
class Program(models.Model): name = models.CharField(max_length=225) cost = models.IntegerField(default=0) description = models.TextField(default="", max_length=555)
class UserProgram(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) program = models.ForeignKey(Program, on_delete=models.CASCADE, related_name="program")Serializers.py
class ProgramSerializers(serializers.ModelSerializer): class Meta: model = Program fields = "__all__"
class UserProgramSerializers(serializers.ModelSerializer): class Meta: model = UserProgram fields = "__all__" #IMPORTANT PART def to_representation(self, instance): response = super().to_representation(instance) response['program'] = ProgramSerializers(instance.program).data return responseViews.py
class UserProgramViewset(viewsets.ModelViewSet): permission_classes = [ permissions.IsAuthenticated ] serializer_class = UserProgramSerializers def get_queryset(self): return UserProgram.objects.filter(user=self.request.user) def perform_create(self, serializer): serializer.save(user=self.request.user)When you call the GET request the following should be the output:GET Request Output
When you call the POST request you only need to pass the programID and not the whole JSON dictionary!
Hope this helped.