首页 文章

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

提问于
浏览
71

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

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

我想知道的是:

  • 从选择框中删除此自动生成选项的最简洁方法是什么?

  • 最简洁的自定义方式是什么,它显示为:

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

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

谢谢你的帮助,

杰夫

编辑:我已经配置了ForeignKey字段:

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

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

15 回答

  • 0

    对于最新版本的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)`
    
  • 8

    避风港't tested this, but based on reading Django' s代码herehere我相信它应该有效:

    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 :这是documented,虽然你不会't necessarily know to look for ModelChoiceField if you'重新使用自动生成的ModelForm .

    EDIT :正如jlpp在他的回答中指出的那样,这不是一个有点hacky,另一个可能更容易理解的选择就是覆盖整个ModelChoiceField:

    class ThingForm(models.ModelForm):
      verb = ModelChoiceField(Verb.objects.all(), empty_label=None)
    
      class Meta:
        model = Thing
    
  • 5

    来自文档

    如果模型字段为空白= False且显式默认值(最初将选择默认值),则不包括空白选项 .

    所以设置默认值就可以了

  • 5

    以Carl的回答为指导,在Django源码周围生根几个小时后,我认为这是完整的解决方案:

    • 要删除空选项(扩展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
    
    • 自定义空选项标签基本相同:
    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不太熟悉,知道这是否应该被视为一个bug .

  • 2

    您可以在模型上使用它:

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

    default=None 就是答案:D

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

  • 1

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

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

    有关此问题的完整辩论和解决方法,请参阅here .

  • 2

    你可以在管理员中这样做:

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

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

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

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

    对于像 CharField 这样的其他字段,您可以将 default 设置为 None ,但这不适用于Django 1.11中的 ForeignKey 字段 .

  • 0

    今天我正在搞乱这个问题,刚刚提出了一个懦夫黑客的漂亮解决方案:

    # 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:不需要downvotes,无害的猴子补丁总是很方便 .

  • 0

    我找到解决方案!

    但不适用于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),
            }
    
  • 22

    这里有很多很棒的答案,但我对这些实现还不是很满意 . 我也有点沮丧的是,来自不同来源(外键,选择)的选择小部件会产生不同的行为 .

    我有一个设计我正在使用选择字段 always 有一个空白选项,如果他们're required they will have a star next to them and the form will simply not validate if they'留空 . 也就是说,我只能正确地覆盖非 TypedChoiceField 字段的empty_label .

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

    select widget

    这就是我最终做的事情 . 以下是我的表单的重写 __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]]
    
  • 84

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

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

    我检查了不同版本的Django的文档,发现这是added in Django 1.7 .

  • 34

    choices are foreign keysfilter 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()
    

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

相关问题