实例化时 Django REST framework 序列化程序错误
Django REST framework serializer error when instantiating
我在使用 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,抱歉我错过了那个
第二次编辑:根据某些人的建议,我已将产品字段添加到 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...有任何指示吗?
使用 data
字段是正确的方法,因为 DRF Serializer 层次结构中的关键字不是通用的。如果您为 data
指定的字典有效,您可以使用 .save()
创建一个模型实例(在调用 .is_valid()
之后)。在创建模型之前,当然可以使用更多属性来扩充字典。但请注意,序列化程序仅使用在序列化程序的 Meta.fields
字段中指定的属性。
这里是关键点,为什么你的方法最终不起作用:AccomodationSerializer.Meta.fields
不包括 product
字段,如果你想创建模型,这是强制性的。
可以使用 AccommodationSerializer
从 Accommodation
模型中读取,或者如果您出于某种原因想要 post 模型的部分结构。但是如果你想用它来创建一个模型实例,你必须指定所有不可为空或具有默认值的字段。
您可以调用:
而不是在此处使用 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)
,这对我来说似乎是一种反模式。
我在使用 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,抱歉我错过了那个
第二次编辑:根据某些人的建议,我已将产品字段添加到 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...有任何指示吗?
使用 data
字段是正确的方法,因为 DRF Serializer 层次结构中的关键字不是通用的。如果您为 data
指定的字典有效,您可以使用 .save()
创建一个模型实例(在调用 .is_valid()
之后)。在创建模型之前,当然可以使用更多属性来扩充字典。但请注意,序列化程序仅使用在序列化程序的 Meta.fields
字段中指定的属性。
这里是关键点,为什么你的方法最终不起作用:AccomodationSerializer.Meta.fields
不包括 product
字段,如果你想创建模型,这是强制性的。
可以使用 AccommodationSerializer
从 Accommodation
模型中读取,或者如果您出于某种原因想要 post 模型的部分结构。但是如果你想用它来创建一个模型实例,你必须指定所有不可为空或具有默认值的字段。
您可以调用:
而不是在此处使用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)
,这对我来说似乎是一种反模式。