python form教學 - 自定義/刪除Django選擇框空白選項




modelform view (13)

我正在使用Django 1.0.2。 我寫了一個由Model支持的ModelForm。 這個模型有一個ForeignKey,其中blank = False。 當Django為這個表單生成HTML時,它會為ForeignKey引用的表中的每一行創建一個選擇框。 它還會在列表頂部創建一個沒有值的選項,並顯示為一系列破折號:

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

我想知道的是:

  1. 從選擇框中刪除此自動生成選項的最簡潔方法是什麼?
  2. 什麼是最簡潔的方式來定制它,使其顯示為:

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

在尋找解決方案時,我遇到了Django票據4653 ,這給我的印像是其他人有相同的問題,並且Django的默認行為可能已被修改。 這張票已經有一年多了,所以我希望有一個更清晰的方式來完成這些事情。

謝謝你的幫助,

傑夫

編輯:我已經配置ForeignKey字段如下:

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

這確實設置了默認值,以便它不再是空白/破折號選項,但不幸的是,它似乎無法解決我的任何一個問題。 也就是說,空白/破折號選項仍然出現在列表中。


Answers

你可以在管理員處執行此操作:

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

以卡爾的回答為指導,並在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不會刷新選擇小部件的選項列表。 我對Django不夠熟悉,不知道這是否應該被視為一個錯誤。


沒有測試過這個,但基於閱讀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 ,雖然你不一定知道尋找ModelChoiceField,如果你正在使用自動生成ModelForm。

編輯 :正如jlpp在他的答案中註意到的,這不完整 - 您必須在更改empty_label屬性後將選項重新分配給小部件。 由於這有點冒險,另一個可能更容易理解的選項只是覆蓋整個ModelChoiceField:

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

  class Meta:
    model = Thing

對於ForeignKey字段,在模型上將default值設置為''將刪除空白選項。

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

對於像CharField這樣的其他字段,可以將defaultNone ,但這對於Django 1.11中的ForeignKey字段不起作用。


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

我找到解決方案!

但不適用於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),
        }

這裡有很多很棒的答案,但我仍然不完全滿意實現。 我也有點沮喪,選擇來自不同來源(外鍵,選擇)的小部件會產生不同的行為。

我有一個設計,我正在處理選擇字段總是有一個空白選項,如果他們需要他們將有一個明星旁邊他們和表單將無法驗證,如果他們留空。 也就是說,我只能正確地覆蓋不是TypedChoiceField的字段的TypedChoiceField

以下是結果應該是什麼樣子。 第一個結果始終是該字段的名稱 - 在我的情況下是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]]

今天我正在討論這個問題,並且提出了一個懦夫黑客精妙的解決方案:

# 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,無害的猴子補丁總是方便。


至於Django 1.4,您只需要在選項字段上設置“default”值和“blank = False”

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

請參閱此處以了解有關問題的完整辯論和解決方法。


由於Django 1.7,您可以通過在您的模型字段定義中的選擇列表中添加值來自定義空白值的標籤。 從配置字段選擇的文檔:

除非在默認情況下在字段上設置了空白= False,則包含“---------”的標籤將與選擇框一起呈現。 要覆蓋此行為,請將元組添加到包含None的選項; 例如(None,'Your String For Display')。 或者,你可以使用空字符串而不是None,這是有道理的 - 比如在CharField上。

我檢查了不同版本Django的文檔,發現這是在Django 1.7添加的


從文檔

如果模型字段為空= False並且顯式默認值(將初始選擇默認值),則不會包含空白選項。

所以設置默認值即可


一個可能的修復:

from django.contrib import auth

def login(request):
    # ....
    auth.login(request, user)
    # ...

現在您的視圖名稱不會覆蓋django的視圖名稱。





python django django-models django-forms