Frage

Ich habe ein Django-Modell, dass wie folgt aussieht.

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")

Ich benutze ein Formular für das Hinzufügen von Modellen, die wie folgt aussieht:

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

Mein Problem ist, dass der SolutionForm nicht Validate Solution die unique_together Einschränkung und somit kehrt eine IntegrityError wenn sie versuchen, das Formular zu speichern. Ich weiß, dass ich validate_unique manuell überprüfen hierfür verwenden könnte, aber ich habe mich gefragt, ob es eine Möglichkeit gibt, diese in Form Validierung zu fangen und einen Formfehler automatisch zurück.

Danke.

War es hilfreich?

Lösung 3

ich es geschafft, dieses Problem zu beheben, ohne die Sicht zu modifizieren durch eine saubere Methode, um meine Form hinzufügen:

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

Das einzige, was ich tun muß jetzt in der Ansicht ist, ein Problem Eigenschaft zu dem Formular hinzufügen, bevor is_valid Ausführung.

Andere Tipps

Ich löste das gleiche Problem durch Überschreiben der validate_unique() Methode des 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)

Jetzt mache ich sicher nur immer, dass das Attribut nicht im Formular noch verfügbar ist, z.B. instance=Solution(problem=some_problem) auf Initialisierungsstab.

Wie Felix sagt, ModelForms soll die unique_together Einschränkung in ihrer Validierung überprüfen.

Doch in Ihrem Fall Sie ohne tatsächlich ein Element dieses Zwangs aus dem Formular. Ich stelle mir das Ihr Problem ist - wie ist die Form geht die Einschränkung zu überprüfen, wenn die Hälfte davon nicht einmal auf dem Formular ist

die Lösung von @sttwister richtig ist aber vereinfacht werden.

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

Als Bonus Sie retreive nicht das Objekt im Fall von doppelten, sondern nur prüfen, ob es in der Datenbank vorhanden ist, ein wenig Performance zu speichern.

Mit Hilfe von Jarmo Antwort scheint die folgende für mich gut zu funktionieren (in Django 1.3), aber es ist möglich, ich habe einige Ecke Fall gebrochen (es gibt eine Menge von Tickets rund um _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

Ich bin nicht sicher, aber dies scheint wie ein Django Bug zu mir ... aber ich würde die zuvor gemeldeten Probleme umsehen müssen.


Edit: Ich sprach zu früh. Vielleicht, was ich geschrieben habe in einigen Situationen oben funktioniert, aber nicht in meinem; Ich landete direkt Jarmo Antwort zu verbrauchen.

Sie müssen so etwas tun:

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

Wenn Sie die Fehlermeldung mögen einen im Zusammenhang mit dem name Feld (und erscheint daneben) werden:

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

Meine Lösung basiert weg Django 2.1

Leave SolutionForm allein, hat eine save () Methode in Lösung

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()

Beim full_clean () in save () nicht funktioniert als Validation wird nicht behandelte

Ich brauchte das company Feld in meinem Fall auszuschließen, und es in der form_valid Funktion Ansicht hinzufügen. Ich landete dabei die folgende (inspiriert von verschiedenen Antworten) auf. In meinem 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 meiner UpdateView hatte ich bei der Prüfung der aktuelle Instanz des Objekts auszuschließen, wenn die Abfrage exist mit 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)

Nicht die sauberste Lösung, die ich hatte gehofft, für, dachte aber, es jemand profitieren könnten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top