Django 更新序列化程序和唯一的约束
Posted
技术标签:
【中文标题】Django 更新序列化程序和唯一的约束【英文标题】:Django Update serializer and unique constainst 【发布时间】:2022-01-01 02:17:46 【问题描述】:我正在尝试使用 react 和 django 创建一个膳食计划器。我遇到了表单问题并将其发送到后端。现在我可以从 react 提交表单,并将数据正确地返回给 django。除非我需要为相同的日期和 serviceType('lunch'、'dinner')添加另一个 mealItem。我收到以下字段的唯一约束错误:“meals_meal.menu_id、餐食_meal.date、餐食_meal.type、餐食_meal.url”。
我在序列化程序中有一个未被调用的更新函数。如果存在日期和类型,我该怎么做才能更新数据。
我需要将午餐和晚餐分开,即使它们来自同一日期。
模型.py
class Meal(models.Model):
menu = models.ForeignKey(
Menu,
related_name='meals',
on_delete=models.CASCADE,
)
date = models.DateField(db_index=True)
type = models.CharField(
choices=[
('lunch', 'Lunch'),
('dinner', 'Dinner'),
],
max_length=10,
)
url = models.URLField(max_length=200, default="")
created_at = models.DateTimeField(
auto_now_add=True,
editable=False,
)
updated_at = models.DateTimeField(
auto_now=True,
editable=False,
)
objects = MealQuerySet.as_manager()
class Meta:
unique_together = ['menu', 'date', 'type', 'url']
ordering = ['date', '-type']
class MealItem(models.Model):
meal = models.ForeignKey(
Meal,
related_name='items',
on_delete=models.CASCADE,
)
name = models.CharField(max_length=100)
type = models.CharField(
choices=[
('entre', 'Entre'),
('side', 'Side'),
('other', 'Other'),
],
max_length=10,
)
is_dairy_free = models.BooleanField(
default=False,
verbose_name='D',
help_text='Dairy Free',
)
is_gluten_free = models.BooleanField(
default=False,
verbose_name='G',
help_text='Gluten Free',
)
is_vegetarian = models.BooleanField(
default=False,
verbose_name='V',
help_text='Vegetarian',
)
created_at = models.DateTimeField(
auto_now_add=True,
editable=False,
)
updated_at = models.DateTimeField(
auto_now=True,
editable=False,
)
序列化器.py
class MealSerializer(serializers.ModelSerializer):
items = MealItemSerializer(many=True)
class Meta:
model = Meal
fields = ['id', 'date', 'type','url', 'items', 'menu', ]
validators = []
def create(self, validated_data):
item_data = validated_data.pop('items')
meal= Meal.objects.create(**validated_data)
for item_data in item_data:
MealItem.objects.get_or_create(meal=meal, **item_data)
return meal
def update(sel, instance, validated_data):
instance.id = validated_data.get('id', instance.id)
instance.date = validated_data.get('date', instance.date)
instance.type = validated_data.get('type', instance.type)
instance.save()
return instance
Views.Py
class MealViewSet(LoginRequiredMixin, viewsets.ReadOnlyModelViewSet):
serializer_class = MealSerializer
pagination_class = MealPagination
def get_queryset(self):
if self.request.user.is_authenticated:
queryset = Menu.objects.all()
menu = get_object_or_404(
queryset, pk=self.kwargs['menu_pk'], users=self.request.user)
return Meal.objects.filter(menu=menu)
else:
print("not auth")
return HttpResponseRedirect(self.request, "/login")
def post(self, request, menu_pk):
data = self.request.data
user = self.request.user
if user.is_authenticated and user.has_perm("meals.change_menu"):
if request.method == "POST":
serializer =MealSerializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response('success':"Your post was successfull.")
return Response('failure': 'post was not authenticated')
return Response('failure': "user is not authenticated or does not have permission to submit form")
def update(self, request):
data = self.request.data
user = self.request.user
if user.is_authenticated and user.has_perm("meals.change_menu"):
if request.method == 'PUT':
serializer = MealItemSerializer(instance=self.get_object(), data=data, partial=True )
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response("Success": "Your meal was updated")
这是我需要得到的结果。但是现在我一次只在 items 数组中提交一顿饭。与我在 django admin 添加餐点面板中添加所有三餐相反。
"id": 1,
"date": "2021-11-17",
"type": "lunch",
"url": "#ImageUrlFromFirebase",
"items": [
"name": "milk",
"type": "entre",
"is_dairy_free": false,
"is_gluten_free": false,
"is_vegetarian": true
,
"name": "beans",
"type": "side",
"is_dairy_free": false,
"is_gluten_free": true,
"is_vegetarian": false
,
"name": "sleep",
"type": "other",
"is_dairy_free": true,
"is_gluten_free": false,
"is_vegetarian": false
],
"menu": 1
,
【问题讨论】:
请仅添加相关代码,以及您在创建和更新时调用的端点, 好了,我删除了所有不必要的代码。 【参考方案1】:当请求类型为PUT
时调用更新方法,并且对应于您的视图集上必须有一个update
方法,这似乎缺少?另外,如果更新是部分的(这意味着只有一些字段会被更新)然后partial = True
(必须是),参考https://www.django-rest-framework.org/tutorial/2-requests-and-responses/
理想情况下,我会创建另一个名称为 MealItemViewset
的视图集,然后它上面会有 update
方法,因此我们可以将 creation, deletion and updating
的 Meals
和 MealItems
分开
【讨论】:
我需要在MealItemViewset
中添加 get_queryset 吗?我还将发布功能更改为创建,并在 MealViewset 中添加了更新功能。我可以考虑为这些创建一个新的视图集。现在,当我使用 put 作为请求时,我得到的方法是不允许的。我更新了视图以显示更新功能。以上是关于Django 更新序列化程序和唯一的约束的主要内容,如果未能解决你的问题,请参考以下文章
没有唯一一起验证的 Django Rest Framework 模型序列化程序
Django Rest Framework 中嵌套序列化程序的唯一验证
在 Django Rest Framework 中访问 .data 后更新序列化程序中的值