IT박스

Django 선택 상자 빈 옵션 사용자 지정 / 제거

itboxs 2020. 10. 23. 07:43
반응형

Django 선택 상자 빈 옵션 사용자 지정 / 제거


Django 1.0.2를 사용하고 있습니다. Model이 뒷받침하는 ModelForm을 작성했습니다. 이 모델에는 blank = False 인 ForeignKey가 있습니다. Django가이 양식에 대한 HTML을 생성 할 때 ForeignKey가 참조하는 테이블의 각 행에 대해 하나의 옵션이있는 선택 상자를 만듭니다. 또한 목록 맨 위에 값이없고 일련의 대시로 표시되는 옵션을 생성합니다.

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

내가 알고 싶은 것은 :

  1. 선택 상자에서이 자동 생성 옵션을 제거하는 가장 깨끗한 방법은 무엇입니까?
  2. 다음과 같이 표시되도록 사용자 정의하는 가장 깨끗한 방법은 무엇입니까?

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

해결책을 찾을 때 나는 다른 사람들이 같은 질문을하고 Django의 기본 동작이 수정되었을 수 있다는 인상을 준 Django 티켓 4653 을 발견했습니다. 이 티켓은 1 년이 넘었 기 때문에 이러한 일을 수행 할 수있는 더 깨끗한 방법이 있기를 바랐습니다.

도움을 주셔서 감사합니다.

제프

편집 : 다음과 같이 ForeignKey 필드를 구성했습니다.

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

이것은 더 이상 빈 / 대시 옵션이 아니도록 기본값을 설정하지만 불행히도 내 질문 중 하나를 해결하지 못하는 것 같습니다. 즉, 비어있는 / 대시 옵션이 목록에 계속 나타납니다.


이것을 테스트하지는 않았지만 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 속성을 변경 한 후 위젯에 선택 사항을 다시 할당해야합니다. 약간 해키하기 때문에 이해하기 쉬운 다른 옵션은 전체 ModelChoiceField를 재정의하는 것입니다.

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

  class Meta:
    model = Thing

문서에서

모델 필드에 blank = False이고 명시 적 기본값이있는 경우 공백 선택이 포함되지 않습니다 (기본값이 대신 초기에 선택됨).

그래서 기본값을 설정하면 괜찮습니다.


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
    

이 접근 방식은 ModelChoiceFields가 HTML로 렌더링되는 모든 시나리오에 적용되지만 긍정적이지 않습니다. 이 필드가 초기화되면 선택 사항이 Select 위젯에 전달된다는 것을 알았습니다 (django.forms.fields.ChoiceField._set_choices 참조). 초기화 후 empty_label을 설정하면 선택 위젯의 선택 목록이 새로 고쳐지지 않습니다. 나는 이것이 버그로 간주되어야하는지 알기 위해 Django에 대해 충분히 익숙하지 않습니다.


모델에서 사용할 수 있습니다.

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

default = 없음 은 답입니다 : D

참고 : Django 1.7에서 시도했습니다.


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)

이 문제의 해결 방법에 대한 전체 토론 및 방법 여기참조 하십시오 .


관리자에서이 작업을 수행 할 수 있습니다.

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

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

A의 ForeignKey필드에 설정 default에 값을 ''모델에하면 빈 옵션을 제거합니다.

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

다른 필드의 CharField경우 default를로 설정할 수 None있지만 ForeignKeyDjango 1.11의 필드에서는 작동하지 않습니다 .


나는 오늘 이것을 엉망으로 만들고 겁쟁이 해킹 멋진 솔루션을 생각해 냈습니다 .

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

PS: No need for downvotes, non-harmful monkey patches are always handy.


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

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

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

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

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.

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

select widget

Here's what I ended up doing. The following is an overridden __init__ method of my form:

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

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:

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.

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


This becomes more complicated when the choices are foreign keys and if you want to filter the choices based on some criteria. In such case if you set the empty_label and then re-assign the choices (you can apply filtration here too) the empty label will be blank:

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, the first line under init can be applied for all of the fields in the form with a loop or inline loop:

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

참고URL : https://stackoverflow.com/questions/739260/customize-remove-django-select-box-blank-option

반응형