Как проверить / очистить () уникальное = True поле без использования ModelForm? - PullRequest
12 голосов
/ 13 октября 2009

В пользовательской Форме, как можно проверить уникальность поля Модели (т. Е. Установлен unique=True)?

Я знаю, что ModelForm django автоматически выполняет функцию validate_unique(), которая вызывается в методе clean() BaseModelForm - поэтому при использовании ModelForm это будет обрабатываться правильно (как в Admin).

Тем не менее, я создаю свою собственную форму с нуля и удивляюсь, как я могу справиться с этим самостоятельно? Я думаю, что мой самый большой камень преткновения - это знание, какой объект прикреплен к форме при очистке данных ...

Код:

class UserProfile(CreatedModifiedModel):
    user            = models.ForeignKey(User, unique=True)
    display_name    = models.CharField('Display Name',max_length=30,
                        blank=True,unique=True)

class EditUserProfileForm(forms.Form):
    display_name    = forms.CharField(required=False,max_length=30)

    # "notifications" are created from a different model, not the UserProfile
    notifications    = forms.MultipleChoiceField(
                        label="Email Notifications",
                        required=False,
                        widget=forms.CheckboxSelectMultiple,)

    def clean_display_name(self):
        # how do I run my own validate_unique() on this form?
        # how do I know which UserProfile object I am working with?

    # more code follows, including the __init__ which sets up the notifications

Ответы [ 2 ]

16 голосов
/ 13 октября 2009

Уникальная проверка трудно получить полностью правильно, поэтому я бы рекомендовал использовать ModelForm в любом случае:

class EditUserProfileForm(forms.ModelForm):
    # "notifications" are created from a different model, not the UserProfile
    notifications    = forms.MultipleChoiceField(
                        label="Email Notifications",
                        required=False,
                        widget=forms.CheckboxSelectMultiple,)

    class Meta:
        model = UserProfile
        fields = ('display_name',)

Создать форму из нескольких моделей непросто, но в этом случае вы можете просто добавить поле notifications в ModelForm и вытянуть его из .cleaned_data как обычно:

# view
if request.method == 'POST':
    form = EditUserProfileForm(request.POST, instance=user_profile)
    if form.is_valid():
        user_profile = form.save()
        notifications = form.cleaned_data['notifications']
        # Do something with notifications.

Вот как я бы это сделал, но если вы настроены на проверку уникальности самостоятельно, вы всегда можете сделать что-то вроде:

def clean_display_name(self):
    display_name = self.cleaned_data['display_name']
    if UserProfile.objects.filter(display_name=display_name).count() > 0:
        raise ValidationError('This display name is already in use.')
    return display_name

Здесь я вижу две проблемы. Во-первых, вы можете столкнуться с проблемами параллелизма, когда два человека отправляют одно и то же имя, оба проходят уникальные проверки, но затем один получает ошибку БД. Другая проблема заключается в том, что вы не можете редактировать профиль пользователя, потому что у вас нет идентификатора, который нужно исключить из поиска. Вы должны сохранить его в своем __init__, а затем использовать его для очистки:

def __init__(self, *args, **kwargs):
    ...
    if 'instance' in kwargs:
        self.id = kwargs['instance'].id
    ...

def clean_display_name(self):
    display_name = self.cleaned_data['display_name']
    qs = UserProfile.objects.filter(display_name=display_name)
    if self.id:
        qs = qs.exclude(pk=self.id)
    if qs.count() > 0:
        raise ValidationError('This display name is already in use.')
    return display_name

Но в этот момент вы просто дублируете логику в ModelForms.

0 голосов
/ 28 августа 2018

Рабочий пример!

Я использую электронную почту как уникальное поле

В models.py используйте код ниже

User._meta.get_field('email')._unique = True

В Forms.py используйте код ниже

class ProfileForm(forms.ModelForm):
    email = forms.EmailField(max_length=255, required=True)

    def clean_email(self):
        user_id = self.cleaned_data['user'].id
        email = self.cleaned_data['email']
        obj = User.objects.filter(email=email).exclude(id = user_id)
        if obj:
            raise forms.ValidationError('User with this email is already exists.Try a new email')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...