Проблема с использованием unique_together и сохранением аутентифицированного пользователя - PullRequest
1 голос
/ 09 апреля 2019

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

Я определил модель с помощью 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", добавленным ранее, но этопринимает их (без предупреждения).

Когда я заменяю «определитель» другими полями, он работает нормально и предупреждает о дублировании записей.Кроме того, когда записи добавляются администратором, это работает, и появляется ожидаемое предупреждение.

Я полагаю, эта проблема заключается в том, что аутентифицированный пользователь вставляется как «определитель» с помощью «def form_valid» после «unique_together =»("определитель", "имя_элемента") "выполнил свою роль.С другой стороны, проверка уникальности выполняется, когда «определитель» пуст.

Что я должен сделать, чтобы решить эту проблему?

Редактировать: Добавление полной модели

```` 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 ]

1 голос
/ 10 апреля 2019

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

Проблема в том, что вы добавляете 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())
...