Django - предотвращение дублирования записей

У меня есть список записей клиентов в моей базе данных. Каждый год мы формируем единый заказ на работу для каждого клиента. Затем для каждой записи рабочего задания пользователь должен иметь возможность создать примечание, относящееся к рабочему заданию. Однако не все рабочие задания нуждаются в примечании, а только некоторые.

Теперь я не могу просто добавить поле note к рабочему заданию, потому что иногда нам нужно создать примечание еще до того, как рабочее задание будет создано. Иногда это примечание относится к рабочему заданию, которое не будет выполнено в течение 2-3 лет. Таким образом, заметки и рабочее задание должны быть независимыми, хотя они «найдут» друг друга, когда оба существуют.

Итак, вот ситуация. Я хочу, чтобы пользователь мог заполнить очень простую форму note, где у него есть два поля: noteYear и note. Таким образом, все, что они делают, это выбирают год, а затем пишут записку. Фишка в том, что у пользователя не должно быть возможности создать две заметки за один и тот же год для одного и того же клиента.

То, что я пытаюсь получить, это проверка примечания, гарантируя, что для этого года еще нет примечания для этого клиента. Я предполагаю, что это будет достигнуто с помощью пользовательского метода is_valid в форме, но я не могу понять, как это сделать.

Это то, что я пробовал до сих пор (обратите внимание, что я знаю, что это неправильно, это не работает, но пока это моя попытка):

Обратите внимание, что systemID — это запись моего клиента.

Моя модель:

class su_note(models.Model):
    YEAR_CHOICES = (
        ('2013', 2013),        
        ('2014', 2014),        
        ('2015', 2015),        
        ('2016', 2016),        
        ('2017', 2017),        
        ('2018', 2018),        
        ('2019', 2019),        
        ('2020', 2020),        
        ('2021', 2021),        
        ('2022', 2022),        
        ('2023', 2023),        
    )
    noteYear = models.CharField(choices = YEAR_CHOICES, max_length = 4, verbose_name = 'Relevant Year')
    systemID = models.ForeignKey(System, verbose_name = 'System ID')
    note = models.TextField(verbose_name = "Note")

    def __unicode__(self):
        return u'%s | %s | %s' % (self.systemID.systemID, self.noteYear, self.noteType)

И моя форма:

class SU_Note_Form(ModelForm):
    class Meta:
        model = su_note
        fields = ('noteYear', 'noteType', 'note')
    def is_valid(self):
        valid = super (SU_Note_Form, self).is_valid()

        #If it is not valid, we're done -- send it back to the user to correct errors
        if not valid:
            return valid

        # now to check that there is only one record of SU for the system
        sysID = self.cleaned_data['systemID']
        sysID = sysID.systemID
        snotes = su_note.objects.filter(noteYear = self.cleaned_data['noteYear'])
        for s in snotes:
            if s.systemID == self.systemID:
                self._errors['Validation_Error'] = 'There is already a startup note for this year'
                return False
        return True

EDIT -- вот мое решение (спасибо janos за то, что направил меня в правильном направлении)

Моя окончательная форма выглядит так:

class SU_Note_Form(ModelForm):
    class Meta:
        model = su_note
        fields = ('systemID', 'noteYear', 'noteType', 'note')
    def clean(self):
        cleaned_data = super(SU_Note_Form, self).clean()
        sysID = cleaned_data['systemID']
        sysID = sysID.systemID
        try:
            s = su_note.objects.get(noteYear = cleaned_data['noteYear'], systemID__systemID = sysID)
            print(s)
            self.errors['noteYear'] = "There is already a note for this year."
        except:
            pass
        return cleaned_data        

Для всех, кто смотрит на этот код, единственной запутанной частью является строка, в которой есть: sysID = sysID.systemID. systemID на самом деле является полем другой модели - хотя systemID также является полем этой модели - вероятно, плохой дизайн, но он работает.


person Garfonzo    schedule 06.04.2013    source источник


Ответы (1)


См. эту страницу в документации Django: https://docs.djangoproject.com/en/dev/ref/forms/validation/

Поскольку ваша логика проверки зависит от двух полей (год и идентификатор системы), вам необходимо реализовать это с помощью специального метода очистки формы, например:

def clean(self):
    cleaned_data = super(SU_Note_Form, self).clean()
    sysID = cleaned_data['systemID']
    sysID = sysID.systemID
    try:
        su_note.objects.get(noteYear=cleaned_data['noteYear'], systemID=systemID)
        raise forms.ValidationError('There is already a startup note for this year')
    except su_note.DoesNotExist:
        pass

    # Always return the full collection of cleaned data.
    return cleaned_data
person janos    schedule 06.04.2013
comment
Я обнаружил, что это на самом деле терпит неудачу, если я хочу отредактировать уже созданную запись заметки. Это всегда будет подтверждать, что заметка за текущий год уже есть, и не разрешает редактирование. Как бы я обойти это? - person Garfonzo; 07.04.2013
comment
При редактировании существующей записи заметки указывается year+systemId, вам не нужно их проверять. Вы можете либо параметризовать эту форму и повторно использовать ее, добавив условие в метод очистки, либо использовать разные формы для двух разных целей (добавлять новые или редактировать существующие). - person janos; 07.04.2013
comment
Для Django 1.7+ предпочтительным методом является использование нового form.add_error - person Ryan; 23.03.2015