82

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

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

我想知道的是:

  1. 从选择框中删除此自动生成的选项的最简洁方法是什么?
  2. 自定义它以使其显示为最干净的方法是什么:

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

在寻找解决方案时,我遇到了Django ticket 4653,它给我的印象是其他人也有同样的问题,并且 Django 的默认行为可能已被修改。这张票已有一年多的历史了,所以我希望可能有一种更清洁的方法来完成这些事情。

谢谢你的帮助,

杰夫

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

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

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

4

15 回答 15

96

尚未对此进行测试,但基于在这里这里阅读 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

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

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

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

  class Meta:
    model = Thing
于 2009-04-11T12:40:43.337 回答
39

从文档

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

所以设置默认值就可以了

于 2009-04-11T00:58:57.440 回答
24

以卡尔的回答为指导,在围绕 Django 源代码扎根几个小时后,我认为这是完整的解决方案:

  1. 要删除空选项(扩展 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. 自定义空选项标签本质上是一样的:

    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
    

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

于 2009-04-12T05:13:30.360 回答
21

您可以在您的模型上使用它:

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

默认=无是答案:D

注意:我在 Django 1.7 上试过这个

于 2014-07-21T15:22:22.313 回答
8

至于 django 1.4,您只需要在选择字段上设置“默认”值和“空白=假”

class MyModel(models.Model):
    CHOICES = (
        (0, 'A'), 
        (1, 'B'),
    )
    choice_field = models.IntegerField(choices=CHOICES, blank=False, default=0)
于 2012-07-30T12:50:51.640 回答
6

您可以在管理员中执行此操作:

formfield_overrides = {
    models.ForeignKey: {'empty_label': None},
}
于 2013-04-27T10:52:25.097 回答
5

有关此问题的完整辩论和解决方法,请参见此处。

于 2009-09-01T15:29:11.007 回答
5

self.fields['xxx'].empty_value = None如果您的字段类型是TypedChoiceField没有empty_label属性的,则不起作用。

我们应该做的是删除第一选择:

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.只有几种形式,你可以使用:

class AddClientForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(AddClientForm, self).__init__(*args, **kwargs)
        self.fields['xxx'].choices = self.fields['xxx'].choices[1:]
于 2015-05-24T09:03:09.010 回答
4

对于ForeignKey字段,将default值设置''为模型将删除空白选项。

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

CharField对于像您这样的其他字段,可以将 设置defaultNone,但这不适用于ForeignKeyDjango 1.11 中的字段。

于 2017-09-21T14:55:29.857 回答
3

我今天搞砸了这个,只是想出了一个懦夫黑客漂亮的解决方案:

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

现在您可以将默认的ModelChoiceField空标签调整为您想要的任何内容。:-)

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

于 2014-01-12T06:13:41.323 回答
2

对于 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)`
于 2015-03-27T14:33:39.483 回答
2

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

除非在字段上设置了空白=假以及默认值,否则将使用选择框呈现包含“---------”的标签。要覆盖此行为,请向包含 None 的选项添加一个元组;例如(无,'您的显示字符串')。或者,您可以在有意义的情况下使用空字符串而不是 None - 例如在 CharField 上。

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

于 2018-04-26T18:18:02.210 回答
1

我找到解决方案!

但不适用于 ForeignKey :-)

我也许可以帮你。我查看了 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),
        }
于 2014-02-14T16:52:00.390 回答
0

这里有很多很好的答案,但我仍然对实现并不完全满意。我也有点沮丧,从不同来源(外键、选择)选择小部件会产生不同的行为。

我有一个我正在使用的设计,其中选择字段总是有一个空白选项,如果需要它们,它们旁边会有一个星号,并且表单根本不会验证它们是否为空。也就是说,我只能正确覆盖非TypedChoiceFields 字段的 empty_label。

结果应该是这样的。第一个结果始终是字段的名称——在我的例子中是label.

选择小部件

这就是我最终做的事情。以下是__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]]
于 2018-02-14T04:17:00.177 回答
0

This becomes more complicated when the choices are foreign keys and if you want to filter the choices based on some criteria . 在这种情况下,如果您设置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()

基本上,下面的第一行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
于 2018-08-23T23:46:35.997 回答