-
django signup FBV using forms.pyProject using python/Cloning Airbnb 2021. 3. 1. 13:38
Forms.py
users/forms.py
forms.py에 있는 SignUpForm이다. forms.ModelForm을 상속받는다. 여기서 필수적으로 있어야 하는 code는 class Meta에 있는 model과 fields다. Meta class는 부모 class의 meta 정보를 담고 있다. meta 정보란 하나의 data의 추가적인 정보를 뜻한다. 사진을 예로 든다면, 사진은 색깔과 화소 등의 정보를 가지고 있다. 그리고 사진의 meta 정보란 찍은 장소, 시간 및 keyword나 작가 정보를 가진다. 사진의 필수 정보는 색깔과 화소이고 없어도 되지만 있으면 더 좋은 것을 장소, 시간, keyword와 작가 정보다.
Django에서 Meta class는 admin.py를 작성할 때도 많이 볼 수 있다. forms.ModelForm에서 Meta class는 없어도 되는 것이 아니라 반드시 있어야 한다. 그 중 model과 fields는 반드시 정의해야 form을 작성할 수 있다. model을 정해주고 model에 정의된 field 값을 할당하면 할당된 field를 client 측에 전송할 수 있다.
widgets은 만들어진 field에 속성을 추가하는 등의 추가적인 작업을 할 수 있다.
clean__data는 유효성 검사를 위해서 사용한다. clean__<field_name>으로 function을 만들고 logic은 유효성 검사를 한 후 valid하면 값을 return하는 방식이고 타당하지 않다면 error를 발생시킨다.
from django import forms from . import models class SignUpForm(forms.ModelForm): class Meta: model = models.User fields = { "first_name", "last_name", "email", "avatar", "bio", "gender", "language", "currency", "birthdate", } widgets = { "first_name": forms.TextInput( attrs={"placeholder": "First name", "required": True, "class": "w-full"} ), "last_name": forms.TextInput( attrs={"placeholder": "Last name", "required": True, "class": "w-full"} ), "email": forms.EmailInput( attrs={"placeholder": "Email", "required": True, "class": "w-full"} ), "bio": forms.Textarea(attrs={"placeholder": "Bio", "class": "resize-none"}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["avatar"].required = False self.fields["birthdate"].required = False password = forms.CharField( widget=forms.PasswordInput(attrs={"placeholder": "Password"}) ) password1 = forms.CharField( widget=forms.PasswordInput(attrs={"placeholder": "Confirm Password"}), label="Confirm Password", ) def clean__email(self): email = self.cleaned_data.get("email") try: models.User.objects.get(email=email) raise forms.ValidationError("User already exists with that email") except models.User.DoesNotExist: return email def clean__password(self): password = self.cleaned_data.get("password") password1 = self.cleaned_data.get("password1") if password != password1: raise forms.ValidationError("Password confirmation does not match") else: return password def save(self): user = super().save(commit=False) email = self.cleaned_data.get("email") password = self.cleaned_data.get("password") user.username = email user.set_password(password) user.save()
FBV(Function Based View)
users/views.py
밑에 코드는 forms.py를 사용하기 이전 code다.
from django.shortcuts import render, redirect, reverse from django.core.exceptions import ValidationError from . import models def signUp(request): if request.method == "GET": genders = models.User.GENDER_CHOICES languages = models.User.LANGUAGE_CHOICES currencies = models.User.CURRENCY_CHOICES choices = { "genders": genders, "languages": languages, "currencies": currencies, } return render(request, "pages/users/signup.html", context={**choices}) elif request.method == "POST": email = request.POST.get("email") if email is None: return redirect(reverse("users:signup")) try: # If a registered user with the email exists, raise error models.User.objects.get(email=email) raise ValidationError("User already exists") except models.User.DoesNotExist: password = request.POST.get("password") if password is None: return redirect(reverse("users:signup")) password1 = request.POST.get("password1") if password1 is None: return redirect(reverse("users:signup")) # If password doesn't match, raise error if password != password1: raise ValidationError("Password confirmation does not match") user = models.User.objects.create_user( username=email, email=email, password=password1 ) avatar = request.FILES.get("avatar") if avatar is not None and avatar != "": user.avatar = avatar first_name = request.POST.get("first_name") if first_name is not None: user.first_name = first_name last_name = request.POST.get("last_name") if last_name is not None: user.last_name = last_name gender = request.POST.get("gender") if gender is not None: user.gender = gender language = request.POST.get("language") if language is not None: user.language = language currency = request.POST.get("currency") if currency is not None: user.currency = currency birthdate = request.POST.get("birthdate") if birthdate is not None: user.birthdate = birthdate superhost = bool(request.POST.get("superhost")) if superhost is not None: user.superhost = superhost bio = request.POST.get("bio") if bio is not None: user.bio = bio user.save() return redirect(reverse("core:home")) except ValidationError as error: print(error) return redirect("core:home")
이 code는 forms.py를 사용한 code다. code 수를 확연히 줄인 것을 알 수 있다.
from django.shortcuts import render, redirect, reverse from django.contrib.auth import authenticate from . import forms def signUp(request): if request.method == "GET": form = forms.SignUpForm return render(request, "pages/users/signup.html", {"form": form}) elif request.method == "POST": form = forms.SignUpForm(request.POST) if form.is_valid(): form.save() email = form.cleaned_data.get("email") password = form.cleaned_data.get("password") user = authenticate(request, username=email, password=password) user.avatar = request.FILES.get("avatar") user.save() return redirect(reverse("core:home")) else: print(form.errors) return redirect(reverse("users:signup"))
templates
pages/users/signup.html
template part도 form을 받아서 각각의 key 값을 사용하면 알아서 input이나 select을 만들어 준다.
{% extends 'base.html' %} {% block page_title %} Sign Up {% endblock page_title %} {% block search-bar %} <div></div> {% endblock search-bar %} {% block content %} <div class="background"> <div class="wrap"> <form action="{% url 'users:signup' %}" method="post" class="form" enctype="multipart/form-data"> {% csrf_token %} <div class="form_input rounded-tl-lg rounded-tr-lg"> {{form.first_name}} </div> <div class="form_input"> {{form.last_name}} </div> <div class="form_input"> {{form.email}} </div> <div class="form_input"> {{form.password}} </div> <div class="form_input"> {{form.password1}} </div> <div class="form_input"> <label for="{{form.avatar.id_for_label}}" class="text-lg font-bold">Avatar</label> {{form.avatar}} </div> <div class="form_input"> {{form.bio}} </div> <div class="select"> <label for="{{form.gender.id_for_label}}">Gender:</label> {{form.gender}} </div> <div class="select"> <label for="{{form.language.id_for_label}}">Language:</label> {{form.language}} </div> <div class="select"> <label for="{{form.currency.id_for_label}}">Currency:</label> {{form.currency}} </div> <div class="birthdate rounded-br-xl rounded-bl-lg"> <label for="birthdate">Birthdate:</label> <input type="date" id="birthdate" name="birthdate"> </div> <button class="form_button">Submit</button> </form> </div> </div> {% endblock content %}
참고 자료
- 노마드 코더의 Airbnb 클론 강의
- form and filed validation
소스 코드
github.com/zpskek/airbnb-clone-v3/commit/b0d464579a0e60b37bdd8d2c9bdec98350888077
'Project using python > Cloning Airbnb' 카테고리의 다른 글
django login FBV (0) 2021.03.02 django signup CBV (0) 2021.03.01 django signup FBV (0) 2021.03.01 tailwind customzing (0) 2021.02.28 tailwind css @apply (0) 2021.02.28