Below the Radar
Below the Radar

Reputation: 7635

Django - Validate a disabled field in modelform_factory

I use modelform_factory to generate modelforms with an extra form. For the forms with instances, the type field is disabled, for the extra form, the field is enabled.

On save() the forms does'nt validate because no data are in the POST for the disabled field. Even a custom clean method is not working (see this answer). I would like to skip the validation on the disabled fields or have a way to keep the instance data for this field.

models.py

class Attribute(models.Model):
    shapefile = models.ForeignKey(Shapefile)
    name = models.CharField(max_length=255)
    type = models.IntegerField()
    width = models.IntegerField()
    precision = models.IntegerField()

    def __unicode__(self):
        return self.name

forms.py

FIELD_TYPE = [('', '--Choose a type--'),
                    (0, 'Integer'),
                    (1, 'Integer list'),
                    (2, 'Double Precision Float'),
                    (3, 'List of doubles'),
                    (4, 'String of ASCII chars'),
                    (5, 'Array of strings'),
                    (8, 'Raw Binary data'),
                    (9, 'Date'),
                    (10, 'Time'),
                    (11, 'Date and Time')]

class AttributeForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(AttributeForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            self.fields['type'].widget.attrs['disabled'] = True
            self.fields['width'].widget.attrs['readonly'] = True
            self.fields['precision'].widget.attrs['readonly'] = True

    type = forms.ChoiceField(choices=FIELD_TYPE)

    class Meta:
        model = Attribute
        exclude = ['shapefile']

views.py

def editFields(request, shapefile_id):
    layer_selected = Shapefile.objects.get(pk=shapefile_id)
    attributes_selected= Attribute.objects.filter(shapefile__pk=shapefile_id)
    attributesFormset = modelformset_factory(Attribute, form=AttributeForm, extra=1, can_delete=True)
    if request.POST:
        formset = attributesFormset(request.POST, queryset=attributes_selected)
        formset.save()
    else:
        formset = attributesFormset(queryset=attributes_selected)

    return render_to_response("ezmapping/editFields.html", {'shapefile': layer_selected, 'formset':formset}, context_instance=RequestContext(request))

Upvotes: 1

Views: 3302

Answers (1)

Peter DeGlopper
Peter DeGlopper

Reputation: 37319

There are a number of approaches out there, but I think this one is fairly elegant (assuming it actually works; it looks right on first inspection but I haven't tested it).

https://stackoverflow.com/a/5994681/2337736

On your form, conditionally set the field as not required and then declare a custom clean method:

def __init__(self):
    # as above, until here
        self.fields['type'].widget.attrs['disabled'] = True
        self.fields['type'].required = False
    # then continue as above

def clean_type(self):
    if self.instance and self.instance.pk:
        return self.instance.type
    else:
        return self.cleaned_data['type']

Setting it as not required means that the field does not immediately short-circuit during validation of that field, and the custom clean method for the field returns the instance's unmodified value so that it is not overridden with None when constructing the modified instance from the form. Custom clean methods are not called for required fields that are given empty values or no value at all.

Upvotes: 6

Related Questions