使用Django Rest Framework 3.2.2与现有对象可写的嵌套序列化程序 [英] Writable nested serializer with existing objects using Django Rest Framework 3.2.2

查看:58
本文介绍了使用Django Rest Framework 3.2.2与现有对象可写的嵌套序列化程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑一个具有关联类别的清单模型。我想通过对数据进行POST来为现有类别创建新的清单:
{ title: myapp, category:{ name: Business} } ,其中 title 是应创建的列表的标题,而 Business 是用于此新列表的现有类别的名称。

Consider a Listing model that has an associated Category. I want to create a new Listing for an existing Category by doing a POST with data: {"title": "myapp", "category": {"name": "Business"}}, where title is the title of the Listing that should be created, and Business is the name of an existing category to use for this new listing.

当我尝试发出这样的请求并实例化 ListingSerializer [] ,但这并没有改变行为。

When I try to make such a request and instantiate the ListingSerializer for this, I get an error indicating that the Category name must be unique - I don't want to create a new Category, but use an existing one instead. I've tried setting the validators on the category field to [], but that didn't change the behavior.

我可以使用 SlugRelatedField ,但是这迫使我的请求数据看起来更像 { title: myapp, category: 业务} ,这不是我想要的。我尝试对 SlugRelatedField 使用 source 参数来指定嵌套关系,但是这也不起作用:

I can use a SlugRelatedField, but that forces my request data to look more like {"title": "myapp", "category": "Business"}, which isn't what I want. I tried using the source argument for the SlugRelatedField to specify a nested relationship, but that didn't work either:

category = serializers.SlugRelatedField(
        slug_field='category.name',
        queryset=models.Category.objects.all()
    )

收益率:

  "category": [
    "Object with name={'name': 'Business'} does not exist."
  ]

models.py:

models.py:

import django.contrib.auth
from django.db import models
from django.conf import settings

class Profile(models.Model):
    display_name = models.CharField(max_length=255)
    user = models.OneToOneField(settings.AUTH_USER_MODEL)

class Category(models.Model):
    name = models.CharField(max_length=50, unique=True)
    description = models.CharField(max_length=200)

class Listing(models.Model):
    title = models.CharField(max_length=50, unique=True)
    category = models.ForeignKey(Category, related_name='listings', null=True)
    owners = models.ManyToManyField(
        Profile,
        related_name='owned_listings',
        db_table='profile_listing',
        blank=True
    )

serializers.py:

serializers.py:

import logging
import django.contrib.auth
from rest_framework import serializers
import myapp.models as models

logger = logging.getLogger('mylogger')

class ShortUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = django.contrib.auth.models.User
        fields = ('username', 'email')

class ProfileSerializer(serializers.ModelSerializer):
    user = ShortUserSerializer()
    class Meta:
        model = models.Profile
        fields = ('user', 'display_name')
        read_only = ('display_name',)

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Category
        fields = ('name', 'description')
        read_only = ('description',)


class ListingSerializer(serializers.ModelSerializer):
    owners = ProfileSerializer(required=False, many=True)
    # TODO: how to indicate that this should look for an existing category?
    category = CategorySerializer(required=False, validators=[])

    class Meta:
        model = models.Listing
        depth = 2

    def validate(self, data):
        logger.info('inside ListingSerializer validate')
        return data

    def create(self, validated_data):
        logger.info('inside ListingSerializer.create')
        # not even getting this far...

views.py:

import logging

from django.http import HttpResponse
from django.shortcuts import get_object_or_404

import django.contrib.auth

from rest_framework import viewsets
from rest_framework.response import Response

import myapp.serializers as serializers
import myapp.models as models


# Get an instance of a logger
logger = logging.getLogger('mylogger')

class CategoryViewSet(viewsets.ModelViewSet):
    queryset = models.Category.objects.all()
    serializer_class = serializers.CategorySerializer

class UserViewSet(viewsets.ModelViewSet):
    queryset = django.contrib.auth.models.User.objects.all()
    serializer_class = serializers.ShortUserSerializer

class ProfileViewSet(viewsets.ModelViewSet):
    queryset = models.Profile.objects.all()
    serializer_class = serializers.ProfileSerializer

class ListingViewSet(viewsets.ModelViewSet):
    logger.info('inside ListingSerializerViewSet')
    queryset = models.Listing.objects.all()
    serializer_class = serializers.ListingSerializer

完整示例:https://github.com/arw180/drf-example

推荐答案

这不是理想的选择,但是我确实找到了解决我的问题的解决方案(我正等待接受它作为答案,希望其他人可以做得更好)。有两个部分:

This isn't ideal, but I did find a solution that solved my problem (I'm waiting to accept it as the answer, hoping someone else can do better). There are two parts:

首先,在初始化时使用 partial = True 参数ListingSerializer http:// www .django-rest-framework.org / api-guide / serializers /#partial-updates )。然后使用序列化程序的 validate 方法获取与输入数据相对应的实际模型实例。

First, use the partial=True argument when initializing the ListingSerializer ( http://www.django-rest-framework.org/api-guide/serializers/#partial-updates). Then use the serializer's validate method to get the actual model instance corresponding to the input data.

第二,显式删除 CategorySerializer 中的名称字段的验证器。这特别糟糕,因为它不仅影响 ListingSerializer

Second, explicitly remove the validators for the name field in the CategorySerializer. This is especially lousy because it effects more than just the ListingSerializer.

如果遗漏其中任何一个,都会导致在实例化序列化程序时引发验证错误。

Leaving out either piece will result in the validation errors being thrown at the time the serializer is instantiated.

对views.py的修改:

modifications to views.py:

class ListingViewSet(viewsets.ModelViewSet):
    queryset = models.Listing.objects.all()
    serializer_class = serializers.ListingSerializer

    def create(self, request):
        serializer = serializers.ListingSerializer(data=request.data,
            context={'request': request}, partial=True)
        if not serializer.is_valid():
            logger.error('%s' % serializer.errors)
            return Response(serializer.errors,
                  status=status.HTTP_400_BAD_REQUEST)

        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)

对serializers.py的修改:

modifications to serializers.py:

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Category
        fields = ('name', 'description')
        read_only = ('description',)
        # also need to explicitly remove validators for `name` field
        extra_kwargs = {
            'name': {
                'validators': []
            }
        }

class ListingSerializer(serializers.ModelSerializer):
    owners = ProfileSerializer(required=False, many=True)
    category = CategorySerializer(required=False)
    class Meta:
        model = models.Listing
        depth = 2

    def validate(self, data):
        # manually get the Category instance from the input data
        data['category'] = models.Category.objects.get(name=data['category']['name'])
        return data

    def create(self, validated_data):
        title = validated_data['title']

        listing = models.Listing(title=validated_data['title'],
            category=validated_data['category'])

        listing.save()

        if 'owners' in validated_data:
            logger.debug('owners: %s' % validated_data['owners'])
            for owner in validated_data['owners']:
                print ('adding owner: %s' % owner)
                listing.owners.add(owner)
        return listing

我会请稍等一下以接受答案,以防万一有人可以提出更好的解决方案(例如如何使 source 参数与一起正常工作SlugRelatedField )-在 https:// github上,我有一个使用上述解决方案的工作示例.com / arw180 / drf-example (如果要尝试)。我也很想听听有关为何在 CategorySerializer 中需要 extra_kwargs 东西的评论-为什么不实例化的原因像这样: category = CategorySerializer(required = False,validators = [])是否足够(在 ListingSerializer 中)?更新:我相信这是行不通的,因为唯一验证器是从数据库约束中自动添加的,并且无论在此处设置的任何明确验证器如何运行都可以运行,如以下答案中所述: /3bf20dfabe1f/python-order-of-serializer-validation-in-django-rest-framework.html rel = noreferrer> http://iswwwup.com/t/3bf20dfabe1f/python-order-of-serializer-validation -in-django-rest-framework.html

I'll wait a bit to accept this as the answer in case someone can come up with a better solution (like how to make the source argument work properly with a SlugRelatedField) - I have a working example using the solution above at https://github.com/arw180/drf-example if you want to experiment. I'd also love to hear comments regarding why the extra_kwargs stuff is necessary in the CategorySerializer - why isn't instantiating it like this: category = CategorySerializer(required=False, validators=[]) sufficient (in the ListingSerializer)? UPDATE: I believe that doesn't work because the unique validator is added automatically from the DB constraints and run regardless of any explicit validators set here, as explained in this answer: http://iswwwup.com/t/3bf20dfabe1f/python-order-of-serializer-validation-in-django-rest-framework.html

这篇关于使用Django Rest Framework 3.2.2与现有对象可写的嵌套序列化程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆