Django – ModelForm field 사용자 정의 data- 속성 추가하기

Form field 가 html 로 렌더링 될 때 원하는 속성값을 부가 하고 싶은데, 장고 코드에서 자동으로 하고 싶다.

이래 저래 찾다가 코드가 발견

class CompanySelect(forms.Select):
    def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
        option = super().create_option(name, value, label, selected, index, subindex, attrs)
        if value:
            option["attrs"]["data-company"] = value.instance.company_id
        return option


class ProductProfileForm(forms.ModelForm):
    class Meta:
        model = ProductProfile
        fields = ["company", "name", "product", "fwver1", "fwver2", "fwver3", "fwver4", "testset", "boxlabel", "boxpq"]
        # exclude = ["order", "status", "date_done", "elapsed_time", "mode", "desc", "is_deleted", "updated_at", "created_at"]
        # fields = ["order", "product", "workline", "fwver", "testset", "quantity", "date_due", "box", "boxpq", "status"]
        labels = {
            "company": _("*회사명"),
            "product": _("상품명"),
...}

        widgets = {
            "company": forms.Select(attrs={"class": "form-select"}),
            # "product": forms.Select(attrs={"class": "form-select flex-fill", "required": True, "style": "width: 100px;"}),
            "product": CompanySelect(attrs={"class": "form-select flex-fill companyselect", "required": True}),
...

원래 그냥 forms.Select 만 쓰면 특별히 해당 필드 값을 가지고 속성을 별도로 주는 것이 어렵다. 클래스 처럼 한꺼번에 줄 수 는 있어도 해당 값에 맞는 값을 DB 에서 가져와서 넣어주는게 힘들다. 예를 들면 해당 레코드별로 있는 ID 값을 data-code 속성에 넣어주는 그런것은 하기 어렵다.

이때는 forms.Select 를 오버라이딩 해서 사용하면 된다.

CompanySelect 클래스를 참고하면 된다. 원하는 DB instance 의 값을 바로 쓸 수 있다.

아래 처럼 data-company 값이 떡 하니 적혀져서 렌더링 된다. 이값을 이용해서 코드를 작성하면 편리하게 활용할 수 있다.

<select name="form-0-product" class="form-select companyselect" required="" id="id_form-0-product">
  <option value="" selected="" style="display: none;">---------</option>

  <option value="200" data-company="1" style="display: none;">test1 (twareLAB)</option>

  <option value="208" data-company="1" style="display: none;">test11 (twareLAB)</option>

  <option value="221" data-company="1" style="display: none;">TJ100 (twareLAB)</option>

  <option value="244" data-company="1" style="display: none;">TW100XR (twareLAB)</option>

  <option value="222" data-company="9" style="">Product1 (WIZnet)</option>

  <option value="3" data-company="9" style="">Test (WIZnet)</option>

  <option value="210" data-company="10" style="display: none;">Product1-1 (TEST Company)</option>

  <option value="219" data-company="10" style="display: none;">Product1-111 (TEST Company)</option>

  <option value="220" data-company="10" style="display: none;">Product1-123 (TEST Company)</option>
</select>

빙에게서 받은 예제도 여기에 첨부해 둡니다.

Django의 forms.Select 위젯을 오버라이딩하는 예제를 제공해 드리겠습니다. forms.Select 위젯은 ChoiceField의 기본 위젯으로 HTML의 <select> 요소를 생성합니다.

예를 들어, 다음과 같은 ModelForm이 있다고 가정해봅시다.

from django import forms
from myapp.models import MyModel

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ['myfield']

이 경우 myfieldChoiceField인 경우, 다음과 같이 forms.Select 위젯을 오버라이딩하여 사용자 정의 위젯을 만들 수 있습니다.

from django import forms
from myapp.models import MyModel

class MySelect(forms.Select):
    def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
        option = super(MySelect, self).create_option(name, value, label, selected, index, subindex=subindex, attrs=attrs)
        option['attrs']['data-myattribute'] = 'myvalue'
        return option

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ['myfield']
        widgets = {
            'myfield': MySelect
        }

위와 같이 수정하면 myfield 필드의 <option> 요소에 data-myattribute="myvalue" 속성이 추가됩니다.

create_option 메서드는 Django의 forms.Select 위젯에서 <option> 요소를 생성하는 데 사용됩니다. 이 메서드는 다음과 같은 인수를 받습니다.

  • name: 필드의 이름입니다.
  • value: 옵션의 값입니다.
  • label: 옵션의 레이블입니다.
  • selected: 옵션이 선택되었는지 여부를 나타내는 불리언 값입니다.
  • index: 옵션의 인덱스입니다.
  • subindex: 옵션의 하위 인덱스입니다. 기본값은 None입니다.
  • attrs: 옵션의 HTML 속성을 나타내는 딕셔너리입니다. 기본값은 None입니다.

create_option 메서드는 위와 같은 인수를 받아서 딕셔너리 형태의 옵션 객체를 반환합니다. 이 딕셔너리에는 다음과 같은 키가 있습니다.

  • name: 필드의 이름입니다.
  • value: 옵션의 값입니다.
  • label: 옵션의 레이블입니다.
  • selected: 옵션이 선택되었는지 여부를 나타내는 불리언 값입니다.
  • index: 옵션의 인덱스입니다.
  • attrs: 옵션의 HTML 속성을 나타내는 딕셔너리입니다.

create_option 메서드를 오버라이딩하여 위와 같은 인수를 받고 딕셔너리 형태의 옵션 객체를 반환하는 사용자 정의 메서드를 만들 수 있습니다. 이렇게 하면 <option> 요소의 속성을 동적으로 변경할 수 있습니다.

create_option 메서드를 오버라이딩하는 예제를 더 제공해 드리겠습니다. 이전 예제에서는 create_option 메서드를 오버라이딩하여 <option> 요소에 data-myattribute="myvalue" 속성을 추가하는 방법을 보여드렸습니다.

이번에는 create_option 메서드를 오버라이딩하여 <option> 요소의 레이블을 동적으로 변경하는 방법을 보여드리겠습니다.

from django import forms
from myapp.models import MyModel

class MySelect(forms.Select):
    def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
        option = super(MySelect, self).create_option(name, value, label, selected, index, subindex=subindex, attrs=attrs)
        option['label'] = label.upper()
        return option

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ['myfield']
        widgets = {
            'myfield': MySelect
        }

위와 같이 수정하면 myfield 필드의 <option> 요소의 레이블이 모두 대문자로 변경됩니다.

위젯을 오버라이딩하는 이유

Django의 위젯은 폼 필드의 HTML 표현을 담당합니다. 기본적으로 Django는 각 필드 유형에 대해 기본 위젯을 제공하지만, 때로는 이러한 기본 위젯이 원하는 요구 사항을 충족시키지 못할 수 있습니다. 이 경우 위젯을 오버라이딩하여 사용자 정의 위젯을 만들 수 있습니다.

위젯을 오버라이딩하는 이유는 다음과 같습니다.

  • HTML 요소의 속성을 동적으로 변경하려는 경우
  • HTML 요소의 레이아웃이나 스타일을 변경하려는 경우
  • JavaScript 이벤트 핸들러를 추가하려는 경우
  • 기타 사용자 정의 요구 사항을 충족시키기 위해

위와 같은 이유로 위젯을 오버라이딩하여 사용자 정의 위젯을 만들 수 있습니다.

![Close-up of Modern Gadget Screen with Widget](assets/images/2023/04/13003482.jpg?resize=1024%2C767&ssl=1)
Share: Twitter Facebook
Bongjun Hur's Picture

About Bongjun Hur

BJ is a dev.

Seoul, Korea https://devbj.com