NERD WORLD

Django template language이 제공하는 {{ form }} 커스터마이징 본문

Python - Django

Django template language이 제공하는 {{ form }} 커스터마이징

학부생7년차 2016. 5. 25. 22:13

배경


Django를 활용해서 간단한 모바일 웹을 하나 만드는 중이다. 플스방에서 위닝 2016을 친구들과 즐기는게 취미인데, 이때 상호 전적을 기록해두고 나중에 다시 확인할 수 있는 간단한 로직의 서비스다. 간만에 Django로 개발을 해보니 복습도 되고.


Django가 제공해주는 여러 기능들 중 하나가 DTL(Django Template Engine)이다. 실제로 유저에게 보여지는 웹 페이지는 HTML 문서다. 이 HTML 문서에는 정적(static)인 요소와 동적(dynamic)인 요소가 혼재되어있다. 웹 페이지의 구조를 결정하는 태그들, 그리고 그 태그들을 꾸미는 CSS stylesheet는 정적인 요소다. 해당 웹페이지가 요청되는 어느 경우에도 그 내용이 바뀌지 않기 때문이다. 그러나 요청하는 유저나 요청하는 시점에 따라서 보여지는 데이터는 달라지는 경우가 많다. 내 경우를 들자면 전적 열람을 요청한 유저와, 그 유저가 지정한 상대방 유저가 누군지에 따라서 보여지는 전적 정보가 달라지기 때문이다. 또 다른 예는 로그인 페이지와 회원가입 페이지에 동일한 template를 적용해놓고 내용물인 <form> 태그만 달리하는 것이다. 정적인 요소는 직접 HTML/CSS 작업을 하면 되고, 동적인 요소를 편리하게 작업할 수 있게 도와주는 것이 DTL이다.


앞서 언급한 예와 같이 DTL에서 HTML Form에 대한 작업도 지원해준다. 아래와 같이 template에서 {{ form }}이라고 쓰면 된다. 


<body>
    <form action="" method="POST">
        {% csrf_token %}
        {{ form|linebreaks }}
        <button>계정 만들기</button>
    </form>
</body>


일일이 <input> 태그를 작성할 필요 없는 것이다. 결과물은 아래와 같다.


문제 발생


원하던 Form이 template에 만들어졌다. 그러나 몇 가지 아쉬운 부분들이 있었다


1) label을 <input>과 별도로 출력하고 싶지 않았다.

2) <input> 밑에 나오는 안내 문구들(help_text)를 출력하고 싶지 않았다. 상식적인 수준에서 우선 입력을 유도한 뒤 만약 부적절한 입력이라면 추후에 경고 메시지를 보여주는게 낫겠다 싶었다.

3) label과 유사하게 <input> 태그에 입력할 내용을 지칭해주는 문구를 <input> 안에 흐릿하게 넣고 싶었다.


문제를 더 정확히 정의하기 위해 내가 {{ form }}을 사용하기 까지의 과정을 살펴봐야했다. template에서 {{..}}의 syntax를 사용할 수 있었던 이유는 template의 rendering을 호출하는 view에서 dictionary 타입으로 template이 필요로 할 데이터들을 전달해준 덕이다. 즉, view에서 form 객체를 생성했다. 그 form 객체는 Django 라이브러리에서 제공해주는 UserCreationForm을 상속받아 내가 커스터마이징한 form 타입의 객체였다. 직접 form을 기술하거나, 만든 model에 대한 ModelForm으로 form을 만들어낸 것이 아니기 때문에 많은 부분이 감춰져있었다. 그래서 위의 아쉬운 부분들을 해소하기 위한 커스터마이징이 쉽지 않았다.


문제 해결


스택오버플로우에 검색을 해봐도 원하는 내용이 잘 나오지 않아서 고민했었다. Django GitHub 소스코드에서 힌트를 얻을 수 있었다. 링크의 LINE 95-97이다.


def __init__(self, *args, **kwargs):
        super(UserCreationForm, self).__init__(*args, **kwargs)
        self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({'autofocus': ''})


이 생성자가 어떤 역할을 하는지는 모르겠지만, UserCreationForm 클래스 내부에서 self.fields라는 dictionary에 접근해서 widget.attrs.update() 와 같은 식으로 함수를 호출해서 뭔가 widget에 새로운 attribute를 가할 수 있겠다는 느낌을 받을 수 있었다.


3) 문제의 작업이 이와 정확히 일치했다. 각각의 <input> 태그의 widget에 'placeholder'라는 attribute 값에 원하는 문자열을 넣어주면 되는 작업이라는 걸 이전에 확인했기 때문이다. 또한 2) 문제의 안내문구는 help_text라는 attribute 값이라는 것 또한 확인한 상태였다. 그러니 'label''help_text'에 대해서도 동일한 방법으로 해결할 수 있겠다 싶었다. UserCreationForm을 커스터마이징한 Form 클래스 생성자에 아래의 기술을 추가하니 해결되었다.


def __init__(self, *args, **kwargs):
        super(MyUserCreationForm, self).__init__(*args, **kwargs)
        # 각 input 태그의 help_text, label을 제거함.
        for field in self.fields:
            self.fields[field].help_text=None
            self.fields[field].label=''
        # 각 input 태그에 placeholder를 추가함.
        self.fields['username'].widget.attrs['placeholder'] = "이름"
        self.fields['password1'].widget.attrs['placeholder'] = "비밀번호"
        self.fields['password2'].widget.attrs['placeholder'] = "비밀번호 확인"


아래와 같이 원하는 결과를 얻을 수 있었다.

추가 정보

    • Form을 직접 작성한 경우 or ModelForm을 사용한 경우의 해결 방법 (링크)


Comments