본문 바로가기

Django

20210203_ Django 로그인,세션처리, render, redirect, is_valid, clean(), form 활용

Django

로그인

redirect 처리

확인해 보면 (개발자 툴 - Application - cookies)
브라우저가 다르면는 session을 다르게 인식하기 때문에 시크릿 모드 브라우저와 그냥 브라우저와 session id와 토큰이 다르다
각 세션 아이디 별로 세션 공간이 따로 존재하는데 이것을 쿠키임-> 자동으로 세션 관리해줌으로 그냥 쓰면 됨

세션 값들이 클라이언트를 식별하게 해주는 식별자가 됨

render vs redirect

  • render : render(request, template_name, context=None, content_type=None, status=None, using=None)

    • context 값을 넘길 수 있음, 템플릿을 불러옴
  • redirect : redirect(to, permanent=False, *args, **kwargs)

    • 단지, URL로 이동함(이동하여 해당 views가 다시 실행됨) context 값 못 넘김

세션과 로그인 처리

from django.http import HttpResponse
from django.shortcuts import render
from django.contrib.auth.hashers import make_password, check_password  # 저장된 password 암호화
from .models import Fcuser
# Create your views here.


def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')

    elif request.method == 'POST':
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        res_data = {}
        if not (username and password):
            res_data['error'] = '모든 값을 입력해 주세요.'
        else:
            sl_user = Sl_user.objects.get(username=username)
            if check_password(password, sl_user.password):
                user_id = sl_user.username
                # 객체의 username을 user_id 할당
                request.session['user'] = user_id
                # 세션에서 user를 key로하고 값을 할당 받은 user_id로 설정 
                return redirect('/')
                # '/' 홈으로 가게 만듦음
            else:
                res_data['error'] = '잘못된 아이디 또는 비밀번호 입니다.'
        return render(request, 'login.html', res_data)

home 만들기

  • home의 경우 어느 앱에도 속하지 않는 최상단 url로서 일단 프로젝트 폴더 urls를 설정한다. (결국에는 sl_user.views에서 home 함수를 만들어서 프로젝트 url로 연결)
  • 프로젝트 urls.py
from django.contrib import admin
from django.urls import path, include
from sl_user.views import home

urlpatterns = [
    path('admin/', admin.site.urls),
    path('sl_user/', include('sl_user.urls')),
    path('', home)
]

  • views.py
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.contrib.auth.hashers import make_password, check_password  # 저장된 password 암호화
from .models import Sl_user
# Create your views here.


def home(request):
    user_id = request.session.get('user')
    # home의 세션을 user_id에 할당 시켜서

    if user_id:
        sl_user = Sl_user.objects.get(pk=user_id)  # pk primary key
        # 모델에서 키가 user_id인 경우의 객체를 가져와 sl_user에 할당
        return HttpResponse(sl_user.username)
        # sl_user 객체의 username을 반환하여 표시

    return HttpResponse('Home!') # 맞지 않으면 Home!으로 표시
  • 로그아웃
def logout(request):
    if request.session.get('user'):
        del(request.session['user'])
# 세션이 있으면 세션을 제거하여 로그아웃 됨
    return redirect('/')
  • 로그인, 로그아웃 url연결
from django.urls import path
from.import views

urlpatterns = [
    path('register/', views.register),
    path('login/', views.login),
    path('logout/', views.logout),
]

template 상속

  • html 구성이 대부분이 중복되고 조금 다르기에 상속으로 효율적으로 할 필요가 있음

  • 또한 수정시 유지 보수의 편의성을 위해서 필요함

  • 기준 template를 만들고 상속받아 원하는 부분만 바꿈

  • base.html 을 만들어서 무조건 중복되는 부분을 만듦

  • container 클래스 안에 부분이 변경 됨으로 그 부분만 바꾸도록 설정

  • 블록 구성이나 지정할때 주석 넣으면 인식 안됨 주의!

  • base.html

<html>
<head>
        <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> -->
    <!-- bootstrap에서 다운받은 stylesheet 를 연결시켜 적용 -->
    <link rel="stylesheet" href="/static/bootstrap.min.css" />
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js" integrity="sha384-q2kxQ16AaE6UbzuKqyBE9/u/KzioAlnx2maXQHiDX9d4/zp8Ok3f+M7DPm+Ib6IU" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.min.js" integrity="sha384-pQQkAEnwaBkjpqZ8RU1fF1AKtTcHJwFl3pblpTlHXybJjHpMYo79HY3hIi4NKxyj" crossorigin="anonymous"></script>
</head>
<body>
    <div class="container">
        {% block contents %}
        {% endblock %}
    </div>
</body>
</html>

  • login.html
{% extends "base.html" %}

{% block contents %}
<div class="row mt-5">
    <div class="col-12 text-center">
        <h1>로그인</h1>
    </div>
</div>
<div class="row mt-5">
    <div class="col-12">
        {{error}}
    </div>
</div>
<div class="row mt-5">
    <div class="col-12">
        <form method="POST", action=".">
            {% csrf_token %}
            <div class="form-group">
                <label for="username" class="form-label">사용자 이름</label>
                <input type="text" class="form-control" id="username" placeholder="사용자 이름" name="username">
            </div>
            <div class="form-group">
                <label for="password" class="form-label">비밀번호</label>
                <input type="password" class="form-control" id="password" placeholder="비밀번호" name="password">
            </div>
            <button type="submit" class="btn btn-primary">로그인</button>
        </form>
    </div>
</div>
{% endblock %}

  • register.html
{% extends "base.html" %}

{% block contents %}
<div class="row mt-5">
    <div class="col-12 text-center">
        <h1>회원가입</h1>
    </div>
</div>
<div class="row mt-5">
    <div class="col-12">
        {{error}}
    </div>
</div>
<div class="row mt-5">
    <div class="col-12">
        <form method="POST", action=".">
            {% csrf_token %}
            <div class="form-group">
                <label for="username" class="form-label">사용자 이름</label>
                <input type="text" class="form-control" id="username" placeholder="사용자 이름" name="username">
            </div>
            <div class="form-group">
                <label for="useremail" class="form-label">사용자 이메일</label>
                <input type="email" class="form-control" id="useremail" placeholder="사용자 이메일" name="useremail">
                </div>
            <div class="form-group">
                <label for="password" class="form-label">비밀번호</label>
                <input type="password" class="form-control" id="password" placeholder="비밀번호" name="password">
            </div>
            <div class="form-group">
                <label for="re-password" class="form-label">비밀번호 확인</label>
                <input type="password" class="form-control" id="re-password" placeholder="비밀번호 확인" name="re-password">
            </div>
            <button type="submit" class="btn btn-primary">등록</button>
        </form>
    </div>
</div>
{% endblock %}



django form을 이용한 구성

  • django에서 제공하는 form 만들기 기능을 통해서 html, view를 좀더 쉽게 구현 가능함

  • form이 제공하는 기본적인 기능들이 있지만 그것에서 조건을 추가하여 전에 기능을 구현 가능함 (복잡해 보여도 조금더 단순하게 view 구성이 가능함)

form 구성

  • models 만드는 느낌으로 구성함
  • 폼을 구성하는 필드를 만듦
  • error_messages 속성은 views파일에서 form의 자체 함수 is_valid()error 통해 나타 나는 에러 메세지를 수정할 수 있음(기본 key가 required로 되어 있음)
  • passwordwidget 속성은 html에서 input에 대한 속성을 지정 가능
  • label은 위에 나타나는 label임

  • clean vs is_vaild
    • 원래 clean은 기본 내장함수로 views의 is_vaild 실행시 값의 존재 유무를 체크하여 타당한 값을 자동으로 cleaned_data라는 dict형태의 변수에 저장함
    • 폼 안의 clean 함수를 지정하는 이유는 기본 내장함수를 오버라이딩 하여 views is_vaild 실행시 빈값만이 아닌 조건을 커스터 마이징 하기 위함임
    • 그래서 기본적으로 빈값체크를 위해서 cleaned_data = super().clean()인 부모의 clean 함수를 통해 미리 cleaned_data를 체크하고 우리가 필요한 조건을 검사하게 함
  • username = cleaned_data.get('username'), password = cleaned_data.get('password') 를 통해서 해당 필드의 값을 가져와 할당함
  • form.py

from django import forms
from .models import Fcuser
from django.contrib.auth.hashers import check_password


class LoginForm(forms.Form):
    username = forms.CharField(error_messages={
        'required': '아이디를 입력해 주세요.'
    }, max_length=32, label="사용자 이름")
    password = forms.CharField(error_messages={
        'required': '비밀번호를 입력해 주세요.'
    }, widget=forms.PasswordInput, label="비밀번호")

    def clean(self):
        cleaned_data = super().clean()
        username = cleaned_data.get('username')
        password = cleaned_data.get('password')

        if username and password:
            sl_user = Sl_user.objects.get(username=username)
            #Sl_user모델의 객체에서 가져온다(username 필드와 아까 할당한 username과 같은 객체) -> sl_user 변수에 할당
            if not check_password(password, sl_user.password):
                # check_password를 통해 입력 받은 password와 sl_user의 password와 비교
                self.add_error('password', '비밀번호가 일치하지 않습니다.')
                # form 안에 기본적으로 있는 add_error()함수로 특정 필드에 error를 넣는 함수임(password를 키로 하고 값을 지정)
            else:
                self.user_id = sl_user.id
            # 이상 없으면 일치하는 객체 sl_user의 id를 use_id에 할당

views 구성

  • POST를 받는 형식은 같지만
def login(request):
    if request.method == "POST":
        form = LoginForm(request.POST)  # POST 데이터를 넣어서 form변수에 할당
        if form.is_valid():  # is_valid() 함수를 통해 정상적인지 검증(값이 단지 들어가 있는지 확인하는데 전에 커스터마이징 했음)
            # 세션 코드
            request.session['user'] = form.user_id
            return redirect('/')
    else:
        form = LoginForm() # 맞지 않으면 post 할당하지 않음

    return render(request, 'login.html', {'form': form})
  • html 데이터 표현 형식 설정
    • {{ form.as_p}} : 각요소를 P태그를 붙여서 정렬됨
    • {{form.as_table}} : 각요소가 한줄로
    • {% for field in form %} , {% endfor %} : form을 for 반복문으로 넣을 수 있음
    • 그리고 안에 어떤 형식을 반복할 것인지 넣을 수 있음
  • django reference-Looping over the form’s fields
{% extends "base.html" %}

{% block contents %}
<div class="row mt-5">
    <div class="col-12 text-center">
        <h1>로그인</h1>
    </div>
</div>
<div class="row mt-5">
    <div class="col-12">
        {{error}}
    </div>
</div>
<div class="row mt-5">
    <div class="col-12">
        <form method="POST", action=".">
            {% csrf_token %}
            <!-- for반복문 넣기 -->
            {% for field in form %}
            <!-- 생성된 form의 fireld 가져와 반복 -->
            <div class="form-group">
                <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                <input type="{{ field.field.widget.input_type }}" class="form-control" id="{{ field.id_for_label }}" placeholder="{{ field.label }}" name="{{ field.name }}">
            </div>
            {% if field.errors %}
            <!-- field 에러시 if 조건문 -->
            <span style="color: red">{{ field.errors }}</span>
            {% endif %}
            {% endfor %}
            <button type="submit" class="btn btn-primary">로그인</button>
        </form>
    </div>
</div>
{% endblock %}