'Django forms.Form reflecting property field constraints

I'm implementing a Django Form witch should contain the field 'fieldA' from modelA:

class ModelA(models.Model):
    fieldA =  models.CharField(max_length=8)
    ...

My question is: Is there a way to have Django form, which will automatically handle validation of fieldA (check the max_length)? I know I Could use form.ModelFormclass referring to ModelA, but then the form would reflect all the fields of the ModelA. I would like to use simple forms.Form.

I'm looking for a solution like:

class formX(forms.Form):
    fieldA = forms.CharField(**modelA.fieldA.constraints)
    fieldB = ... some other fields not related to ModelA ...
    .... even more fields



Solution 1:[1]

I found a way how to achieve a validation of form Field reflecting constrains from model Field.

class Foo(models.Model):
    x = models.CharField(max_length=30)
    y = models.IntegerField(null=True)

class FooForm(forms.Form):
    foo_x = forms.CharField(validators=Foo._meta.get_field('x').validators)

Like this, the form will respect the max_length validator of attribute x or any other validator defined.

Solution 2:[2]

Maybe this question is an XY problem, but let me try...

Direct question: get field constraints from existing model

from django import forms
from django.db import models

class Foo(models.Model):
    x = models.CharField(max_length=30)
    y = models.IntegerField(null=True)

class FooForm(forms.Form):
    foo_x = forms.CharField(max_length=Foo._meta.get_field('x').max_length)

You can access the field directly in two ways:

  1. ModelClass.field_name.field (kind of hack, ModelClass.field_name is a django.db.models.query_utils.DeferredAttribute)
  2. ModelClass._meta.get_field('field_name') (better way, described somewhere in docs)

However, this way you have to a) update form if field constraints are added or b) specify all attributes in advance (max_length, min_length, verbose_name, blank, etc.), making declaration of FooForm.foo_x too verbose.

Alternatives

Fields subset

First of all, if you need a subset of Foo fields, use ModelForm and specify them:

class FooForm(forms.ModelForm):
    class Meta:
        fields = ('x',)

Now you have a form with only x field.

Add some fields

If you want to add fields to this form (that are not related to other model), do it:

class FooForm(forms.ModelForm):
    another_field = forms.IntegerField()

    class Meta:
        fields = ('x',)

    def clean_another_field(self):
        data = self.cleaned_data['another_field'] 
        if data != 42:
            raise ValidationError('Not an answer!')  # i18n should be here
        return data

Also you can override clean and save methods for another goals, documentation explains that nicely.

Mix fields from different models

If you need fields of two different models to be present in one form, you don't. You need two separate forms in this case, plus some inter-validation logic outside of this forms maybe (as a view method, for example, or as another class that is not a form). Maybe what you need is inline formset, it doesn't represent two mixed forms, but at least has some inter-model communication.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 pe.kne
Solution 2 SUTerliakov