Проблема с использованием unique_together и сохранением аутентифицированного пользователя

В моем коде есть проблема с проверкой уникальности ввода двух полей.

Я определил модель с помощью 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())
````

Если вы перенесли ограничение в базу данных с помощью makemigrations и migrate (проверьте, присутствует ли ограничение в базе данных, если можете), нарушения ограничений невозможны. Можно создать экземпляры модели, нарушающие ограничение, но их невозможно сохранить. Так что еще немного покопайтесь в том, что именно происходит. Ограничение не проблема.

Endre Both 09.04.2019 18:44

Я не уверен, что вы делаете в этой строке form.instance.definer = self.request.user, если вы используете представление создания, у вас нет экземпляра. Вы можете объяснить?

p14z 09.04.2019 19:00

@Pedro Я новичок, и у меня нет файла form.py в этом приложении, я получил этот шаблон (включая экземпляр) из аналогичных кодов в книгах («Django для начинающих», «Изучение веб-разработки с Django 2.0») и веб-страниц. Это проблема?

m kiani 09.04.2019 19:17
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
3
294
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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())

Спасибо, Педро, это решает текущую проблему, но добавляет другую; пользователь может выбрать других пользователей по значению «определитель», поскольку в поле «определитель» есть параметры, включающие всех зарегистрированных пользователей.

m kiani 09.04.2019 19:45

@mkiani Я добавил альтернативное решение, обратите внимание, что вам не нужно переопределять метод get_initial.

p14z 09.04.2019 20:18

Спасибо за редакцию, я тоже использовал это, но не было успеха, и дубликаты принимаются от пользователя.

m kiani 09.04.2019 21:43

@mkiani можешь опубликовать свою полную модель

p14z 09.04.2019 22:01

@mkiani также запускали ли вы makemigrations и migrate после добавления ограничения в свою модель?

p14z 09.04.2019 22:08

Я добавил полную модель, а также сделал миграцию.

m kiani 09.04.2019 22:17

@mkiani вы пробовали еще раз после миграции?

p14z 09.04.2019 22:56

Да, конечно, я сделал это.

m kiani 09.04.2019 23:21

Благодаря очень полезным советам @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)
````

Другие вопросы по теме