Home > Net >  Django forms.Form reflecting property field constraints
Django forms.Form reflecting property field constraints

Time:04-29

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

CodePudding user response:

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.

  • Related