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

时间:2019-04-09 16:04:06

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

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

我用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”时,它可以正常工作并警告重复记录。此外,当管理员添加记录时,它会起作用,并且会出现预期的警告。

我猜,这个问题是由于在“ 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())
````

2 个答案:

答案 0 :(得分:1)

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

我也删除了model.py中的这一部分: “类元:     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)
````

答案 1 :(得分:0)

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