ERROR: Cannot create a Meal instance with a FK Store instance in the views.py (Django and Django Rest Framework)
ERROR: Cannot create a Meal instance with a FK Store instance in the views.py (Django and Django Rest Framework)
我正在交付 API REST 使用 Django 和 Django Rest Framework。
Meal 应用程序中出现错误已经好几天了,我无法解决。我不知道如何解决它。
提前致谢。
这是邮递员中显示的错误:
{
"store": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got Store."
]
}
Meal/models/meals.py
class Meal(models.Model):
'''Meals models.'''
store = models.ForeignKey(
Store,
on_delete=models.CASCADE,
)
name = models.CharField(max_length=120)
slugname = models.SlugField(
unique=True,
max_length=120,
)
description = models.TextField(max_length=300, blank=True)
price = models.DecimalField(
'meal price',
max_digits=5,
decimal_places=2
)
picture = models.ImageField(
'meal picture',
upload_to='meals/pictures/',
blank=True,
null=True,
help_text="Price max up to 9.99"
)
# Status
is_available = models.BooleanField(
'Meal available in menu',
default=True,
help_text='Show is the items is available for customers'
)
# Stats
rating = models.FloatField(
default=5.0,
help_text="Meal's rating based on client califications"
)
Meal/views/meals.py
class MealViewSet(mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
'''Meals view set.
'''
serializer_class = MealModelSerializer
lookup_field = 'slugname'
search_fields = ('slugname', 'name')
# Method call every time this MealViewSet is instanced
def dispatch(self, request, *args, **kwargs):
'''Verify that the store exists.
Add the URL input <store_slugname> to the Meal model field store(FK).
'''
store_slugname = kwargs['store_slugname']
self.store = get_object_or_404(Store, store_slugname=store_slugname)
return super(MealViewSet, self).dispatch(request, *args, **kwargs)
def get_queryset(self):
'''Get Store's available meals'''
return Meal.objects.filter(
store=self.store,
is_available=True
)
def create(self, request, *args, **kwargs):
'''Assign Meal to a Store (received in the URL input <store_slugname>)
'''
store = self.store # Got from the dispatcher
request.data['store'] = store
serializer = MealModelSerializer(
store,
data=request.data
)
# import pdb; pdb.set_trace()
serializer.is_valid(raise_exception=True)
serializer.save()
data = serializer.data
return Response(data, status=status.HTTP_201_CREATED)
和Meal/serializers/meals.py
class MealModelSerializer(serializers.ModelSerializer):
'''Meal model serializer.'''
store = StoreModelSerializer()
class Meta:
"""Meta class."""
model = Meal
fields = (
'store',
'name',
'slugname',
'description',
'price',
'picture',
'rating'
)
read_only_fields = (
'rating',
)
终端中的调试器显示如下:
(Store=dominospizza 是有效的且之前创建的商店)
Pdb) store
<Store: dominospizza>
(Pdb) store.__dict__
{'_state': <django.db.models.base.ModelState object at 0x7fd539882c10>, 'id': 1, 'name': 'Peperoni large pizza', 'store_slugname': 'dominospizza', 'about': '', 'picture': '', 'pickup_address': 'Urdesa cerca de casa de Agapito', 'is_active': True, 'is_open': True, 'orders_dispatched': 0, 'reputation': 5.0}
(Pdb) request.data
{'name': 'Peperoni large pizza', 'slugname': 'LPizzaPeperoni', 'price': '9.00', 'store': <Store: dominospizza>}
请尝试在 MealViewSet.create 中更改此设置:
request.data['store'] = store
通过这个:
request.data['store'] = store.id
这是因为我认为默认的 ModelSerializer fks 像主键相关字段一样被解决,然后它期待一个 pk 而不是实例。
首先,
尽量遵循 REST。如果要根据字段值过滤后端实体,请在查询参数中提供它们。您可以使用 django-filters 自动过滤。参考 this.
其次,
您无需重写 ViewSet 的创建方法即可实现。序列化器就是为此而生的。修改序列化器代码如下,
class MealModelSerializer(serializers.ModelSerializer):
'''Meal model serializer.'''
store = StoreModelSerializer()
store_id = models.PrimaryKeyRelatedField(
queryset=models.Store.objects.all(),
write_only=True,
source="store",
required=False
)
store_slogname = models.SlugRelatedField(
queryset=models.Store.objects.all(),
write_only=True,
slug_field="store_slugname"
source="store",
required=False
)
class Meta:
"""Meta class."""
model = Meal
fields = (
'store',
'name',
'slugname',
'description',
'price',
'picture',
'rating'
)
read_only_fields = (
'rating',
)
但请记住,标准做法是传递 ID 而不是名称。确保商店字段 store_slugname 是唯一的。
从前端,您可以在 POST、PUT、PATCH 主体中传递 store_id 或 store_slugname。序列化程序自动查找数据库并验证数据。您不需要重写任何方法。
编辑#1:
您可以使用 django-rest-framework-filters 功能 AllLookupsFilter 在前端级别提供所有类型的过滤。
示例:
/api/{version}/meals/?store__store_slugname=<value>
/api/{version}/meals/?store__store_slugname__contains=<value>
/api/{version}/meals/?store__store_slugname__in=<value1,value2,value3>
总而言之,您的视图集将如下所示,
class MealViewSet(
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet
):
'''Meals view set.'''
serializer_class = MealModelSerializer
lookup_field = 'slugname'
search_fields = ('slugname', 'name')
filter_class = MealFilter
希望对您有所帮助!
我正在交付 API REST 使用 Django 和 Django Rest Framework。
Meal 应用程序中出现错误已经好几天了,我无法解决。我不知道如何解决它。
提前致谢。
这是邮递员中显示的错误:
{
"store": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got Store."
]
}
Meal/models/meals.py
class Meal(models.Model):
'''Meals models.'''
store = models.ForeignKey(
Store,
on_delete=models.CASCADE,
)
name = models.CharField(max_length=120)
slugname = models.SlugField(
unique=True,
max_length=120,
)
description = models.TextField(max_length=300, blank=True)
price = models.DecimalField(
'meal price',
max_digits=5,
decimal_places=2
)
picture = models.ImageField(
'meal picture',
upload_to='meals/pictures/',
blank=True,
null=True,
help_text="Price max up to 9.99"
)
# Status
is_available = models.BooleanField(
'Meal available in menu',
default=True,
help_text='Show is the items is available for customers'
)
# Stats
rating = models.FloatField(
default=5.0,
help_text="Meal's rating based on client califications"
)
Meal/views/meals.py
class MealViewSet(mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
'''Meals view set.
'''
serializer_class = MealModelSerializer
lookup_field = 'slugname'
search_fields = ('slugname', 'name')
# Method call every time this MealViewSet is instanced
def dispatch(self, request, *args, **kwargs):
'''Verify that the store exists.
Add the URL input <store_slugname> to the Meal model field store(FK).
'''
store_slugname = kwargs['store_slugname']
self.store = get_object_or_404(Store, store_slugname=store_slugname)
return super(MealViewSet, self).dispatch(request, *args, **kwargs)
def get_queryset(self):
'''Get Store's available meals'''
return Meal.objects.filter(
store=self.store,
is_available=True
)
def create(self, request, *args, **kwargs):
'''Assign Meal to a Store (received in the URL input <store_slugname>)
'''
store = self.store # Got from the dispatcher
request.data['store'] = store
serializer = MealModelSerializer(
store,
data=request.data
)
# import pdb; pdb.set_trace()
serializer.is_valid(raise_exception=True)
serializer.save()
data = serializer.data
return Response(data, status=status.HTTP_201_CREATED)
和Meal/serializers/meals.py
class MealModelSerializer(serializers.ModelSerializer):
'''Meal model serializer.'''
store = StoreModelSerializer()
class Meta:
"""Meta class."""
model = Meal
fields = (
'store',
'name',
'slugname',
'description',
'price',
'picture',
'rating'
)
read_only_fields = (
'rating',
)
终端中的调试器显示如下: (Store=dominospizza 是有效的且之前创建的商店)
Pdb) store
<Store: dominospizza>
(Pdb) store.__dict__
{'_state': <django.db.models.base.ModelState object at 0x7fd539882c10>, 'id': 1, 'name': 'Peperoni large pizza', 'store_slugname': 'dominospizza', 'about': '', 'picture': '', 'pickup_address': 'Urdesa cerca de casa de Agapito', 'is_active': True, 'is_open': True, 'orders_dispatched': 0, 'reputation': 5.0}
(Pdb) request.data
{'name': 'Peperoni large pizza', 'slugname': 'LPizzaPeperoni', 'price': '9.00', 'store': <Store: dominospizza>}
请尝试在 MealViewSet.create 中更改此设置:
request.data['store'] = store
通过这个:
request.data['store'] = store.id
这是因为我认为默认的 ModelSerializer fks 像主键相关字段一样被解决,然后它期待一个 pk 而不是实例。
首先,
尽量遵循 REST。如果要根据字段值过滤后端实体,请在查询参数中提供它们。您可以使用 django-filters 自动过滤。参考 this.
其次,
您无需重写 ViewSet 的创建方法即可实现。序列化器就是为此而生的。修改序列化器代码如下,
class MealModelSerializer(serializers.ModelSerializer):
'''Meal model serializer.'''
store = StoreModelSerializer()
store_id = models.PrimaryKeyRelatedField(
queryset=models.Store.objects.all(),
write_only=True,
source="store",
required=False
)
store_slogname = models.SlugRelatedField(
queryset=models.Store.objects.all(),
write_only=True,
slug_field="store_slugname"
source="store",
required=False
)
class Meta:
"""Meta class."""
model = Meal
fields = (
'store',
'name',
'slugname',
'description',
'price',
'picture',
'rating'
)
read_only_fields = (
'rating',
)
但请记住,标准做法是传递 ID 而不是名称。确保商店字段 store_slugname 是唯一的。
从前端,您可以在 POST、PUT、PATCH 主体中传递 store_id 或 store_slugname。序列化程序自动查找数据库并验证数据。您不需要重写任何方法。
编辑#1:
您可以使用 django-rest-framework-filters 功能 AllLookupsFilter 在前端级别提供所有类型的过滤。
示例:
/api/{version}/meals/?store__store_slugname=<value>
/api/{version}/meals/?store__store_slugname__contains=<value>
/api/{version}/meals/?store__store_slugname__in=<value1,value2,value3>
总而言之,您的视图集将如下所示,
class MealViewSet(
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet
):
'''Meals view set.'''
serializer_class = MealModelSerializer
lookup_field = 'slugname'
search_fields = ('slugname', 'name')
filter_class = MealFilter
希望对您有所帮助!