【问题标题】:Problem using unique_together and saving authenticated user使用 unique_together 并保存经过身份验证的用户时出现问题
【发布时间】:2019-04-09 16:04:06
【问题描述】:

我的代码中存在两个字段条目唯一性检查的问题。

我用unique_together 定义了一个模型来检查每个用户的字段记录的唯一性,但它接受该用户添加的重复条目。

model.py

from django.db import models
from django.contrib.auth.models import User

class UserItem(models.Model):
    definer = models.ForeignKey(User, on_delete=models.CASCADE)
    item_name = models.CharField(max_length=50)
    .
    .
    class Meta:
        unique_together = ("definer", "item_name")

views.py

from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic.edit import CreateView, UpdateView, DeleteView

class RecordCreateView(LoginRequiredMixin, CreateView):
    model = UserItem
    template_name = 'item_new.html'
    #excluding "definer" field and inserting its value by form_valid
    fields = ['item_name', . . .]   

    def form_valid(self, form):
        form.instance.definer = self.request.user
        return super().form_valid(form)

我希望警告并阻止用户使用之前添加的相同“item_name”添加新记录,但它接受它们(没有警告)。

当我用其他字段替换“定义器”时,它可以正常工作并警告重复记录。此外,当管理员添加记录时,它会起作用并且会出现预期的警告。

我想,这个问题是因为在“unique_together = ("definer", "item_name")" 完成其角色之后,经过身份验证的用户被 "def form_valid" 插入为 "definer"。另一方面,当“定义器”为空时进行唯一性检查。

我应该怎么做才能解决这个问题?

编辑:添加完整模型

```` Full Model in model.py
class UserItem(models.Model):
    item_type = models.CharField(max_length=12, verbose_name='Item type')
    item_name = models.CharField(max_length=50)
    bound = models.CharField(null=True, blank=True, default=None, max_length=4, verbose_name='Bound')
    price = models.FloatField(default=0)
    maximum_use = models.FloatField(default=0, verbose_name='Maximum use (%)’)
    matterial = models.FloatField(null=True, blank=True, default=None, verbose_name='matterial (%)')
    energy = models.FloatField(null=True, blank=True, default=None, verbose_name='energy (kcal/k)')

    definer = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return "{}, name: {}, definer: {}".format(self.item_type, self.item_name, self.definer,)

    def get_absolute_url(self):
        return reverse('profile')

    class Meta:
        unique_together = ("definer", "item_name")
````


```` views.py after @Pedro suggestion to edit
from django.db import IntegrityError
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.contrib.auth.models import User
from django.contrib.auth.mixins import LoginRequiredMixin

class RecordCreateView(LoginRequiredMixin, CreateView):
    model = UserItem
    template_name = 'item_new.html'
    fields = ['item_name', 'matterial', 'energy',]

    def get_success_url(self):
        return reverse('profile')

    def form_valid(self, form):
        user_item = form.save(commit=False)
        user_item.definer = self.request.user
        user_item.item_type = 'required'
        user_item.bound = 'min'
        try:
            user_item.save()
        except IntegrityError:
            form.add_error('item_name', 'Item name is repeated')
            return self.form_invalid(form)
        return HttpResponseRedirect(self.get_success_url())
````

【问题讨论】:

  • 如果你已经用makemigrationsmigrate 将约束迁移到数据库中(如果可以,检查约束是否存在于数据库中),约束违反是不可能的。可以创建违反约束的模型实例,但无法保存它们。因此,请进一步挖掘到底发生了什么。约束不是问题。
  • 我不确定您在form.instance.definer = self.request.user 这一行中在做什么,如果您使用的是创建视图,则您没有实例。你能解释一下吗?
  • @Pedro 我是一个初学者,在这个应用程序中没有 form.py 文件,我从书中的类似代码中得到了这个模式(包括实例)(Django for Beginners, Learn Web Development with Django 2.0 ) 和网页。这是个问题吗?

标签: python django django-models django-orm unique-constraint


【解决方案1】:

感谢@Pedro 非常有用的提示;最后,我可以通过对他的代码进行一些更改来解决我的问题。

我还删除了 model.py 中的这一部分: “类元: unique_together = ("定义器", "item_name")"

````views.py
class RecordCreateView(LoginRequiredMixin, CreateView):
    model = UserItem
    template_name = 'item_new.html'
    fields = ['item_name', 'matterial', 'energy',]

    def form_valid(self, form):
        user_items = form.save(commit=False)
        item_name = user_items.item_name
        qs = UserItemComposition.objects.filter(definer=self.request.user, item_name=item_name)
        if qs.exists():
            form.add_error('item_name', 'Item name is repeated')
            return self.form_invalid(form)
        form.instance.definer = self.request.user
        return super().form_valid(form)
````

【讨论】:

    【解决方案2】:

    问题是您在验证表单后添加definer。您可以将request.user 作为初始数据传递,如下所示:

    class RecordCreateView(LoginRequiredMixin, CreateView):
        model = UserItem
        template_name = 'item_new.html'
        #excluding "definer" field and inserting its value by form_valid
        fields = ['item_name', 'definer', ...]
    
        def get_initial(self):
            initial = super().get_initial()
            initial['definer'] = self.request.user
            return initial
    

    现在您不需要覆盖form_valid

    编辑:如果您不想在表单字段中出现definer,您可以这样做:

    class RecordCreateView(LoginRequiredMixin, CreateView):
        model = UserItem
        template_name = 'item_new.html'
        fields = ['item_name', ...]
    
        def form_valid(self, form):
            user_item = form.save(commit=False)
            user_item.definer = self.request.user
            try:
                user_item.save()  # should raise an exception if unique_together constrain fails
            except ValidationError:
                form.add_error('item_name', 'Item name is repeated')  # add custom error to form
                return self.form_invalid(form)  # return the invalid form
            return HttpResponseRedirect(self.get_success_url())
    

    【讨论】:

    • 感谢佩德罗,这解决了当前的问题,但又引入了另一个问题;用户可以选择其他用户的“定义器”值,因为“定义器”字段具有包括所有注册用户的选项。
    • @mkiani 我添加了一个替代解决方案,请注意您不需要覆盖 get_initial 方法。
    • 感谢编辑,我也用过这个,但没有成功,用户接受了重复。
    • @mkiani 你能发布你的完整模型
    • @mkiani 在将约束添加到模型后,您是否还运行了 makemigrationsmigrate
    猜你喜欢
    • 2019-07-22
    • 2017-12-18
    • 1970-01-01
    • 2016-01-15
    • 2020-11-15
    • 2016-01-17
    • 1970-01-01
    • 1970-01-01
    • 2015-06-21
    相关资源
    最近更新 更多