使用 unique_together 和保存经过身份验证的用户时出现问题

Problem using unique_together and saving authenticated user

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

我用 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" 的新记录,但它接受了它们(没有警告)。

当我用其他字段替换 "definer" 时,它工作正常并警告重复记录。此外,当管理员添加记录时,它会起作用,并且会有预期的警告。

我想,这个问题是因为 "def form_valid" 在“unique_together = ("definer", "item_name")”已经发挥了作用。另一方面,当 "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())
````

问题是您在验证表单后添加 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())

感谢@Pedro 非常有用的提示;最后我可以通过修改他的代码来解决我的问题。

我还在 model.py 中删除了这部分: “class 元: unique_together = ("definer", "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)
````