0

I have an issue when trying to create nested objects, more specifically creating a parent and its child at the same time.

The child's model has the parent's id as foreign key as can be seen below.

Here is my parent_model:

from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
from PIL import Image

class Work(models.Model):
    user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
    name = models.CharField(max_length=200)
    length = models.IntegerField(null=True)
    width = models.IntegerField(null=True)

    def __str__(self):
        return "{}".format(self.id)

My child_model:

from django.db import models
from .model_work import *
from .model_taxes import *
from djmoney.models.fields import MoneyField

class Price(models.Model):
    work = models.OneToOneField(Work, on_delete=models.CASCADE, related_name='price')
    price = MoneyField(max_digits=19, decimal_places=4, default_currency='USD', null=True)
    total = models.IntegerField(null=True)

    def __str__(self):
        return "{}".format(self.price)

Here is my ParentCreateSerializer:

class WorkCreateSerializer(serializers.ModelSerializer):
    """
    Serializer to create a new Work model in DB
    """
    price = PriceCreateSerializer()

    class Meta:
        model = Work
        fields = [
            'user',
            'price',
            'name',
            'length',
            'width'
        ]

    def create(self, validated_data):
        price_data = validated_data.pop('price')
        work = Work.objects.create(**validated_data)
        price = Price.objects.create(**price_data)
        return work

My ChildCreateSerializer:

class PriceCreateSerializer(serializers.ModelSerializer):
    """
    Serializer to create a new Price when new Work model is created in DB
    """
    # work = WorkDetailsSerializer()
    class Meta:
        model = Price
        fields = [
            'work',
            'price',
            'price_currency',
            'total'
        ]

    def create(self, validated_data):
        work_data = validated_data.pop('work')
        work = Work.objects.create(**work_data)
        price = Price.objects.create(**validated_data)
        return price

When I POST an object as shown below, both the parent and child objects are created but I can't manage giving the child the parent's id as foreign key, so they are not linked.

I have tried linking the child's create serializer to the parent's detail serializer (the commented line in my ChildCreateSerializer) but that creates an error work = WorkDetailsSerializer() NameError: name 'WorkDetailsSerializer' is not defined.

Due to the serializers initialisation, because it seems this creates an infinite loop as explained in this Django: Creating Nested Objects with Reverse Relationship post.

{
    "user":2,
    "price":
        {
            "price":20,
            "price_currency":"EUR",
            "total":32
        },
    "name":"work 42",
    "length":"50",
    "width":"60",
}

Here is the result:

    {
        "id": 33,
        "user": {
            "id": 2,
            "username": "Max",
            "password": "pbkdf2_sha256$180000$WXTaxmhOOTZF$oTx2i/HoZk+lCxHWsRYGVVZcw3/Sy8Micc4YOfaDRaM="
        },
        "price": null,
        "name": "work 42",
        "length": 50,
        "width": 60
    }

I've noticed that I don't enter the "create()" method of the child's serializer.

Does anyone know how to pass to the child the parent's id as foreign key?

Is that done in the "create()" method, and if yes then how can I access it?

Alessio
  • 3,404
  • 19
  • 35
  • 48
MaxenceP
  • 291
  • 2
  • 12

1 Answers1

1

This is because you need to pass the newly created Work instance to your Price serializer. This is done through the "create()" method in your WorkCreateSerializer.

class WorkCreateSerializer(serializers.ModelSerializer):
"""
Serializer to create a new Work model in DB
"""

price = PriceCreateSerializer()

class Meta:
    model = Work
    fields = [
        'user',
        'price',
        'name',
        'length',
        'width',
        'height',
        'depth',
        'weight',
        'creation_year',
        'description',
        'unit_system'
    ]

def create(self, validated_data):
    price_data = validated_data.pop('price')
    work = Work.objects.create(**validated_data)
    Price.objects.create(work=work, **price_data)

    return art_piece

As you can see in the line below, you create a new Price object to which you pass to its field "work" (from the Price model) the newly created "work" instance from the line above.

This other post explains it well too: create() argument after ** must be a mapping, not unicode

Concerning your issue with accessing the "create()" method from the PriceCreateSerializer, I do not know why you don't access it.

Hope this helps!

Seb
  • 363
  • 1
  • 18