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 updatingMealsMealItems 分开

【讨论】:

我需要在 MealItemViewset 中添加 get_queryset 吗?我还将发布功能更改为创建,并在 MealViewset 中添加了更新功能。我可以考虑为这些创建一个新的视图集。现在,当我使用 put 作为请求时,我得到的方法是不允许的。我更新了视图以显示更新功能。

以上是关于Django 更新序列化程序和唯一的约束的主要内容,如果未能解决你的问题,请参考以下文章

没有唯一一起验证的 Django Rest Framework 模型序列化程序

Django Rest Framework 中嵌套序列化程序的唯一验证

在 Django Rest Framework 中访问 .data 后更新序列化程序中的值

Django 可写嵌套序列化程序更新

在 Django Rest Framework 中正确更新嵌套序列化程序

Django 深度序列化 - 遵循反向外键约束