78

I have a form where a couple of fields are coming out as required when I don't want them too. Here is the form from models.py

class CircuitForm(ModelForm):
    class Meta:
        model = Circuit
        exclude = ('lastPaged',)
    def __init__(self, *args, **kwargs):
        super(CircuitForm, self).__init__(*args, **kwargs)
        self.fields['begin'].widget = widgets.AdminSplitDateTime()
        self.fields['end'].widget = widgets.AdminSplitDateTime()

In the actual Circuit model, the fields are defined like this:

begin = models.DateTimeField('Start Time', null=True, blank=True)
end = models.DateTimeField('Stop Time', null=True, blank=True)

My views.py for this is here:

def addCircuitForm(request):
    if request.method == 'POST':
        form = CircuitForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/sla/all')
    form = CircuitForm()    
    return render_to_response('sla/add.html', {'form': form})

What can I do so that the two fields aren't required?

Serjik
  • 10,543
  • 8
  • 61
  • 70
Ryan
  • 1,085
  • 1
  • 10
  • 15
  • 2
    There is no way that these fields become required by changing the widgets. If models has (null=True, blank=True), the modelForm renders it as required=False. – simplyharsh Jul 16 '09 at 07:42
  • Looks like you are right. The fields themselves aren't required but the Date and Time fields in the widget are required. – Ryan Jul 16 '09 at 21:34

6 Answers6

117

If you don't want to modify blank setting for your fields inside models (doing so will break normal validation in admin site), you can do the following in your Form class:

def __init__(self, *args, **kwargs):
    super(CircuitForm, self).__init__(*args, **kwargs)

    for key in self.fields:
        self.fields[key].required = False 

The redefined constructor won't harm any functionality.

DataGreed
  • 13,245
  • 8
  • 45
  • 64
19

If the model field has blank=True, then required is set to False on the form field. Otherwise, required=True

Says so here: http://docs.djangoproject.com/en/dev/topics/forms/modelforms/

Looks like you are doing everything right. You could check the value of self.fields['end'].required.

stefanw
  • 10,456
  • 3
  • 36
  • 34
  • Note that some fields (e.g. `BooleanField`) have blank=True hard-coded in their constructor (https://docs.djangoproject.com/en/dev/_modules/django/db/models/fields/#BooleanField). So, setting blank=False will silently fail. You have to use `self.fields[…].required = True` in the form’s constructor to force required behaviour for those fields. See also https://code.djangoproject.com/ticket/22282. – Edward D'Souza Feb 17 '18 at 18:14
10

Expanding on DataGreed's answer, I created a Mixin that allows you to specify a fields_required variable on the Meta class like this:

class MyForm(RequiredFieldsMixin, ModelForm):

    class Meta:
        model = MyModel
        fields = ['field1', 'field2']
        fields_required = ['field1']

Here it is:

class RequiredFieldsMixin():

    def __init__(self, *args, **kwargs):

        super().__init__(*args, **kwargs)

        fields_required = getattr(self.Meta, 'fields_required', None)

        if fields_required:
            for key in self.fields:
                if key not in fields_required:
                    self.fields[key].required = False
gitaarik
  • 42,736
  • 12
  • 98
  • 105
  • What will happen if the user of this mixin will override `__init__` as well? will the mixin still work? how the multiple inheritance will be resolved? – user3599803 Jul 06 '16 at 17:33
  • @user3599803 when you override `__init__` you should always call `super()`, like it's done in my snippet. – gitaarik Jul 07 '16 at 08:52
  • So if I understand- I create a form and subclass MyForm. I call super from init(), which will call the mixin init, but when djangl form init get called? – user3599803 Jul 07 '16 at 11:40
  • Yes, there's other resources discussing this, you can also create a new question if you want, please keep the comments here on topic of this particular question. – gitaarik Jul 07 '16 at 13:11
3

It's not an answer, but for anyone else who finds this via Google, one more bit of data: this is happening to me on a Model Form with a DateField. It has required set to False, the model has "null=True, blank=True" and the field in the form shows required=False if I look at it during the clean() method, but it's still saying I need a valid date format. I'm not using any special widget and I get the "Enter a valid date" message even when I explicitly set input_formats=['%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', ''] on the form field.

EDIT: Don't know if it'll help anyone else, but I solved the problem I was having. Our form has some default text in the field (in this case, the word "to" to indicate the field is the end date; the field is called "end_time"). I was specifically looking for the word "to" in the form's clean() method (I'd also tried the clean_end_time() method, but it never got called) and setting the value of the clean_data variable to None as suggested in this Django ticket. However, none of that mattered as (I guess) the model's validation had already puked on the invalid date format of "to" without giving me a chance to intercept it.

Tom
  • 22,301
  • 5
  • 63
  • 96
0

This is a bug when using the widgets:

workaround: Using Django time/date widgets in custom form

or ticket 12303

Community
  • 1
  • 1
Sam A.
  • 131
  • 2
  • 4
0

From the model field documentation,

If you have a model as shown below,

class Article(models.Model):
    headline = models.CharField(
        max_length=200,
        null=True,
        blank=True,
        help_text='Use puns liberally',
    )
    content = models.TextField()

You can change the headline's form validation to required=True instead of blank=False as that of the model as defining the field as shown below.

class ArticleForm(ModelForm):
    headline = MyFormField(
        max_length=200,
        required=False,
        help_text='Use puns liberally',
    )

    class Meta:
        model = Article
        fields = ['headline', 'content']

So answering the question,

class CircuitForm(ModelForm):
    begin = forms.DateTimeField(required=False)
    end = forms.DateTimeField(required=False)
        class Meta:
            model = Circuit
            exclude = ('lastPaged',)

this makes begin and end to required=False

All Іѕ Vаиітy
  • 24,861
  • 16
  • 87
  • 111