В моем коде есть проблема с проверкой уникальности ввода двух полей.
Я определил модель с помощью unique_together
для проверки уникальности записей поля для каждого пользователя, но она принимает повторяющиеся записи, добавленные этим пользователем.
модель.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")
просмотры.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», которое было добавлено ранее, но оно принимает их (без предупреждения).
Когда я заменяю «определитель» другими полями, он отлично работает и предупреждает о дублирующихся записях. Кроме того, когда записи добавляются администратором, это работает, и будет ожидаемое предупреждение.
Я предполагаю, что эта проблема связана с тем, что аутентифицированный пользователь вставляется как «определитель» с помощью «def form_valid» после того, как «unique_together = («definer», «item_name»)» выполнил свою роль. С другой стороны, проверка уникальности выполняется, когда «определитель» пуст.
Что мне делать, чтобы решить эту проблему?
Обновлено: добавление полной модели
```` 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())
````
Я не уверен, что вы делаете в этой строке form.instance.definer = self.request.user
, если вы используете представление создания, у вас нет экземпляра. Вы можете объяснить?
@Pedro Я новичок, и у меня нет файла form.py в этом приложении, я получил этот шаблон (включая экземпляр) из аналогичных кодов в книгах («Django для начинающих», «Изучение веб-разработки с Django 2.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())
Спасибо, Педро, это решает текущую проблему, но добавляет другую; пользователь может выбрать других пользователей по значению «определитель», поскольку в поле «определитель» есть параметры, включающие всех зарегистрированных пользователей.
@mkiani Я добавил альтернативное решение, обратите внимание, что вам не нужно переопределять метод get_initial
.
Спасибо за редакцию, я тоже использовал это, но не было успеха, и дубликаты принимаются от пользователя.
@mkiani можешь опубликовать свою полную модель
@mkiani также запускали ли вы makemigrations
и migrate
после добавления ограничения в свою модель?
Я добавил полную модель, а также сделал миграцию.
@mkiani вы пробовали еще раз после миграции?
Да, конечно, я сделал это.
Благодаря очень полезным советам @Pedro; Наконец, я мог решить свою проблему, внеся некоторые изменения в его код.
Также я удалил эту часть в model.py: "мета класса: unique_together = ("определитель", "имя_элемента")"
````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)
````
Если вы перенесли ограничение в базу данных с помощью
makemigrations
иmigrate
(проверьте, присутствует ли ограничение в базе данных, если можете), нарушения ограничений невозможны. Можно создать экземпляры модели, нарушающие ограничение, но их невозможно сохранить. Так что еще немного покопайтесь в том, что именно происходит. Ограничение не проблема.