ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • django signup FBV using forms.py
    Project 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 %}
      

    참고 자료

    소스 코드

    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

    댓글

Designed by Tistory.