Django Modelform unique_together Validierung
-
22-09-2019 - |
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.
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.