일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 탐욕 알고리즘
- docker-compose
- 아이폰
- 충북
- gpu 병렬처리
- 깃허브
- 유튜브
- 그리디 알고리즘
- pymongo
- venv
- List
- 단축어
- MongoDB
- 추천 영상
- G-Suite
- 리스트
- 파이썬
- selenium
- Django
- python
- flask
- 링크
- 장고
- 바로학교
- nocookie
- 구글 드라이브
- 코딩
- DB
- Google Drive
- 알고리즘
- Today
- Total
SSAMKO의 개발 이야기
한 페이지에서 여러 개의 폼(forms) 다루기 | Django 본문
기존 위와 같던 로그인 화면에 아래와 같이 비밀번호 분실시 재설정을 할 수 있도록 바꾸고자 했습니다.
클릭하면 새 창을 띄우지 않고, Modal로 처리하고 싶었습니다.
이후 확인을 누르면 비밀번호 재설정 메일을 보내는 식으로 View를 구성하고자 했는데요.
여기서 가장 큰 어려움이 새 창이 아니라 Modal을 사용하려면 한 페이지에 2개의 폼을 띄워야 한다는 점이었습니다.
단순히 View 클래스만 상속받아서 복잡하게 코드를 짜면 가능은 하겠지만 Django답지 못하고, Class-based View의 장점을 살리지 못했기에 고민하며 구글링을 시작했습니다.
몇 가지 방법이 나왔지만 가장 만족스러웠던 건
https://www.codementor.io/@lakshminp/handling-multiple-forms-on-the-same-page-in-django-fv89t2s3j
위 사이트였습니다. 그래서 위 사이트의 내용을 뽑아와 적용시켜보기로 했습니다.
(사이트만 보고 하실 분들은 httpResponsseForbidden이 빠져있으니 import해주셔야 합니다.)
from django.http import httpResponseForbidden
일단 기본 원리는 이렇습니다.
View.py
class MultipleFormsLoginView(MultiFormsView):
template_name = "login.html"
form_classes = {'login': LoginForm_multi,
'get_admin': GetAdminForm,
}
success_urls = {
'login': reverse_lazy('index'),
'get_admin': reverse_lazy('send_password_mail'),
}
def login_form_valid(self, form):
# Logic Here
return HttpResponseRedirect(self.get_success_url(form_name))
def get_admin_form_valid(self, form):
# Logic Here
return HttpResponseRedirect(self.get_success_url(form_name))
Django의 Mixin클래스를 활용하여 재사용이 가능한 하나의 Class-based View 모듈을 만들어 그것을 이용하는 겁니다.
MultiFormsView의 속성(Attributes)들을 간단히 살펴보면,
하나의 Template을 이용해서 (template_name)
여러 개의 폼(form)을 입력받아 (form_classes)
각각의 success_url을 지정해주는 거죠. (success_urls)
그리고 각각 폼이 valid할 때, 즉 최종적으로 처리할 로직을 <form__name>_form_valid()함수로 작성해주면 되는거죠.
Forms.py
class MultipleForm(forms.Form):
action = forms.CharField(max_length=60, widget=forms.HiddenInput())
class LoginForm(MultipleForm):
user = forms.CharField(max_length=64, label='아이디')
password = forms.CharField(widget=forms.PasswordInput, label='비밀번호')
class GetAdminForm(MultipleForm):
email = forms.EmailField(widget=forms.Emailinput(), label='이메일')
teacher_name = forms.CharField(max_length=16, label='선생님 성함')
Form에서 MultiForms로 사용할 Form들은 MultipleForm을 상속받습니다.
MultipleForm에는 action이라는 필드가 들어있는데요. 바로 이 action을 사용해 여러 폼들을 구별할 수 있게 됩니다.
마지막으로 위 사이트에 나온 모듈인데요. 해당 모듈은
https://gist.github.com/badri/4a1be2423ce9353373e1b3f2cc67b80b
위 링크에서 확인하실 수 있습니다.
구성을 간단히 살펴보면
multiforms.py
from django.views.generic.base import ContextMixin, TemplateResponseMixin
from django.views.generic.edit import ProcessFormView
from django.http import HttpResponseForbidden
class MultiFormMixin(ContextMixin):
form_classes = {}
prefixes = {}
success_urls = {}
initial = {}
prefix = None
success_url = None
def get_form_classes(self):
return self.form_classes
def get_forms(self, form_classes):
return dict([(key, self._create_form(key, class_name))
for key, class_name in form_classes.items()])
def get_form_kwargs(self, form_name):
kwargs = {}
kwargs.update({'initial': self.get_initial(form_name)})
kwargs.update({'prefix': self.get_prefix(form_name)})
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs
def forms_valid(self, forms, form_name):
form_valid_method = '%s_form_valid' % form_name
if hasattr(self, form_valid_method):
return getattr(self, form_valid_method)(forms[form_name])
else:
return HttpResponseRedirect(self.get_success_url(form_name))
def forms_invalid(self, forms):
return self.render_to_response(self.get_context_data(forms=forms))
def get_initial(self, form_name):
initial_method = 'get_%s_initial' % form_name
if hasattr(self, initial_method):
return getattr(self, initial_method)()
else:
return {'action': form_name}
def get_prefix(self, form_name):
return self.prefixes.get(form_name, self.prefix)
def get_success_url(self, form_name=None):
return self.success_urls.get(form_name, self.success_url)
def _create_form(self, form_name, form_class):
form_kwargs = self.get_form_kwargs(form_name)
form = form_class(**form_kwargs)
return form
class ProcessMultipleFormsView(ProcessFormView):
def get(self, request, *args, **kwargs):
form_classes = self.get_form_classes()
forms = self.get_forms(form_classes)
return self.render_to_response(self.get_context_data(forms=forms))
def post(self, request, *args, **kwargs):
form_classes = self.get_form_classes()
form_name = request.POST.get('action')
return self._process_individual_form(form_name, form_classes)
def _process_individual_form(self, form_name, form_classes):
forms = self.get_forms(form_classes)
form = forms.get(form_name)
if not form:
return HttpResponseForbidden()
elif form.is_valid():
return self.forms_valid(forms, form_name)
else:
return self.forms_invalid(forms)
class BaseMultipleFormsView(MultiFormMixin, ProcessMultipleFormsView):
"""
A base view for displaying several forms.
"""
class MultiFormsView(TemplateResponseMixin, BaseMultipleFormsView):
"""
A view for displaying several forms, and rendering a template response.
"""
위에도 언급했지만 링크에 가보면 3째줄에 있는 HttpResponseForbidden이 빠져있습니다. 코드 작성시 넣어주세요.
Django에서 Mixin Class View는 보통 Template과 Context를 처리하는 과정을 분리(?)하기 위해 사용됩니다.
이 모듈에 있는 MultiFormsView 역시 마찬가지 입니다.
Template은 Django의 기본 Mixin View인 TemplateResponseMixin을 상속받아 처리하고,
Context부분을 처리하기 위한 클래스를 만들어주고 있습니다.
MultiFormsView에서 Context처리를 위해 상속받고 있는 BaseMultipleFormsView 역시 이 모듈에서 정의하는 두 클래스를 상속받고 있습니다.
ProcessMultipleFormsView 에서는 get, post, process_individual_form 함수를 정의하고 있는데
get은 모든 폼을 준비시키고
post는 action 필드를 통해 구분된 form을 process_individual_form으로 보내 처리하도록 합니다.
MultiFormMixin은 MultiFormsView의 기본적인 뼈대를 갖추고 있습니다.
Conclusion
모듈을 사용해서 어떤 프로젝트에서도 손쉽게 한 페이지에서 여러 개의 폼을 사용할 수 있게 되었습니다. 프론트엔드와 결합된다면 다양한 프로젝트를 구상해볼 수 있을 것 같습니다.
'Django' 카테고리의 다른 글
[Django] TestCase 이용해서 테스트 코드 짜기 (0) | 2021.01.14 |
---|---|
[GCP] django 개발서버 배포하기 (0) | 2021.01.13 |
[django] systemd, gunicorn, nginx 서버 배포하기 (0) | 2021.01.12 |
pillow - 이미지 리사이즈 하기 | Django (0) | 2020.05.08 |
Django에서 하나의 폼으로 여러 테이블에 데이터 작성하기 | transaction.atomic (0) | 2020.03.29 |