widgets validations validar form custom django validation modelform

validations - modelform widgets django



La validaciĆ³n de unique_together ModelForm de Django (7)

Tengo un modelo de Django que se ve así.

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

Utilizo un formulario para agregar modelos que se ve así:

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

Mi problema es que SolutionForm no valida la restricción unique_together Solution y, por lo tanto, devuelve un IntegrityError al intentar guardar el formulario. Sé que podría usar validate_unique para verificar manualmente esto, pero me preguntaba si hay alguna forma de detectar esto en la validación del formulario y devolver un error de forma automáticamente.

Gracias.


Como dice Felix, se supone que unique_together verifica la restricción unique_together en su validación.

Sin embargo, en su caso, está excluyendo un elemento de esa restricción de su formulario. Me imagino que este es su problema: ¿cómo va a verificar el formulario la restricción, si la mitad de ella ni siquiera está en el formulario?


Con la ayuda de la respuesta de Jarmo, lo siguiente parece funcionar bien para mí (en Django 1.3), pero es posible que haya roto algún caso de esquina (hay muchas entradas en torno a _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

No estoy seguro, pero esto me parece un error de Django ... pero tendría que analizar los problemas informados anteriormente.

Editar: hablé demasiado pronto. Tal vez lo que escribí arriba funcionará en algunas situaciones, pero no en la mía; Terminé usando la respuesta de Jarmo directamente.


Deberás hacer algo como esto:

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


Logré solucionar esto sin modificar la vista agregando un método de limpieza a mi formulario:

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

Lo único que tengo que hacer ahora en la vista es agregar una propiedad problemática al formulario antes de ejecutar is_valid .


Resolví este mismo problema anulando el método validate_unique() de 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)

Ahora siempre me aseguro de que el atributo no proporcionado en el formulario aún esté disponible, por instance=Solution(problem=some_problem) , instance=Solution(problem=some_problem) en el inicializador.


Si desea que el mensaje de error esté asociado con el campo de name (y que aparece junto a él):

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


la solución de @sttwister es correcta, pero se puede simplificar.

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

Como extra, no recuperas el objeto en caso de duplicación, sino que solo verifica si existe en la base de datos, lo que ahorra un poco de rendimiento.