developer tip

Django의 ModelForm unique_together 유효성 검사

optionbox 2020. 12. 25. 09:39
반응형

Django의 ModelForm unique_together 유효성 검사


다음과 같은 Django 모델이 있습니다.

class Solution(models.Model):
    '''
    Represents a solution to a specific problem.
    '''
    name = models.CharField(max_length=50)
    problem = models.ForeignKey(Problem)
    description = models.TextField(blank=True)
    date = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ("name", "problem")

다음과 같은 모델을 추가하기 위해 양식을 사용합니다.

class SolutionForm(forms.ModelForm):
    class Meta:
        model = Solution
        exclude = ['problem']

내 문제는 제약 조건을 SolutionForm확인하지 않으므로 양식을 저장하려고 할 때을 반환한다는 것입니다. 나는 이것을 수동으로 확인 하는 사용할 수 있다는 것을 알고 있지만 양식 유효성 검사에서 이것을 포착하고 양식 오류를 자동으로 반환하는 방법이 있는지 궁금합니다.Solutionunique_togetherIntegrityErrorvalidate_unique

감사.


validate_unique()ModelForm 메서드를 재정 의하여 동일한 문제를 해결했습니다 .


def validate_unique(self):
    exclude = self._get_validation_exclusions()
    exclude.remove('problem') # allow checking against the missing attribute

    try:
        self.instance.validate_unique(exclude=exclude)
    except ValidationError, e:
        self._update_errors(e.message_dict)

이제는 양식에 제공되지 않은 속성이 여전히 사용 가능한지 확인합니다 (예 : instance=Solution(problem=some_problem)이니셜 라이저).


Felix가 말했듯이 ModelForms는 unique_together유효성 검사에서 제약 조건 을 확인 해야합니다.

그러나 귀하의 경우 실제로 해당 제약 조건의 한 요소를 양식에서 제외합니다. 나는 이것이 당신의 문제라고 생각합니다-양식의 절반이 양식에 똑같지 않은 경우 양식이 제약 조건을 어떻게 확인합니까?


내 양식에 깨끗한 메서드를 추가하여보기를 수정하지 않고이 문제를 해결할 수있었습니다.

class SolutionForm(forms.ModelForm):
    class Meta:
        model = Solution
        exclude = ['problem']

    def clean(self):
        cleaned_data = self.cleaned_data

        try:
            Solution.objects.get(name=cleaned_data['name'], problem=self.problem)
        except Solution.DoesNotExist:
            pass
        else:
            raise ValidationError('Solution with this Name already exists for this problem')

        # Always return cleaned_data
        return cleaned_data

보기에서 지금해야 할 유일한 일은 실행하기 전에 폼에 문제 속성을 추가하는 것 is_valid입니다.


@sttwister의 솔루션은 맞지만 단순화 할 수 있습니다.

class SolutionForm(forms.ModelForm):

    class Meta:
        model = Solution
        exclude = ['problem']

    def clean(self):
        cleaned_data = self.cleaned_data
        if Solution.objects.filter(name=cleaned_data['name'],         
                                   problem=self.problem).exists():
            raise ValidationError(
                  'Solution with this Name already exists for this problem')

        # Always return cleaned_data
        return cleaned_data

보너스로 복제의 경우 개체를 검색하지 않고 데이터베이스에 존재하는지 확인하여 약간의 성능을 절약합니다.


Jarmo의 답변의 도움으로 다음은 나에게 잘 작동하는 것 같습니다 (Django 1.3에서).하지만 일부 코너 케이스를 깨 뜨렸을 가능성이 있습니다 (많은 티켓이 있습니다 _get_validation_exclusions).

class SolutionForm(forms.ModelForm):
    class Meta:
        model = Solution
        exclude = ['problem']

    def _get_validation_exclusions(self):
        exclude = super(SolutionForm, self)._get_validation_exclusions()
        exclude.remove('problem')
        return exclude

잘 모르겠지만 이것은 나에게 Django 버그처럼 보이지만 이전에보고 된 문제를 살펴 봐야합니다.


편집 : 너무 빨리 말 했어요. 위에서 쓴 내용이 어떤 상황에서는 작동하지만 내 상황에서는 작동하지 않을 수 있습니다. 나는 Jarmo의 대답을 직접 사용했습니다.


다음과 같이해야합니다.

def your_view(request):
    if request.method == 'GET':
        form = SolutionForm()
    elif request.method == 'POST':
        problem = ... # logic to find the problem instance
        solution = Solution(problem=problem) # or solution.problem = problem
        form = SolutionForm(request.POST, instance=solution)
        # the form will validate because the problem has been provided on solution instance
        if form.is_valid():
            solution = form.save()
            # redirect or return other response
    # show the form

오류 메시지를 name필드 와 연결하고 그 옆에 표시하려는 경우 :

def clean(self):
    cleaned_data = super().clean()
    name_field = 'name'
    name = cleaned_data.get(name_field)

    if name:
        if Solution.objects.filter(name=name, problem=self.problem).exists():
            cleaned_data.pop(name_field)  # is also done by add_error
            self.add_error(name_field, _('There is already a solution with this name.'))

    return cleaned_data

내 솔루션은 Django 2.1을 기반으로합니다.

SolutionForm은 그대로두고 Solution에 save () 메서드가 있습니다.

class Solution(models.Model):
...
   def save(self, *args, **kwargs):
      self.clean()
      return super(Solution, self).save(*args, **kwargs)


  def clean():
      # have your custom model field checks here
      # They can raise Validation Error

      # Now this is the key to enforcing unique constraint
      self.validate_unique()

ValidationError가 처리되지 않으므로 save ()에서 full_clean () 호출이 작동하지 않습니다.


필자의 company경우 필드 를 제외 하고 뷰의 form_valid기능 에 추가해야했습니다 . 나는 결국 다음을 수행했습니다 (다른 답변에서 영감을 얻음). CreateView

    def form_valid(self, form):
        cleaned_data = form.cleaned_data
        user_company = self.request.user.profile.company
        if UnitCategory.objects.filter(code=cleaned_data['code'],
                                    company=user_company).exists():
            form.add_error('code',                           _(
                'A UnitCategory with this Code already exists for this company.'))
            return super(UnitCategoryCreateView, self).form_invalid(form)
        if UnitCategory.objects.filter(color=cleaned_data['color'],
                                    company=user_company).exists():
            form.add_error('color',                           _(
                'A UnitCategory with this Color already exists for this company.'))
            return super(UnitCategoryCreateView, self).form_invalid(form)
        form.instance.company = user_company
        return super(UnitCategoryCreateView, self).form_valid(form)

In my UpdateView I had to exclude the current instance of the object in checking if the query exist using exclude(pk=self.kwargs['pk'])

    def form_valid(self, form):
        cleaned_data = form.cleaned_data
        user_company = self.request.user.profile.company
        if UnitCategory.objects.filter(code=cleaned_data['code'],
                                       company=user_company).exclude(pk=self.kwargs['pk']).exists():
            form.add_error(
                'code', _('A UnitCategory with this Code already exists for this company.'))
            return super(UnitCategoryUpdateView, self).form_invalid(form)
        if UnitCategory.objects.filter(color=cleaned_data['color'],
                                       company=user_company).exclude(pk=self.kwargs['pk']).exists():
            form.add_error('color', _(
                'A UnitCategory with this Color already exists for this company.'))
            return super(UnitCategoryUpdateView, self).form_invalid(form)
        # Return form_valid if no errors raised
        # Add logged-in user's company as form's company field
        form.instance.company = user_company
        return super(UnitCategoryUpdateView, self).form_valid(form)

Not the cleanest solution I was hoping for, but thought it might benefit someone.

ReferenceURL : https://stackoverflow.com/questions/2141030/djangos-modelform-unique-together-validation

반응형