[Django]-Customize/remove Django select box blank option

103👍

Haven’t tested this, but based on reading Django’s code here and here I believe it should work:

class ThingForm(forms.ModelForm):
  class Meta:
    model = Thing
  
  def __init__(self, *args, **kwargs):
    super(ThingForm, self).__init__(*args, **kwargs)
    self.fields['verb'].empty_label = None

EDIT: This is documented, though you wouldn’t necessarily know to look for ModelChoiceField if you’re working with an auto-generated ModelForm.

EDIT: As jlpp notes in his answer, this isn’t complete – you have to re-assign the choices to the widgets after changing the empty_label attribute. Since that’s a bit hacky, the other option that might be easier to understand is just overriding the entire ModelChoiceField:

class ThingForm(forms.ModelForm):
  verb = ModelChoiceField(Verb.objects.all(), empty_label=None)

  class Meta:
    model = Thing

39👍

from the docs

The blank choice will not be included
if the model field has blank=False and
an explicit default value (the default
value will be initially selected
instead).

so set the default and you’re ok

👤zalew

24👍

With Carl’s answer as a guide and after rooting around the Django source for a couple hours I think this is the complete solution:

  1. To remove the empty option (extending Carl’s example):

    class ThingForm(models.ModelForm):
      class Meta:
        model = Thing
    
      def __init__(self, *args, **kwargs):
        super(ThingForm, self).__init__(*args, **kwargs)
        self.fields['verb'].empty_label = None
        # following line needed to refresh widget copy of choice list
        self.fields['verb'].widget.choices =
          self.fields['verb'].choices
    
  2. To customize the empty option label is essentially the same:

    class ThingForm(models.ModelForm):
      class Meta:
        model = Thing
    
      def __init__(self, *args, **kwargs):
        super(ThingForm, self).__init__(*args, **kwargs)
        self.fields['verb'].empty_label = "Select a Verb"
        # following line needed to refresh widget copy of choice list
        self.fields['verb'].widget.choices =
          self.fields['verb'].choices
    

I think this approach applies to all scenarios where ModelChoiceFields are rendered as HTML but I’m not positive. I found that when these fields are initialized, their choices are passed to the Select widget (see django.forms.fields.ChoiceField._set_choices). Setting the empty_label after initialization does not refresh the Select widget’s list of choices. I’m not familiar enough with Django to know if this should be considered a bug.

👤jlpp

22👍

You can use this on your model:

class MyModel(models.Model):
    name = CharField('fieldname', max_length=10, default=None)

default=None is the answer 😀

NOTE: I tried this on Django 1.7

8👍

As for the django 1.4 all you need is to set the “default” value and “blank=False” on the choices field

class MyModel(models.Model):
    CHOICES = (
        (0, 'A'), 
        (1, 'B'),
    )
    choice_field = models.IntegerField(choices=CHOICES, blank=False, default=0)

6👍

you can do this in admin:

formfield_overrides = {
    models.ForeignKey: {'empty_label': None},
}

5👍

See here for the complete debate on and methods for resolution of this issue.

5👍

self.fields['xxx'].empty_value = None would not work If you field type is TypedChoiceField which do not have empty_label property.

What we should do is to remove first choice:

1 . If you want to build a BaseForm auto detect TypedChoiceField

class BaseForm(forms.ModelForm):

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

        for field_name in self.fields:
            field = self.fields.get(field_name)
            if field and isinstance(field , forms.TypedChoiceField):
                field.choices = field.choices[1:]
            # code to process other Field
            # ....

class AddClientForm(BaseForm):
     pass

2.only a few form, you can use:

class AddClientForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(AddClientForm, self).__init__(*args, **kwargs)
        self.fields['xxx'].choices = self.fields['xxx'].choices[1:]

4👍

For a ForeignKey field, setting the default value to '' on the model will remove the blank option.

verb = models.ForeignKey(Verb, on_delete=models.CASCADE, default='')

For other fields like CharField you could set the default to None, but this does not work for ForeignKey fields in Django 1.11.

3👍

I was messing around with this today and just came up with a coward hack nifty solution:

# Cowardly handle ModelChoiceField empty label
# we all hate that '-----' thing
class ModelChoiceField_init_hack(object):
    @property
    def empty_label(self):
        return self._empty_label

    @empty_label.setter
    def empty_label(self, value):
        self._empty_label = value
        if value and value.startswith('-'):
            self._empty_label = 'Select an option'
ModelChoiceField.__bases__ += (ModelChoiceField_init_hack,)

Now you can tweak the default ModelChoiceField empty label to anything you’d like. 🙂

PS: No need for downvotes, non-harmful monkey patches are always handy.

2👍

For the latest version of django
the first answer should be like this

class ThingForm(models.ModelForm):
class Meta:
 model = Thing

  def __init__(self, *args, **kwargs):
    self.base_fields['cargo'].empty_label = None
    super(ThingForm, self).__init__(*args, **kwargs)`

2👍

Since Django 1.7, you can customize the label for the blank value by adding a value to your choices list in your model field definition. From the documentation on configuring field choices:

Unless blank=False is set on the field along with a default then a label containing "———" will be rendered with the select box. To override this behavior, add a tuple to choices containing None; e.g. (None, ‘Your String For Display’). Alternatively, you can use an empty string instead of None where this makes sense – such as on a CharField.

I checked the documentation for different versions of Django and found that this was added in Django 1.7.

1👍

I find SOLUTION!!

But not for ForeignKey 🙂

Maybe I can help you.
I looked in Django source code and discovered that in django.forms.extras.widgets.SelecteDateWidget() is a property called none_value that equals (0, ‘—–‘) so I did in my code this

class StudentForm(ModelForm):
    class Meta:
        this_year = int(datetime.datetime.today().strftime('%Y')) 
        birth_years = []
        years = []

        for year in range(this_year - 2, this_year + 3 ):
            years.append(year)
        for year in range(this_year - 60, this_year+2):
            birth_years.append(year)

        model = Student
        exclude = ['user', 'fullname']
        date_widget = SelectDateWidget(years=years)

        date_widget.__setattr__('none_value', (0, 'THERE WAS THAT "-----" NO THERES THIS:-)'))
        widgets = {
            'beginning': date_widget,
            'birth': SelectDateWidget(years=birth_years),
        }

0👍

There are lots of great answers here, but I’m still not entirely satisfied with the implementations. I’m also a bit frustrated that select widgets from different sources (foreign keys, choices) yield different behaviours.

I have a design I’m working with where select fields always have a blank option, and if they’re required they will have a star next to them and the form will simply not validate if they’re left empty. That said, I can only properly override the empty_label for fields that are not TypedChoiceFields.

Here’s what the result should look like. The first result is always the name of the field – in my case, the label.

select widget

Here’s what I ended up doing. The following is an overridden __init__ method of my form:

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    for _, field in self.fields.items():
        if hasattr(field, 'empty_label'):
            field.empty_label = field.label
        if isinstance(field, forms.TypedChoiceField):
            field.choices = [('', field.label)] + [choice for choice in field.choices if choice[0]]

0👍

This becomes more complicated when the choices are foreign keys and if you want to filter the choices based on some criteria. In such case if you set the empty_label and then re-assign the choices (you can apply filtration here too) the empty label will be blank:

class ThingForm(models.ModelForm):

    class Meta:
    model = Thing

    def __init__(self, *args, **kwargs):
        super(ThingForm, self).__init__(*args, **kwargs)
        self.fields['verb'].empty_label = None
        self.fields['verb'].queryset=Verb.objects.all()

bbasically, the first line under init can be applied for all of the fields in the form with a loop or inline loop:

def __init__(self,user, *args, **kwargs):
    super(NewTicket, self).__init__(*args, **kwargs)
    for f in self.fields:
       self.fields[f].empty_label = None # or "Please Select" etc
👤Ibo

Leave a comment