1

DRF と一部のシリアライザーで奇妙な問題が発生しています。

これが私のモデルです:

class Accommodation(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)
    product = models.OneToOneField(
        Product,
        on_delete=models.CASCADE,
        primary_key=True,
    )
    description = models.TextField(null=True, blank=True, verbose_name=_(u'Description'))
    shared_accommodation = models.BooleanField(default=False)
    accommodation_unit_quantity = models.PositiveSmallIntegerField(default=1,
                                                                verbose_name=_(u'Number of acommodation units '
                                                                              u'for this acommodation'))
    accommodation_unit_name = models.TextField(null=False, blank=False, verbose_name=_(u'Name for accommodation units '
                                                                                   u'for this accommodation'))

    class Meta:
        verbose_name_plural = _(u'Accommodations')

    def __unicode__(self):
        return u'{0} <{1}>'.format(self.product.name, self.product.school)

class Product(AbstractProduct):

    name = models.CharField(max_length=50, verbose_name=_(u'Name'))
    school = models.ForeignKey('school.School')
    levels = models.ManyToManyField('school.Level',verbose_name=_(u'Level'))
    age = IntegerRangeField(null=True)
    gender = models.CharField(choices=GENDER_CHOICES, max_length=1, null=True, blank=True, verbose_name=_(u'Gender'))
    num_sessions = models.PositiveSmallIntegerField(
        verbose_name=_(u'Number of sessions'),
        default=1,
        help_text=_(u"Number of sessions that the product has."),
    )
    school_category = models.ForeignKey(
        'school.Category',
        blank=True, null=True,
        verbose_name=_(u'Category')
    )
    addons = models.ManyToManyField('self',
        verbose_name=_(u'Administrators'),
        through='AddonInService',
        symmetrical=False,
        related_name='addon_can_be_used_in'
    )

    pay_option = models.CharField(choices=PAYMENT_OPTIONS, max_length=1, null=True, blank=True, verbose_name=_(u'Pay_option'), default='U')
    payment_type = models.CharField(choices=PAYMENT_TYPE, max_length=1, null=True, blank=True, verbose_name=_(u'pay_type'))
    payment_amount = models.FloatField(verbose_name=_(u'Amount'), default=0.0)

    objects = ProductManager()

    class Meta(AbstractProduct.Meta):
        verbose_name_plural = _(u'Products')

    def __unicode__(self):
        return self.name

ご覧のとおり、基本的に製品は宿泊施設にすることができます。ここにシリアライザーがあります

class AccommodationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Accommodation
        fields = [
            'description',
            'shared_accommodation',
            'accommodation_unit_quantity',
            'accommodation_unit_name',
        ]


class ProductAccommodationSerializer(ProductSerializer):

    accommodation = AccommodationSerializer()

    class Meta(ProductSerializer.Meta):
        fields = [
            'id',
            'structure',
            'upc',
            'title',
            'slug',
            'description',
            'rating',
            'date_created',
            'date_updated',
            'is_discountable',
            'name',
            'age',
            'gender',
            'num_sessions',
            'parent',
            'product_class',
            'school',
            'levels',
            'school_category',
            'addons',
            'color',
            'price',
            'all_prices',
            'variants',
            'pay_option',
            'payment_type',
            'payment_amount',
            'accommodation',
        ]

    def create(self, validated_data):
        accommodation_data = validated_data.pop('accommodation')

        levels = []
        if 'levels' in validated_data:
            levels = validated_data.pop('levels')

        product = Product.objects.create(**validated_data)
        school_accommodation, created = ProductClass.objects.get_or_create(name='School Accommodation')
        if created:
            product.product_class = school_accommodation
        for lev in levels:
            product.levels.add(lev)
        product.save()

        acc = AccommodationSerializer(product=product, **accommodation_data)
        acc.save()
        return product

class ProductSerializer(serializers.ModelSerializer):
    age = IntegerRangeField()
    addons = AddonSerializer(many=True, read_only=True)
    # Get the price for the Product, using the property in the Model
    price = serializers.DecimalField(required=False, max_digits=7,
                                 decimal_places=2, source='get_price',
                                 read_only=True)
    color = serializers.SerializerMethodField()

    all_prices = PriceSerializer(source='stockrecords', many=True,
                                   required=False)

    variants = VariantSerializer(many=True, source='children', required=False)

    class Meta:
        model = Product
        fields = [
            'id',
            'structure',
            'upc',
            'title',
            'slug',
            'description',
            'rating',
            'date_created',
            'date_updated',
            'is_discountable',
            'name',
            'age',
            'gender',
            'num_sessions',
            'parent',
            'product_class',
            'school',
            'levels',
            'school_category',
            'addons',
            'color',
            'price',
            'all_prices',
            'variants',
            'pay_option',
            'payment_type',
            'payment_amount'
        ]

宿泊施設を作成しようとする簡単なテストを実行すると、次のエラーが発生します。

Traceback (most recent call last):
File "/home/internetmosquito/git/mvp_opencoast/opencoast_django/opencoast/applications/accommodation/tests/test_accommodations.py", line 165, in test_create_accommodation
response = self.client.post(url, data, format='json')
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/test.py", line 170, in post
path, data=data, format=format, content_type=content_type, **extra)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/test.py", line 92, in post
return self.generic('POST', path, data, content_type, **extra)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/django/test/client.py", line 380, in generic
return self.request(**r)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/test.py", line 159, in request
return super(APIClient, self).request(**kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/test.py", line 111, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/django/test/client.py", line 467, in request
six.reraise(*exc_info)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 149, in get_response
response = self.process_exception_by_middleware(e, request)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 147, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/viewsets.py", line 87, in view
return self.dispatch(request, *args, **kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/views.py", line 466, in dispatch
response = self.handle_exception(exc)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/views.py", line 463, in dispatch
response = handler(request, *args, **kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/mixins.py", line 21, in create
self.perform_create(serializer)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/mixins.py", line 26, in perform_create
serializer.save()
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 191, in save
self.instance = self.create(validated_data)
File "/home/internetmosquito/git/mvp_opencoast/opencoast_django/opencoast/applications/accommodation/serializers.py", line 77, in create
acc = AccommodationSerializer(product=product, **accommodation_data)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 95, in __init__
super(BaseSerializer, self).__init__(**kwargs)
TypeError: __init__() got an unexpected keyword argument 'product'

を削除しようとした

商品=商品

から

acc = AccommodationSerializer(product=product, **accommodation_data)

しかし、同じエラーが表示されますが、製品の代わりに「shared_accommodation」フィールドがあります

ここで何が間違っていますか?何か案は?

編集: ProductSerializer を追加しました。

2番目の編集: 一部の人が示唆しているように、製品フィールドを AccommodationSerializer に追加しました:

class AccommodationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Accommodation
        fields = [
            'product',
            'description',
            'shared_accommodation',
            'accommodation_unit_quantity',
            'accommodation_unit_name',
        ]

しかし、インスタンスを作成しようとすると、次のエラーが発生します。

{'accommodation': OrderedDict([('product', [u'This field is required.'])])}

面白いことに、製品をテスト データ ペイロードに追加すると (宿泊施設を作成するためにエンドポイントを呼び出した時点で製品を作成していなくても、上記のエラーは消えます)。

data = {
        "name": "Hotel Hudson",
        "slug": "hotel-hudson",
        "age": {'upper': 99, 'lower': 18},
        "school": school1.id,
        "levels": [school1.level_set.all()[0].id],
        "accommodation": {
            "product": 1,
            "description": "A very nice hotel",
            "shared_accommodation": False,
            "accommodation_unit_quantity": 1,
            "accommodation_unit_name": "Room",
            "accommodation_units": [
                {
                    'name': "Room-1",
                    'max_pax': 1,
                },
                {
                    'name': "Room-2",
                    'max_pax': 3,
                },
            ]
        },
    }

これは興味深いことですが、これは明らかに私が望んでいるものではありません...宿泊施設を作成するためにエンドポイントを呼び出すときに、偽の製品 ID を渡す必要はありません...ポインタはありますか?

4

1 に答える 1

3

dataDRF シリアライザー階層のキーワードは一般的ではないため、フィールドを使用するのが正しい方法です。指定した辞書が有効な場合は、 ( を呼び出した後)dataを使用してモデル インスタンスを作成できます。もちろん、モデルを作成する前に、ディクショナリにさらに属性を追加することもできます。ただし、シリアライザーは、シリアライザーのフィールドで指定された属性のみを使用することに注意してください。.save().is_valid()Meta.fields

そして、ここに重要な点があります。あなたのアプローチが結局うまくいかない理由です:モデルを作成したい場合は必須AccomodationSerializer.Meta.fieldsのフィールドが含まれていません。product

AccommodationSerializerモデルからの読み込みAccommodationや、何らかの理由でモデルの部分構造を投稿したい場合に使用しても問題ありません。ただし、それを使用してモデル インスタンスを作成する場合は、null 許容ではない、またはデフォルト値を持つすべてのフィールドを指定する必要があります。

hereを使用する代わりに、次のAccommodationSerializerように呼び出すことができます。

    Accommodation.objects.create(product=product, **accommodation_data)

最小限の例を設定しようとしました。お役に立てれば。

models.py:

class Owner(models.Model):

    owner_name = models.CharField(max_length=255)


class Product(models.Model):

    name = models.CharField(max_length=255)
    owner = models.OneToOneField(Owner)

serializer.py

class OwnerSerializer(serializers.ModelSerializer):

    class Meta:
        model = Owner
        fields = [
            'owner_name',
        ]

class ProductSerializer(serializers.ModelSerializer):

    owner = OwnerSerializer(read_only=True)

    class Meta:
        model = Product
        fields = [
            'owner',
            'name',
        ]


class ProductOwnerSerializer(serializers.ModelSerializer):

    product = ProductSerializer()

    class Meta:
        model = Owner
        fields = [
            'product',
            'owner_name',
        ]

    def create(self, validated_data):
        product_data = validated_data.pop('product')
        owner = Owner.objects.create(**validated_data)
        Product.objects.create(owner=owner, **product_data)
        return owner

私は本当に嫌いなJerzykのコメントにも同意します 、私にMeta(Superclass)はアンチパターンのようです。

于 2016-07-09T16:46:56.013 に答える