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

时间:2009-04-11 00:43:58

标签: python django django-models django-forms

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

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

我想知道的是:

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

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

    感谢您的帮助,

    杰夫

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

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

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

15 个答案:

答案 0 :(得分:84)

没有对此进行测试,但基于阅读Django的代码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

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

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

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

  class Meta:
    model = Thing

答案 1 :(得分:34)

来自文档

  

不包括空白选项   如果模型字段为空= False和   显式默认值(默认值   最初将选择值   代替)。

所以设置默认值,你没问题

答案 2 :(得分:22)

以Carl的回答为指导,在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
    
  3. 我认为这种方法适用于将ModelChoiceFields呈现为HTML的所有场景,但我并不积极。我发现当初始化这些字段时,它们的选择将传递给Select小部件(请参阅django.forms.fields.ChoiceField._set_choices)。初始化后设置empty_label不会刷新Select小部件的选项列表。我对Django不太熟悉,知道这是否应该被视为一个错误。

答案 3 :(得分:19)

您可以在模型上使用此功能:

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

默认=无是答案:D

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

答案 4 :(得分:8)

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

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

答案 5 :(得分:5)

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

答案 6 :(得分:5)

你可以在admin中执行此操作:

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

答案 7 :(得分: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:]

答案 8 :(得分:3)

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

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

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

答案 9 :(得分:2)

今天我正忙着解决这个问题,并提出了一个懦夫黑客漂亮的解决方案:

# 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,无害的猴子补丁总是很方便。

答案 10 :(得分: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)`

答案 11 :(得分: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),
        }

答案 12 :(得分:0)

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

我有一个设计我正在处理选择字段总是有空白选项,如果他们需要,他们旁边会有一个星号,表单将会根本不验证他们是否留空。也就是说,我只能正确覆盖非TypedChoiceField s。

字段的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]]

答案 13 :(得分:0)

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

  

除非在字段上设置blank = False以及默认值,否则标签包含&#34; ---------&#34;将使用选择框呈现。要覆盖此行为,请将元组添加到包含None的选项中;例如(无,&#39;您的显示字符串&#39;)。或者,您可以使用空字符串而不是空字符串,例如在CharField上。

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

答案 14 :(得分:0)

选择是外键时,如果您要根据某些条件过滤选择,则情况将变得更加复杂。在这种情况下,如果您设置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