Django: создание вложенных объектов с обратной связью - PullRequest
1 голос
/ 04 мая 2020

У меня проблема при попытке создать вложенный объект с обратной связью.

Я пытаюсь POST a Work, но в то же время пытаюсь POST a Price который Work ребенок.

Вот мой work_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 save(self, *args, **kwargs):
        super(Work, self).save(*args, **kwargs)

        img = Image.open(self.image.path)

        if img.height > 300 or img.width > 300:
            output_size = (300, 300)
            img.thumbnail(output_size)
            img.save(self.image.path)

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

Мой price_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)
    taxes = models.ForeignKey(Taxes, null=True, on_delete=models.SET_NULL)
    total = models.IntegerField(null=True)

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

И мой taxes_model:

from django.db import models

class Taxes(models.Model):
    tax_percentage = models.IntegerField(null=True)
    tax_country = models.CharField(max_length=200, null=True)
    tax_region = models.CharField(max_length=200, null=True)

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

Вот мой work_serializer:

from rest_framework import serializers
from ..models.model_work import Work
from .serializers_user import *
from .serializers_price import *


class WorkIndexSerializer(serializers.ModelSerializer):
    """
    Serializer listing all Works models from DB
    """
    user = UserIndexSerializer()
    price = PriceDetailsSerializer(many=False)

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

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

    price = PriceCreateSerializer(many=False)

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

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

        return work


class WorkDetailsSerializer(serializers.ModelSerializer):
    """
    Serializer showing details of an Works model from DB
    """
    user = UserIndexSerializer()

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

Мой price_serializer:

from rest_framework import serializers
from ..models.model_price import Price
from .serializers_work import *

class PriceIndexSerializer(serializers.ModelSerializer):
    """
    Serializer showing Price information when called by Work GET serializers.
    Not showing 'work' field to avoid loop.
    """
    taxes = serializers.StringRelatedField(read_only=True)
    class Meta:
        model = Price
        fields = [
            'price',
            'price_currency',
            'taxes',
            'total'
        ]
        depth = 1

class PriceDetailsSerializer(serializers.ModelSerializer):
    """
    Serializer showing Price information when called by Work GET serializers.
    Not showing 'work' field to avoid loop.
    """
    taxes = serializers.StringRelatedField(read_only=True)
    class Meta:
        model = Price
        fields = [
            'price',
            'price_currency',
            'taxes',
            'total'
        ]
        depth = 1

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

    def create(self, validated_data):
        work = Work.objects.create(**validated_data)
        price = Price.objects.create(**validated_data)
        taxes = Taxes.objects.create(**validated_data)
        return price

А когда я пытаюсь POST a Work с почтальоном:

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

Я получаю сообщение об ошибке:

TypeError at /works/
conversion from collections.OrderedDict to Decimal is not supported

И когда я пытаюсь POST a Price сам по себе:

{
    "work":20,
    "price":20,
    "price_currency":"EUR",
    "taxes":1,
    "total":32
}

Я получаю сообщение об ошибке:

ValueError at /prices/
Cannot assign "<Money: 20.0000 EUR>": "Work.price" must be a "Price" instance.

Я не могу найти решение, работающее с моим делом.

Что я делаю или отсутствует?

Спасибо вам за ваши ответы!

1 Ответ

1 голос
/ 04 мая 2020
1. TypeError at /works/
conversion from collections.OrderedDict to Decimal is not supported

Решение состоит в том, чтобы передать словарь вложенных цен для создания модели цены вместо целых данных:

class WorkCreateSerializer(serializers.ModelSerializer):

    price = PriceDetailsSerializer(many=False)

...

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

2.

ValueError at /prices/
  Cannot assign "<Money: 20.0000 EUR>": "Work.price" must be a "Price" instance.

Сначала измените рабочее поле на PriceCreateSerializer, так что вы можете иметь рабочие данные в validated_data:

work = WorkDetailsSerializer(many=False)

затем:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...