Nick Bewley
Nick Bewley

Reputation: 9289

Empty Label ChoiceField Django

How do you make ChoiceField's label behave like ModelChoiceField? Is there a way to set an empty_label, or at least show a blank field?

Forms.py:

thing = forms.ModelChoiceField(queryset=Thing.objects.all(), empty_label='Label')
color = forms.ChoiceField(choices=COLORS)
year = forms.ChoiceField(choices=YEAR_CHOICES)

I have tried the solutions suggested here:

Stack Overflow Q - Setting CHOICES = [('','All')] + CHOICES resulted in an internal server error.

Stack Overflow Q2 - After defining ('', '---------'), in my choices, still defaulted to the first item in the list, not the ('', '---------'), choice.

Gist - Tried using EmptyChoiceField defined here, but did not work using Django 1.4.

But none of these have worked for me.. How would you solve this issue? Thanks for your ideas!

Upvotes: 59

Views: 75452

Answers (10)

Gaurav Nagar
Gaurav Nagar

Reputation: 381

It's work for me Django version 4.0.4

Group.objects.values_list()

from django.contrib.auth.models import Group
GROUP_CHOICES = [('','Select the Group')] + list(Group.objects.values_list())

groups = forms.ChoiceField(choices=GROUP_CHOICES, required=False)

Upvotes: 0

Add Empty String with 9 Hyphens to choices as shown below:

class DateForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        class Months(models.TextChoices):
            EMPTY_LABEL = '', '---------' # Here
            JANUARY = 'JAN', 'January'
            FEBRUARY = 'FEB', 'February'
            MARCH = 'MAR', 'March'
                                 
        self.fields['month'].choices = Months.choices

Upvotes: 0

Pat
Pat

Reputation: 11

Had to use 0 instead of u'' because of integer field in model. (Error was invalid literal for int() with base 10: ')

# prepend an empty label if it exists (and field is not required!)
if not required and empty_label is not None:
    choices = tuple([(0, empty_label)] + list(choices))

Upvotes: 1

type49
type49

Reputation: 131

another way to achieve this is to define the select widget separately from the rest of the widgets and change the method of saving the content.

forms.py

class CardAddForm(forms.ModelForm):
    category = forms.ModelChoiceField(empty_label='Choose category',
                                      queryset=Categories.objects.all(),
                                      widget=forms.Select(attrs={'class':'select-css'}))

    class Meta:
        **other model field**

And in views.py you should use obj.create(**form.cleaned_data) instead form.save()

Upvotes: 1

Fiver
Fiver

Reputation: 10165

See the Django 1.11 documentation on ChoiceField. The 'empty value' for the ChoiceField is defined as the empty string '', so your list of tuples should contain a key of '' mapped to whatever value you want to show for the empty value.

### forms.py
from django.forms import Form, ChoiceField

CHOICE_LIST = [
    ('', '----'), # replace the value '----' with whatever you want, it won't matter
    (1, 'Rock'),
    (2, 'Hard Place')
]

class SomeForm (Form):

    some_choice = ChoiceField(choices=CHOICE_LIST, required=False)

Note, you can avoid a form error if you want the form field to be optional by using required=False

Also, if you already have a CHOICE_LIST without an empty value, you can insert one so it shows up first in the form drop-down menu:

CHOICE_LIST.insert(0, ('', '----'))

Upvotes: 71

cwhisperer
cwhisperer

Reputation: 1926

It is not the same form, but I did it the following way inspired by the EmptyChoiceField method:

from django import forms
from ..models import Operator


def parent_operators():
    choices = Operator.objects.get_parent_operators().values_list('pk', 'name')
    choices = tuple([(u'', 'Is main Operator')] + list(choices))
    return choices


class OperatorForm(forms.ModelForm):
    class Meta:
        model = Operator
        # fields = '__all__'
        fields = ('name', 'abbr', 'parent', 'om_customer_id', 'om_customer_name', 'email', 'status')

    def __init__(self, *args, **kwargs):
        super(OperatorForm, self).__init__(*args, **kwargs)
        self.fields['name'].widget.attrs.update({'class': 'form-control m-input form-control-sm'})
        self.fields['abbr'].widget.attrs.update({'class': 'form-control m-input form-control-sm'})
        self.fields['parent'].widget.attrs.update({'class': 'form-control m-input form-control-sm'})
        self.fields['parent'].choices = parent_operators()
        self.fields['parent'].required = False
        self.fields['om_customer_id'].widget.attrs.update({'class': 'form-control m-input form-control-sm'})
        self.fields['om_customer_name'].widget.attrs.update({'class': 'form-control m-input form-control-sm'})
        self.fields['email'].widget.attrs.update({'class': 'form-control m-input form-control-sm', 'type': 'email'})enter code here

Upvotes: 0

Javier Buzzi
Javier Buzzi

Reputation: 6828

A little late to the party..

How about not modifying the choices at all and just handling it with a widget?

from django.db.models import BLANK_CHOICE_DASH

class EmptySelect(Select):
    empty_value = BLANK_CHOICE_DASH[0]
    empty_label = BLANK_CHOICE_DASH[1]

    @property
    def choices(self):
        yield (self.empty_value, self.empty_label,)
        for choice in self._choices:
            yield choice

    @choices.setter
    def choices(self, val):
        self._choices = val

Then just call it:

class SomeForm(forms.Form):
    # thing = forms.ModelChoiceField(queryset=Thing.objects.all(), empty_label='Label')
    color = forms.ChoiceField(choices=COLORS, widget=EmptySelect)
    year = forms.ChoiceField(choices=YEAR_CHOICES, widget=EmptySelect)

Naturally, the EmptySelect would be placed inside some kind of common/widgets.py code and then when ever you need it, just reference it.

Upvotes: 2

VT_Drew
VT_Drew

Reputation: 372

I know you already accepted an answer but I just want to post this in case someone out there runs into the issue I was having, namely the accepted solution does not work with a ValueListQuerySet. The EmptyChoiceField, which you linked to, works perfectly for me (although I am using django 1.7).

class EmptyChoiceField(forms.ChoiceField):
    def __init__(self, choices=(), empty_label=None, required=True, widget=None, label=None, initial=None, help_text=None, *args, **kwargs):

        # prepend an empty label if it exists (and field is not required!)
        if not required and empty_label is not None:
            choices = tuple([(u'', empty_label)] + list(choices))

        super(EmptyChoiceField, self).__init__(choices=choices, required=required, widget=widget, label=label, initial=initial, help_text=help_text, *args, **kwargs) 

class FilterForm(forms.ModelForm):
    #place your other fields here 
    state = EmptyChoiceField(choices=People.objects.all().values_list("state", "state").distinct(), required=False, empty_label="Show All")

Upvotes: 6

Nick Bewley
Nick Bewley

Reputation: 9289

Here's the solution that I used:

from myapp.models import COLORS

COLORS_EMPTY = [('','---------')] + COLORS

class ColorBrowseForm(forms.Form):
    color = forms.ChoiceField(choices=COLORS_EMPTY, required=False, widget=forms.Select(attrs={'onchange': 'this.form.submit();'}))

Upvotes: 26

reczy
reczy

Reputation: 81

You can try this (assuming your choices are tuples):

blank_choice = (('', '---------'),)
...
color = forms.ChoiceField(choices=blank_choice + COLORS)
year = forms.ChoiceField(choices=blank_choice + YEAR_CHOICES)

Also, I can't tell from your code whether this is a form or a ModelForm, but it it's the latter, no need to redefine the form field here (you can include the choices=COLORS and choices=YEAR_CHOICES directly in the model field.

Hope this helps.

Upvotes: 8

Related Questions