自定义/移除Django选择框空白选项

时间:2022-08-24 10:39:14

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:

我使用Django 1.0.2。我已经编写了一个模型支持的模型表单。这个模型有一个ForeignKey where blank=False。当Django为这个表单生成HTML时,它会为ForeignKey引用的表中的每一行创建一个选择框。它还在列表顶部创建一个没有值的选项,并以一系列的破折号显示:

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

What I'd like to know is:

我想知道的是:

  1. What is the cleanest way to remove this auto-generated option from the select box?
  2. 从选择框中删除这个自动生成的选项最干净的方法是什么?
  3. 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.

在寻找解决方案时,我遇到了Django罚单4653,这给我的印象是其他人也有同样的问题,Django的默认行为可能已经被修改。这张票已经超过一年了,所以我希望有一种更干净的方式来完成这些事情。

Thanks for any help,

感谢任何帮助,

Jeff

杰夫

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

编辑:我已经将ForeignKey字段设置为:

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.

这的确设置了默认值,使它不再是空/破折号选项,但不幸的是,它似乎不能解决我的任何一个问题。也就是说,在列表中仍然会出现空/dashes选项。

14 个解决方案

#1


80  

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

我还没有对此进行测试,但是根据这里和这里阅读的Django代码,我认为它应该可以工作:

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.

编辑:这是有文档记录的,但是如果您使用的是自动生成的ModelForm,您不一定知道要查找ModelChoiceField。

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:

编辑:正如jlpp在他的回答中所指出的,这还不完整——您必须在更改empty_label属性后将选项重新分配给小部件。由于这有点不合理,另一个可能更容易理解的选项就是覆盖整个ModelChoiceField:

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

  class Meta:
    model = Thing

#2


32  

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

如果模型字段有blank=False和显式默认值(默认值将被初始选择),则不会包含空白选项。

so set the default and you're ok

设置默认值,你就没事了

#3


22  

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:

以卡尔的回答为指导,在仔细研究了Django的源代码几个小时之后,我认为这是一个完整的解决方案:

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

    要删除空选项(扩展Carl的示例):

    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.

我认为这种方法适用于所有将ModelChoiceFields呈现为HTML的场景,但我不是很肯定。我发现当初始化这些字段时,它们的选择被传递给Select小部件(参见django.forms.fields.ChoiceField._set_choices)。初始化后设置empty_label不会刷新Select小部件的选项列表。我对Django不够熟悉,不知道这是否应该被认为是一个bug。

#4


17  

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

默认=无是答案:D

NOTE: I tried this on Django 1.7

注意:我在Django 1.7上尝试过。

#5


8  

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

对于django 1.4,您所需要的是在选择字段中设置“默认”值和“blank=False”。

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

#6


5  

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

有关这个问题的完整辩论和解决方法,请参阅这里。

#7


5  

you can do this in admin:

您可以在admin中这样做:

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

#8


4  

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”。如果字段类型为TypedChoiceField,而它没有empty_label属性,则empty_value = None将不起作用。

What we should do is to remove first choice:

我们应该做的是取消第一选择:

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

1。如果您想构建一个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:

2。只有少数形式,你可以使用:

class AddClientForm(forms.ModelForm):

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

#9


2  

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.

PS:不需要下投,无伤害的猴子补丁总是很方便。

#10


2  

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

对于django的最新版本,第一个答案应该是这样的

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

#11


2  

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

对于一个ForeignKey字段,将默认值设置为“在模型上”将删除blank选项。

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这样的其他字段,您可以将默认值设置为None,但这不适用于Django 1.11中的ForeignKey字段。

#12


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

也许我可以帮你。我在Django源代码中发现,在Django .forms.extras. widgets.selectedatewidget()是一个名为none_value的属性,它等于(0,'---- ')所以我在代码中这样做了

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

#13


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.

我有一个设计,我正在使用,其中select字段总是有一个空白选项,如果需要,它们旁边会有一个星形,如果它们是空的,表单将不会验证。也就是说,我只能正确地覆盖那些不是TypedChoiceFields的字段的empty_label。

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

这是结果。第一个结果总是字段的名称——在我的例子中是标签。

自定义/移除Django选择框空白选项

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

#14


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:

由于Django 1.7,您可以通过在模型字段定义中向您的选择列表中添加一个值来定制空白值的标签。从配置字段选择的文档中:

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.

除非在字段中设置blank=False以及一个默认值,否则包含“—————”的标签将与select框一起呈现。要覆盖此行为,向包含None的选项添加一个tuple;例:(没有,“你的展示线”)。或者,您可以使用一个空字符串,而不是空字符串,因为这样做是有意义的——比如在CharField上。

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

我检查了不同版本的Django的文档,发现这是在Django 1.7中添加的。

#1


80  

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

我还没有对此进行测试,但是根据这里和这里阅读的Django代码,我认为它应该可以工作:

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.

编辑:这是有文档记录的,但是如果您使用的是自动生成的ModelForm,您不一定知道要查找ModelChoiceField。

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:

编辑:正如jlpp在他的回答中所指出的,这还不完整——您必须在更改empty_label属性后将选项重新分配给小部件。由于这有点不合理,另一个可能更容易理解的选项就是覆盖整个ModelChoiceField:

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

  class Meta:
    model = Thing

#2


32  

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

如果模型字段有blank=False和显式默认值(默认值将被初始选择),则不会包含空白选项。

so set the default and you're ok

设置默认值,你就没事了

#3


22  

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:

以卡尔的回答为指导,在仔细研究了Django的源代码几个小时之后,我认为这是一个完整的解决方案:

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

    要删除空选项(扩展Carl的示例):

    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.

我认为这种方法适用于所有将ModelChoiceFields呈现为HTML的场景,但我不是很肯定。我发现当初始化这些字段时,它们的选择被传递给Select小部件(参见django.forms.fields.ChoiceField._set_choices)。初始化后设置empty_label不会刷新Select小部件的选项列表。我对Django不够熟悉,不知道这是否应该被认为是一个bug。

#4


17  

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

默认=无是答案:D

NOTE: I tried this on Django 1.7

注意:我在Django 1.7上尝试过。

#5


8  

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

对于django 1.4,您所需要的是在选择字段中设置“默认”值和“blank=False”。

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

#6


5  

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

有关这个问题的完整辩论和解决方法,请参阅这里。

#7


5  

you can do this in admin:

您可以在admin中这样做:

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

#8


4  

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”。如果字段类型为TypedChoiceField,而它没有empty_label属性,则empty_value = None将不起作用。

What we should do is to remove first choice:

我们应该做的是取消第一选择:

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

1。如果您想构建一个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:

2。只有少数形式,你可以使用:

class AddClientForm(forms.ModelForm):

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

#9


2  

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.

PS:不需要下投,无伤害的猴子补丁总是很方便。

#10


2  

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

对于django的最新版本,第一个答案应该是这样的

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

#11


2  

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

对于一个ForeignKey字段,将默认值设置为“在模型上”将删除blank选项。

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这样的其他字段,您可以将默认值设置为None,但这不适用于Django 1.11中的ForeignKey字段。

#12


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

也许我可以帮你。我在Django源代码中发现,在Django .forms.extras. widgets.selectedatewidget()是一个名为none_value的属性,它等于(0,'---- ')所以我在代码中这样做了

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

#13


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.

我有一个设计,我正在使用,其中select字段总是有一个空白选项,如果需要,它们旁边会有一个星形,如果它们是空的,表单将不会验证。也就是说,我只能正确地覆盖那些不是TypedChoiceFields的字段的empty_label。

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

这是结果。第一个结果总是字段的名称——在我的例子中是标签。

自定义/移除Django选择框空白选项

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

#14


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:

由于Django 1.7,您可以通过在模型字段定义中向您的选择列表中添加一个值来定制空白值的标签。从配置字段选择的文档中:

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.

除非在字段中设置blank=False以及一个默认值,否则包含“—————”的标签将与select框一起呈现。要覆盖此行为,向包含None的选项添加一个tuple;例:(没有,“你的展示线”)。或者,您可以使用一个空字符串,而不是空字符串,因为这样做是有意义的——比如在CharField上。

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

我检查了不同版本的Django的文档,发现这是在Django 1.7中添加的。