웹프로그래밍/Django

[Django] widget (1) widget의 원리와 widget 만들어보기 - 실시간 글자수 표시 widget

ssung.k 2019. 8. 21. 21:14

input 태그는 type 속성에 따라 여러 모습을 보여줍니다. text 일 때는 글을 입력할 수 있도록, date 일 때는 날짜를 지정할 수 있도록, password 일 때는 비밀번호를 입력할 수 있도록 하는 등 다양한 type이 있습니다. 그리고 각 type 에 맞춰서 브라우저에서 그에 맞는 UI 를 제공합니다. 하지만 기본적인 UI 를 제공할 뿐이지 실제로 product 레벨에서는 사용하기 부족한 게 사실입니다. 몇 가지 상황에 대해서 이를 커스텀 하는 방법에 대해서 알아보도록 하겠습니다.

 

django widget

django에서는 widget 을 사용하여 html input 태그를 생성합니다. widget 은 model에 따라 기본으로 지정되는 default 가 존재합니다. 좀 더 구체적으로 model field 에 따라 form field 가 결정되고 form field 에 따라 Widget 이 결정됩니다.

예를 들어 비슷한 model field 인 CharField 와 TextField 는 다음과 같이 위젯에서 차이가 있기 때문에 브라우저에서도 다르게 보여지게 됩니다.

모델 필드 폼 필드 위젯
models.CharField forms.CharField forms.TextInput
models.TextField forms.CharField forms.Textarea

 

기본 설정

여러 widget을 만들기 위해 django project 를 만들겠습니다. 그 후 core 라는 app 을 만들고 model을 만들어주었습니다.

# core/models.py

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

class University(models.Model):
    name = models.CharField(max_length=100, unique=True)

    def __str__(self):
        return self.name

class Student(models.Model):
    university = models.ForeignKey(University, on_delete=models.CASCADE)
    date_birth = models.DateField()
    residencce = models.CharField(max_length=200)
    photo = models.ImageField(blank=True)
    grade = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
    intro = models.TextField()

대학을 의미하는 University 라는 모델과 이와 ForeignKey 로 연결되어 있는 Student 모델이 있습니다. University 은 이름을 나타내는 name field 만을 가지고 있고 Student 모델은 소속 대학, 태어날 날, 거주지, 사진, 성적, 자기소개 등의 Field 가 있습니다.

 

실시간 글자수 표시

첫번째로 만들어볼 widget 은 admin 페이지에서 University 을 생성할 때 그 옆에 글자수를 표시해주는 widget 입니다. 이를 위해서 기존 forms.TextInput 을 상속받은 새로운 widget 을 만들어보도록 하겠습니다.

# core/widgets.py

from django import forms

class CounterTextInput(forms.TextInput):
    template_name = "widgets/counter_text.html"

CounterTextInput widget 은 counter_text.html 을 보여주므로 이를 만들어주도록 합시다.

<!-- templates/widgets/counter_text.html -->

{% include "django/forms/widgets/input.html" %}

<span id="counter_{{widget.attrs.id}}"></span>

<script>
    document.querySelector('#{{widget.attrs.id}}').addEventListener("input", function(){
        document.querySelector("#counter_{{ widget.attrs.id}}").innerHTML = this.value.length + '글자';
    })
</script>

django 내부적으로 존재하는 input.html 을 include 해서 input 태그를 만들어주고, javascript 를 통해 글자를 세서 span 태그에 넣어주었습니다.

다음으로는 커스텀한 widget을 form 에 적용시켜주도록 하겠습니다.

# core/forms.py

from django import forms
from .models import University, Student
from .widgets import CounterTextInput

class UniversityForm(forms.ModelForm):
    class Meta:
        model = University
        fields = ['name']
        widgets = {
            'name': CounterTextInput,
        }
    

이제 admin 에 등록시켜주면 University 을 생성할 때 글자수가 실시간으로 뜨는 걸 확인할 수 있습니다.

# core/admin.py

from django.contrib import admin
from .models import University, Student
from .forms import UniversityForm

@admin.register(University)
class CountryAmin(admin.ModelAdmin):
    form = UniversityForm


@admin.register(Student)
class PostAmin(admin.ModelAdmin):
    pass