0

I am working with Django REST framework. I want to add data to child model along with the parent model. Basically creating two records at a time.

models.py

class Visit(models.Model):
   name = models.CharField(max_length=200)
   gender = models.CharField(choices=GENDER_CHOICE, max_length=1)
   mobile = models.CharField(max_length=18,default="")
   email = models.CharField(max_length=256, null=True, blank=True)
   address = models.TextField(null=True, blank=True)
   visit_type = models.IntegerField(choices=VISIT_TYPE)
   visit_purpose = models.CharField(max_length=250)
   visitor_photo = models.FileField(upload_to="visitor/",null=True, blank=True)
   id_photo = models.FileField(upload_to="id_card/",null=True, blank=True)
   date_created = models.DateTimeField(default=timezone.now, editable=False)

class Status(models.Model):
   visit = models.ForeignKey(Visit,on_delete=models.CASCADE)
   description = models.CharField(max_length=200)
   from_time = models.DateField()
   to_time = models.DateTimeField(null=True, blank=True)
   aproved = models.BooleanField(default=False)
   visit_complete = models.BooleanField(default=False)
   exit_time = models.DateTimeField(null=True, blank=True)
   date_created = models.DateTimeField(default=timezone.now, editable=False)

serializers.py

class StatusSerializers(serializers.ModelSerializer):
       class Meta:
         model = Status
         fields = "__all__"
class VisitSerializers(serializers.ModelSerializer):
   visit = StatusSerializers(many=True)
   class Meta:
       model = Visit
       fields = "__all__"

   def create(self, validated_data):
      print(validated_data)
      model_b = Visit.objects.create(**validated_data)
      # print(self.context.request.data.get('description'))
      child_model_data = {
         'description': self.context.request.data.get('description'),
      }
      child_model_serializer = StatusSerializers(data=child_model_data)
      child_model_serializer.is_valid(raise_exception=True)
      child_model_serializer.save(status=model_b)

      return model_b

views.py

  @api_view(['GET', 'POST'])
  def create_visitor(request):
    if request.method == 'GET':
       visitor = Visit.objects.all()
       serializer = VisitSerializers(visitor,context={'request': request},many=True)
       return Response(serializer.data)
    elif request.method == 'POST':
       serializer = VisitSerializers(data=request.data,context={'request': request})
    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) In the above code I am able to create child record i.e. Status model record but I don't know how to add other fields to it during its creation.

2 Answers2

2

In your serializer child model data can be found in self.context.request.data and you can simply use child serializer to validate and save in db.

class VisitSerializers(serializers.ModelSerializer):
   class Meta:
       model = Visit
       fields = "__all__"

   def create(self, validated_data):
      print(validated_data)
      model_b = Visit.objects.create(**validated_data)
      child_model_data = {
         'field': self.context['request'].data.get('your_field');
         .....
      }
      child_model_serializer = ChildSerializer(data=child_model_data)
      child_model_serializer.is_valid(raise_exception=True)
      child_model_serializer.save()

      return model_b

Update you need to change in multiple places

First in your view, in post method serializer initialization you need to pass context object.

  @api_view(['GET', 'POST'])
  def create_visitor(request):
     if request.method == 'GET':
        visitor = Visit.objects.all()
        serializer = VisitSerializers(visitor,context={'request': request},many=True)
        return Response(serializer.data)
    elif request.method == 'POST':
        serializer = VisitSerializers(data=request.data, context={'request': request})
        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)

And then in your VisitSerializer

class VisitSerializers(serializers.ModelSerializer):
   visit = StatusSerializers(many=True)  # not sure why you did this, this should be removed i guess
   class Meta:
       model = Visit
       fields = "__all__"

   def create(self, validated_data):
      print(validated_data)
      model_b = Visit.objects.create(**validated_data)
      # print(self.context.request.data.get('description'))
      child_model_data = {
         'description': self.context.request.data.get('description'),
          'status': model_b
          # and make sure all other necessary fields.
      }
      child_model_serializer = StatusSerializers(data=child_model_data)
      child_model_serializer.is_valid(raise_exception=True)
      child_model_serializer.save()

      return model_b
Shakil
  • 4,520
  • 3
  • 26
  • 36
  • Can you please see this post https://stackoverflow.com/questions/55161052/instead-of-primary-key-send-different-field-in-django-rest-framework @shakil – Cipher Mar 14 '19 at 11:30
  • how to pass data {"parent_fileds":"something"} how to pass for child model. also i dont see parent model is related to child mode via FK – Clifton Avil D'Souza Mar 14 '19 at 11:35
  • @CliftonAvilD'Souza you are using `drf-serializer` right ? then in you `request` you can pass relative fields of both of your parent and child model. – Shakil Mar 14 '19 at 11:41
  • File "/home/netzary/cliftonprojects/mvisiter/api/views.py", line 25, in create_visitor serializer.save() File "/home/netzary/cliftonprojects/venvs/mvisiter/lib/python3.6/site-packages/rest_framework/serializers.py", line 214, in save self.instance = self.create(validated_data) File "/home/netzary/cliftonprojects/mvisiter/api/serializer.py", line 30, in create print(self.context.request.data.get('description')) AttributeError: 'dict' object has no attribute 'request' – Clifton Avil D'Souza Mar 14 '19 at 11:42
  • @CliftonAvilD'Souza please make sure you are passing `context={'request': request}` in your post serializer, just like your get method. – Shakil Mar 14 '19 at 11:43
  • i havent changed any thing in views.py is same – Clifton Avil D'Souza Mar 14 '19 at 11:45
  • In your post method ` serializer = VisitSerializers(data=request.data)` should change to ` serializer = VisitSerializers(data=request.data,context={'request': request} )` – Shakil Mar 14 '19 at 11:45
  • { "status": [ "This field is required." ] } in postman – Clifton Avil D'Souza Mar 14 '19 at 11:47
  • what i can see in your post that `status=model_b` so you need to make you , you add that to your child_model_data. – Shakil Mar 14 '19 at 11:49
  • `'description': self.context.request.data.get('description'), AttributeError: 'dict' object has no attribute 'request'` now I am getting this.@Shakil – Clifton Avil D'Souza Mar 14 '19 at 12:24
  • @CliftonAvilD'Souza can you please update your code for this. It will be helpful to me give you appropriate answer. – Shakil Mar 14 '19 at 12:30
  • @CliftonAvilD'Souza edited my response. Please feel free to ask if you have any doubt. – Shakil Mar 14 '19 at 13:20
  • there is nor error as of now.can you tell how to pass fields in Postman or in DRF web interface. when i post i get error like in postman `{ "visit": [ "This field is required." ] }` – Clifton Avil D'Souza Mar 14 '19 at 13:27
  • @CliftonAvilD'Souza why you even added the visit field in your serializer. I just point out a comment that, this seems unnecessary. – Shakil Mar 14 '19 at 13:29
  • @Shakil if i remove that line got `File "/home/netzary/cliftonprojects/mvisiter/api/views.py", line 60, in create_visitor serializer.save() File "/home/netzary/cliftonprojects/venvs/mvisiter/lib/python3.6/site-packages/rest_framework/serializers.py", line 214, in save self.instance = self.create(validated_data) File "/home/netzary/cliftonprojects/mvisiter/api/serializer.py", line 57, in create 'description': self.context.request.data.get('description'), AttributeError: 'dict' object has no attribute 'request' ` – Clifton Avil D'Souza Mar 14 '19 at 13:32
  • Clifton please make sure you are passing `serializer = VisitSerializers(data=request.data, context={'request': request})` this in your post method. If you do his, this error should not come. – Shakil Mar 14 '19 at 13:34
  • please go through [this](https://stackoverflow.com/a/38285000/1339013) for more details. – Shakil Mar 14 '19 at 13:36
  • @CliftonAvilD'Souza can you please try with `self.context['request'].data.get('description')` in your serializer. – Shakil Mar 14 '19 at 15:37
0

You can have extra fields in your serializer along with default fields. For reverse relations, you can add a field like this

class VisitSerializers(serializers.ModelSerializer):
   status = StatusSerializer(many=True)
   class Meta:
       model = Visit

You can pass data to VisitSerializer like this. {, status: [, ]}

Nafees Anwar
  • 6,324
  • 2
  • 23
  • 42
  • { "name":"Clifton", "gender":"M", "visit_type":1, "visit_purpose":"xxxxxxx", status:["description":"xxxxxxx",] } got error as "detail": "JSON parse error - Expecting property name enclosed in double quotes: line 6 column 1 (char 77)" } – Clifton Avil D'Souza Mar 14 '19 at 12:06
  • status should be in quotes. "status" – Nafees Anwar Mar 14 '19 at 12:10