Customize/remove Django select box blank option

Customize/remove Django select box blank option



I'm using Django 1.0.2. I've written a ModelForm backed by a Model. This model has a ForeignKey where blank=False. When Django generates HTML for this form it creates a select box with one option for each row in the table referenced by the ForeignKey. It also creates an option at the top of the list that has no value and displays as a series of dashes:


<option value="">---------</option>



What I'd like to know is:



What is the cleanest way to customize it so that it shows as:


<option value="">Select Item</option>



In searching for a solution I came across Django ticket 4653 which gave me the impression that others had the same question and that the default behavior of Django may have been modified. This ticket is over a year old so I was hoping there might be a cleaner way to accomplish these things.



Thanks for any help,



Jeff



Edit: I've configured the ForeignKey field as such:


verb = models.ForeignKey(Verb, blank=False, default=get_default_verb)



This does set the default so that it's no longer the empty/dashes option but unfortunately it doesn't seem to resolve either of my questions. That is, the empty/dashes option still appears in the list.





Turns out it's considered a bug in Django; should remove the empty option automatically in this case: code.djangoproject.com/ticket/10792
– Carl Meyer
Apr 11 '09 at 14:27




15 Answers
15



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


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



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(models.ModelForm):
verb = ModelChoiceField(Verb.objects.all(), empty_label=None)

class Meta:
model = Thing





Thanks for the help Carl. Your example almost worked but I had to refresh the widget choice list as the last step. See my answer. And Wow; fast turnaround on the bug report!
– jlpp
Apr 12 '09 at 5:17





Sidebar: do you know how your last approach would fit in with using Meta: fields to choose which model fields to include on the form?
– jlpp
Apr 12 '09 at 21:55





I'm not sure what would happen if you define a form field manually but don't include it in the Meta list of model fields. I'm sure it would appear, but it would it be saved on the model? My guess is yes - try it and see.
– Carl Meyer
Apr 13 '09 at 20:04





I think the 2nd way to do it using the ModelChoiceField definition is more clear. Easier to spot if looking at someone elses code :)
– radtek
May 13 '14 at 18:28





The first solution is working fine for me without having to re-assign the choices.
– ZAD-Man
Aug 11 '14 at 16:37



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





See Edit in question. Thanks for your help.
– jlpp
Apr 11 '09 at 1:40





Doing this for a ForeignKey field can be dangerous. It might work of it is a CharField with choices. The problem is ForeignKey don't have well established defaults.
– amjoconn
Mar 6 '12 at 19:34





Yeah this was exactly what I was looking for... for a non-ForeignKey choice fields
– byoungb
Jun 6 '16 at 16:40



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:



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



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.





Probably not a bug; I guess there's a reason the empty_label argument is documented, but not re-setting of the attribute. Since this is kind of hacky the better option might be to just override the entire form field; see my edited answer.
– Carl Meyer
Apr 12 '09 at 16:34





This is odd, but I just tested this on my application and I did not need to reset the fields to get the empty label to be removed...
– Paolo Bergantino
Apr 12 '09 at 17:00





This answer worked for me using Django 1.4
– codescribblr
Nov 30 '16 at 1:05



You can use this on your model:


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



default=None is the answer :D



NOTE: I tried this on Django 1.7





Excellent. Like stated at this link, a model field with blank=False and any default value set will not render a blank choices option.
– emyller
Sep 16 '14 at 11:05


blank=False


default





A nice simple solution! All the others verge on the crazy in terms of complexity.
– Robert Moskal
Nov 17 '15 at 22:42





Best solution. Also working in Django 1.9
– Vingtoft
Mar 30 '16 at 13:17





Fantastic. So much better than the other ~9 sort of working answers on the ~3 versions of this question.
– foobarbecue
Sep 1 '17 at 0:42



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)



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



you can do this in admin:


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



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


self.fields['xxx'].empty_value = None


TypedChoiceField


empty_label



What we should do is to remove first choice:



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


BaseForm


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:]





It is a real problem, thanks for explaining. But seems like "for" is not mandatory, we can simply do self.fields['xxx'].choices = self.fields['xxx'].choices[1:] if we want to remove empty choice. But if you want to replace title you need to "recreate" first choice as tuple with first empty element and title in second, e.g. self.fields['xxx'].choices = [('', 'Nothing')] + self.fields['xxx'].choices[1:]
– user3479125
Dec 15 '15 at 14:58



self.fields['xxx'].choices = self.fields['xxx'].choices[1:]


self.fields['xxx'].choices = [('', 'Nothing')] + self.fields['xxx'].choices[1:]





@user3479125 I am making a BaseForm to customize all form, so using for loop.
– Mithril
Dec 16 '15 at 2:21



BaseForm



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. :-)


ModelChoiceField



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



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)`



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


ForeignKey


default


''


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.


CharField


default


None


ForeignKey



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),





Doesn't exactly address the question, but led to me to the solution for the problem I was having. In my case, I was just trying to change the default display for a null value that isn't tied to a model. This defaults to "--------" which I wanted changed. If that's the case then just do: self.fields[field_name].widget.choices[0] = ('', SOME_FIRST_CHOICE_DISPLAY_VALUE)
– Troy Grosfield
Jul 10 '14 at 16:43


self.fields[field_name].widget.choices[0] = ('', SOME_FIRST_CHOICE_DISPLAY_VALUE)



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.


TypedChoiceField



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


label



select widget



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


__init__


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]]



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.



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:


empty_label


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:


init


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






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)