해리의 데브로그

Django 18 - 관계 설정(1) / 유저정보 추가 및 권한 설정

|

관계설정 (1) - 유저 정보 추가

Models.py 수정

  • 유저정보를 저장시킬 새로운 필드인 user를 posts/models.py 내 추가
  • settings.AUTH_USER_MODEL 에는 Django에서 기본적으로 내장되어있는 User 모델에 대한 정보가 담겨있음. 해당 모델을 사용하기 위해 from django.conf import settings 입력
  • models.ForeignKey()는 외래키를 설정하는 함수임
    • 1번째 인자에는 외래키가 연결되는 모델(테이블)을 입력함
    • 2번째 인자에는 외래키가 바라보는 값이 삭제될 때, 외래키를 갖고오는 필드를 어떻게 처리할건지에 대한 옵션을 입력함
      • CASCADE : 부모가 삭제 되면, 자기 자신도 삭제
      • PROTECT : 자식이 존재하면, 부모 삭제 불가능 ( ProtectedError 발생시킴)
      • SET_NULL : 부모가 삭제되면, 자식의 부모 정보를 NULL로 변경
  • 스키마 재설정
    • python manage.py makemigrations
    • python manage.py migrate
from django.conf import settings

class Post(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    content = models.TextField()
    ...(중략)

게시글에 유저정보 표시하기

post_form 은 모델폼인 PostForm의 양식에 따라 저장된 정보를 담고 있는 인스턴스 객체임. 모델 폼의 인스턴스 객체를 post 라는 변수에 다시 저장시킴. 이때, commit=False 속성을 적용하여 DB에 바로 저장시키지 않음.

여기서 잠깐 , post_formpost 는 아래와 같이 정리할 수 있음

  • post_form : 모델폼 PostForm의 인스턴스 객체
  • post : 모델 Post의 인스턴스 객체

모델폼은 모델로부터 일종의 wrapping되어 있는 개념인데, 모델에 user 정보를 넣기 위해서는 wrapping 되어있는 모델폼을 벗겨줄 필요가 있음. 이러한 작업을 하기 위해서 PostForm 의 인스턴스 변수를 Post의 인스턴스 변수인 post에 다시 저장시키는 것임.

post 의 멤버변수인 user에 현재 들어오는 request의 user를 저장시키고, 최종적으로 DB에 저장을 시킴.

이후, 유저 정보는 _post.html 내 원하는 영역에 {{ post.user.username}} 와 같은 방식으로 코드를 작성하여 표시 시킬 수 있음.

@login_required
def create(request):
    if request.method == 'POST':
        post_form = PostForm(request.POST, request.FILES)
        if post_form.is_valid():
            post = post_form.save(commit=False)
            post.user = request.user
            post.save()
        return redirect('posts:list')
    
    else:
        post_form = PostForm()
    return render(request, 'posts/form.html', {'post_form': post_form})

사용자 권한 설정

Django에서는 내장되어있는 User 모델을 추가적인 작업없이 바로 불러올 수 있음. 이를 활용하여, 본인이 작성한 글에 대해서만 수정 또는 삭제를 할 수 있도록 설정을 할 수 있음.

  • _post.html 에서 게시글의 유저정보와 로그인된 유저정보가 같은 경우에만 수정/삭제 링크가 뜨도록 조건문을 적용시킴.
<!--게시글의 유저와 지금 로그인된 정보가 유저가 같다면-->
{% if post.user == user %}
<a href="{% url 'posts:update' post.id %}" class="btn btn-info">Update</a>
<a href="{% url 'posts:delete' post.id %}" class="btn btn-primary">Delete</a>
{% endif %}
  • 그러나 게시글의 id값을 알고 있으면 주소창에 url 주소를 입력하여 여전히 수정 또는 삭제를 할 수 있음. 따라서 추가적인 코드 작성을 통해 url 주소를 통한 접근을 막을 필요가 있음.
def update(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    
    #post의 유저와 현재 로그인된 유저가 일치하지 않다면, 리스트 페이지를 보여주게 함
    if post.user != reqeust.user:
        return redirect('posts:list')
    
    if request.method == 'POST':
        post_form = PostForm(request.POST, request.FILES, instance=post)

        if post_form.is_valid():
            post_form.save()
            return redirect('posts:list')
            
    else:
        post_form = PostForm(instance=post)
    
    return render(request, 'posts/form.html', {'post_form': post_form})

def delete(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    
    #post의 유저와 현재 로그인된 유저가 일치하지 않다면, 리스트 페이지를 보여주게 함
    if post.user != reqeust.user:
        return redirect('posts:list')

    post.delete()
    return redirect('posts:list')

Django 17 - 미디어 파일 관리 및 업로드 구현

|

미디어 파일 관리 및 업로드 구현

Models.py 수정

  • 이미지 업로드를 위한 필드 추가.
  • 빈값을 넣어도 되는 옵션인 blank=True 추가
  • pip install pillow 설치
class Post(models.Model):
    content = models.TextField()
    image = models.ImageField(blank=True)

이미지 재 가공

기존의 ImageField를 그대로 사용할 경우, 이미지 크기를 수정하거나, 저장 확장자 설정 등 이미지 가공에 많은 제약이 있음. 이에, 외부 라이브러리를 사용하여 이미지를 적절하게 재가공할 수 있음

  • pip install pilkit & pip install django-imagekit 설치
  • settings.py 내 INSTALLED_APPS 내에 앱 추가
INSTALLED_APPS = [
    'imagekit',]

Models.py 재 수정

  • imagekit 사용을 위해 import
    • from imagekit.models import ProcessedImageField
    • from imagekit.processors import ResizeToFill
  • ProcessedImageField 의 세부항목
    • upload_to : 미디어파일의 저장 위치 설정
    • processors : 업로드시 어떠한 처리과정을 거칠건지에 대한 설정. 처리할 작업 목록을 리스트 형태로 작성 . 안의 내용 수정 시, 추가적으로 migrate는 필요 없음
    • ResizeToFill : 설정한 사이즈에 딱 맞춰서 이미지 크기 수정. 가로가 긴 경우, 좌,우 영역을 삭제함
    • ResizeToFit : 비율 유지한 채로 이미지 크기 수정. 가로가 긴 경우, 사이즈에 맞추고 위 아래 여백이 생김.
    • options : 저장 포맷 관련 옵션 (JPEG 압축률 설정)
from imagekit.models import ProcessedImageField
from imagekit.processors import ResizeToFill

class Post(models.Model):
    content = models.TextField()
    # image=models.ImageField(blank=True) 기존코드; ImageField 사용
    image = ProcessedImageField(
               upload_to='posts/images', # 저장 위치
               processors=[ResizeToFill(600,600)], # 처리할 작업 목록
               format='JPEG', # 저장 포맷(확장자)
               options= {'quality': 90 }, # 저장 포맷 관련 옵션 (JPEG 압축률 설정)
        )

저장 위치 임의 설정하기

  • create 페이지에서 모델 폼에 따라 내용이 작성되면, instance가 생성된다. instance에는 데이터에 대한 고유 값과 정보가 담겨 있음. 이를 활용하여 파일 경로를 개별적으로 설정 해 줄 수 있음.
  • filename은 확장자를 함께 갖고 있음
  • instance.pk 는 사용이 불가능하다. 이유는 호출되는 시점이 DB에 저장이 되지 않은 시점이기 때문에, 경로를 결정하는 id값을 갖고 있지 않기 때문이다. 따라서 None 폴더에 저장되어 instance.pk 는 추천되지 않는 방법임.
def post_image_path(instance, filename): 
    # return f'posts/{instance.content}/{filename}'
    return f'posts/{instance.content}/{instance.content}.jpg'

class Post(models.Model):
    content = models.TextField()
    image = ProcessedImageField(
               upload_to = post_image_path, 
               processors = [ResizeToFill(600,600)], 
               format = 'JPEG', 
               options = {'quality': 90 }, 
        )

upload_to may also be a callable, such as a function. This will be called to obtain the upload path, including the filename. This callable must accept two arguments and return a Unix-style path (with forward slashes) to be passed along to the storage system. The two arguments are:

Argument Description
instance An instance of the model where the FileField is defined. More specifically, this is the particular instance where the current file is being attached.In most cases, this object will not have been saved to the database yet, so if it uses the default AutoField, it might not yet have a value for its primary key field.
filename The filename that was originally given to the file. This may or may not be taken into account when determining the final destination path.

For example:

def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return 'user_{0}/{1}'.format(instance.user.id, filename)

class MyModel(models.Model):
    upload = models.FileField(upload_to=user_directory_path)

forms.py 수정

  • image 필드 추가
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['content', 'image']

create.html 수정

  • 이미지 파일은 바이너리 형태이므로 enctype 속성을 form 태그 내 추가로 입력해줘야함. form 태그는 기본적으로 텍스트 데이터를 전송함
  • enctype : 데이터를 보낼 때 사용하는 인코딩 유형을 정의할 때 사용하는 속성
    • application/x-www-form-urlencoded : form 태그의 기본값으로 텍스트 데이터만 전송 가능
    • multipart/form-data: binary 데이터 전송 가능
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block container %}
<h1>New Post</h1>

<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {% bootstrap_form post_form %}
    <input type="submit" value="Submit"/>
</form>
{% endblock %}

settings.py 설정

  • 이미지를 업로드 할 때 저장되는 기본 경로 설정
  • 경로 설정은 기본 템플릿 설정 시 진행했던 방법과 매우 유사함.
  • 아래의 코드의 경우, Root Directory 바로 밑 media 폴더 내에 이미지 파일이 저장됨.
#Media
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

프로젝트 내 urls.py 수정

  • from django.conf.urls.static import static : 정적 파일을 제공하기 위한 내장함수
  • from django.conf import settings : settings.py 내 설정한 미디어 경로를 사용할 수 있게 함
  • 미디어 파일을 주소로 설정하는 코드 작성
from django.conf.urls.static import static
from django.conf import settings

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

2019년 6월 2주차 TIL

|

2019-06-03

  • Django 관련 마크다운을 작성하여 깃허브 블로그에 올리는 작업이 어지간히 쉬운일이 아니다. 큰 애를 먹고 있는 부분이, Liquid tag error로 인해 마크다운 업로드 build에 실패하는 부분인데, Django에서 매우 많이 활용하는 진자 템플릿이나, 템플릿 변수에서 사용하는 {{ }} 이나 {% %} 등의 코드에서 많은 에러가 발생하여 깃허브 블로그 build에 어려움을 겪고 있다.
  • 문제가 될만할 부분은 raw 와 endraw로 묶어서 최대한 추려냈음에도 몇 번의 시행착오를 처음에 겪는건 어쩔 수 없는듯 하다. 해당 이슈로 에러가 발생하는 case들을 최대한 모아서 따로 정리를 해봐야겠다.
The page build failed for the `master` branch with the following error:

The tag `csrf_token` on line 69 in `_posts/Django/2019-04-27-Django06_CRUD3.md` is not a recognized Liquid tag. For more information, see https://help.github.com/en/articles/page-build-failed-unknown-tag-error.

For information on troubleshooting Jekyll see:

  https://help.github.com/articles/troubleshooting-jekyll-builds

If you have any questions you can contact us by replying to this email.

2019-06-04

  • Django 미디어 파일 업로드 및 관리에 대한 내용을 마크다운으로 정리해보았다. 미디어 파일을 다룰 때는 특히나 부수적인 코드작성이 많이 필요한데, 여러번 반복을 해서 코드를 짜봐도 손에 100% 익히는덴 더 많은 연습이 필요 할 듯 하다.
    • form 태그 속성에 enctype을 추가 설정 / setttings.py 내 이미지 업로드 경로 설정 / urls.py 내 settings.py에서 설정한 미디어 경로 지정하기
  • 시간이 날 때 staticfile 관리에 대한 Django 공식문서를 찬찬히 읽어보자.

2019-06-05

  • Bixby Capsule 디자인 및 Endpoint, 외부서버 연동하기 강의 공부 및 캡슐 실습을 하였음. 좀 더 세부적인 강의가 빨리 업로드되었으면 되었으면 좋겠다.

2019-06-06

  • Django의 유저 관련 모델 관계설정 및 유저 정보를 추가하는 내용을 복습해보았다. model에 user 필드는 장고에 내장되어잇는 user 모델을 외래키를 통해 관계설정을 하는데, 해당 필드에 유저 정보를 실제 view에서 입력할때는 모델폼을 바로 DB에 저장하지 않고 commit=False 를 이용한 후, 래핑되어있는 모델폼 인스턴스를 다시 모델 인스턴스에 저장시켜, user 정보를 입력하였다.
  • Django 수업에서 들었던 내용을 다시 복기하면서 블로그에 올리기 위한 최종본으로 마크다운을 작성 한 후, 연습 프로젝트를 새로 작성하여 관련 부분을 스스로 코드로 작성하며 복습을 하고 있는데, 어떠한 자료도 보지 않고 혼자서 코드를 짜다보면 어처구니 없는 부분에서 가끔 실수가 나오고 있다.
  • 오늘은 models.py 내 user 필드에 대한 코드를 작성할 때, 끝부분에 ,를 실수로 넣는 바람에, 유저정보가 뜨지 않아 1시간 가까이 어디가 틀렸는지 헤메고 말았다. 좀 더 잔실수를 줄이며 집중해서 코드를 작성할 수 있도록 해야겠다.

2019-06-07

댓글 기능 구현 및 1:N 관계 설정 복습을 진행하였음.

  • Comment(N)와 Post(1)을 연결시킬 때는 외래키를 입력하는 필드(comment.post_id)가 생성됨.
  • 유저 모델과 마찬가지로 wrapping 되어있는 정보를 commit=False 를 통해 벗겨 낸 뒤, 필드의 정보를 추가로 입력 가능
  • @login_required와 마찬가지로 HTTP methods에 대한 데코레이터 설정을 활용하여 HTTP methods를 제한적으로 허용가능함. 허나 decorators가 저장되어있는 경로는 미세하게 다름
    • from django.views.decorators.http import require_POST
    • from django.contrib.auth.decorators import login_required
  • 진자템플릿 문법에서는 1에서 N으로 접근할 경우, post.comment.set.all()이 아니라 post.comment.set 으로 코드를 작성해야함 ( () 뺄 것.)

Django 16 - 사용자 권한 관리

|

사용자 권한 관리

로그인 정보 및 사용자 인증 관련 링크 연결

로그인 정보 및 사용자 인증 관련 링크(회원가입/로그인/로그아웃)은 어느 페이지를 가던 표시가 되어야하므로, 모든 페이지의 기본이 되는 기본 템플릿(base.html; insta\templates)에 코드를 작성하는 것이 가장 적합하다.

  • Django는 기본적으로 user 모델을 갖고 있으며, 템플릿 변수로 사용하는 것이 가능하다. 이를 통해 해당 객체의 username을 갖고 올 수 있음.
  • 사용자 인증 링크는 a태그로 작성 가능함
  • 조건문을 사용하여, 상황에 따라(로그인 유무) 다른 링크가 보여주도록 분기
    • user.is_authenticated : user가 로그인되어있는지 유무를 확인
    • 마찬가지로, 로그인 된 경우에만 새글 쓰기 링크가 보이도록 조건 설정
{% if user.is_authenticated %}
<h3> {{ user.username }}님 환영합니다.</h3>
<a href="{% url 'posts:create' %}"><h3>새글 쓰러 가기</h3></a>
<a href="{% url 'accounts:logout' %}">로그아웃</a>
{% else %}
<a href="{% url 'accounts:signup' %}">회원가입</a>
<a href="{% url 'accounts:login' %}">로그인</a>
{% endif %}

회원 가입 후 자동 로그인 구현

  • auth_login(request, user) 코드를 추가로 삽입하여 세션에 로그인 정보를 생성 & 저장 시킬 수 있음.
def signup(request):
    if request.method == 'POST':
        signup_form = UserCreationForm(request.POST)
        if signup_form.is_valid():
            #회원가입 폼의 데이터를 DB에 저장하는 코드를 user 라는 변수에 저장
            user = signup_form.save()
            #회원가입 후 자동 로그인 코드
            auth_login(request, user)
            return redirect('posts:list')
    #중략

로그인 후 회원가입 및 로그인 페이지 접근 막기

기본 템플릿에 user.is_authenticated에 따라 선별된 링크가 보이게 분기시켜주었으나, 여전히 주소창에 회원가입 및 로그인 페이지로 이동하는 URL 주소를 입력하여 접근하는 것이 가능하다. 따라서 이러한 접근을 막기위한 추가적인 코드를 작성할 필요가 있음.

  • views.py 내 login 및 singup 함수 바로 아래에 추가적인 조건문을 작성
  • request.user.is_authenticated : 요청된 user가 이미 유효하다면(로그인되어있다면), list 페이지로 바로 redirect 시킴
def login(request):
    if request.user.is_authenticated:
        return redirect('posts:list')
    #중략
    
def signup(request):
    if request.user.is_authenticated:
        return redirect('posts:list')
    #중략

새글 생성 페이지(new.html) 의 접근(URL 주소) 막기

위의 방식과 마찬가지로, 로그인이 되어있는 경우에만 새글이 보이도록 base.html에서 조건문을 적용하였으나, 주소창을 통한 접근이 가능하다. 회원가입 및 로그인 페이지 접근을 막았던 방법을 동일하게 적용하여, login 페이지로 redirect 시키는 아래의 코드를 사용할 수도 있으나, Django에서는 @login_required 라는 파이썬 데코레이터를 또한 사용할 수 도 있다.

#로그인 후 회원가입 페이지 접근을 막았던 방법
def create(request):
    if not request.user.is_authenticated:
        return redirect('accounts:login')

@login_required 는 파이썬썬 문법으로 정의된 파이썬 데코레이터이며, 일련의 함수/메서드이다. 이 함수의 파라미터로 아래 함수를 받는 개념이다.

  • Django 내부에 함수로 이미 정의 되어있으며, 적용하고자 하는 함수 위에 데코레이터로 사용 가능
  • 데코레이터 import 요 from django.contrib.auth.decorators import login_required
  • 로그인이 되어있지 않은 경우 로그인 페이지로 redirect 시킴
  • Django 에 정의된 데코레이터 함수의 기본값은 아래와 같음. 지금까지 우리는 어플리케이션 명과 함수이름을 아래 URL에 맞게 정의해줬기 때문에, 인자값을 따로 넣어주지 않아도 로그인 페이지로 redirect 시킬 수 있음.
login_required(login_url='/accounts/login') 
  • setttings.py 를 통해 redirect 시키고자 하는 경로를 임의로 지정하여 기본값을 변경 할 수 있음
    • 예) LOGIN_URL = '/accounts/login'
from django.contrib.auth.decorators import login_required

@login_required
def create(request):
    if request.method == 'POST':
        post_form = PostForm(request.POST)
        
        if post_form.is_valid():
            post_form.save()
            return redirect('posts:list')
    
    else:
        post_form = PostForm()
    return render(request, 'posts/form.html', {'post_form': post_form})

새글 생성 페이지(new.html) 의 접근(URL 주소) 막기 (2)

로그인 하지 않은 상태에서 새글 생성 페이지를 주소창을 통해 접근 할 경우, 로그인 화면으로 redirect 되도록 @login_required 데코레이터를 사용해보았다. 이후, 로그인을 하면, 다시 리스트 페이지로 돌아가게되어, 다시 새글쓰기 링크를 선택해야하는 번거로움이 생기게 된다. 따라서 이 경우, 로그인을 하면 새글을 쓰는 창으로 바로 넘어가게 설정을 할 수 있음.

  • 위의 case에서 새글 생성 페이지를 주소창을 통해 접근하면 아래와 같이 URL이 변경됨
    • 기본주소/accounts/login/?next=/posts/new
    • 해당 주소로도 새글 생성 페이지로 들어갈 수 있도록 login 함수의 redirect 경로 설정
def login(request):
    if request.user.is_authenticated:
        return redirect('posts:list')
        
    if request.method == 'POST':
        login_form = AuthenticationForm(request, request.POST)
        if login_form.is_valid():
            auth_login(request, login_form.get_user())
            return redirect(request.GET.get('next') or 'posts:list')
        
    else:
        login_form = AuthenticationForm()
    
    return render(request, 'accounts/login.html', {'login_form' : login_form})

Django 15 - 사용자 인증(회원가입/로그인/로그아웃)

|

사용자 인증

회원 가입 및 로그인 등 사용자 인증에 관한 코드를 Django에서 구현해보도록 하자. 이들은 전적으로 새로운 기능으로, 이 기능만 담당하는 App을 새로 만드는 것이 적절함. (App 이름은 accounts로 명명함)

  • setttings.py - INSTALLED_APPS에 accounts 추가
  • accounts 앱 내에 urls.py 생성 및 INSTA 프로젝트 urls.py 내에 accounts 앱 경로 재 설정

회원가입

회원가입은 하나의 어떤 유저를 만드는 것. 이는 CRUD 로직 중 Create를 구현하는 것과 동일하다. Django에서는 user모델(이름, 아이디, 비밀번호 등)과 모델폼이 이미 구현되어 있기 때문에, 이들을 import 하여 views.py에 뿌려주기만 하면 된다. 기본적인 골자는 모델폼 기반의 create 함수에 대한 코드를 따른다.

  • UserCreationFormdjango.contrib.auth.form 내 구현되어있는 회원가입 모델 폼으로 import
#views.py
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm

def signup(request):
    #HTTP Method가 POST 인 경우
    if request.method == 'POST';
    	signup_form = UserCreationForm(request.POST)
        #모델폼의 유효성 검증이 valid할 경우, DB에 저장
        if signup_form.is_valid():
            signup_form.save()
        return redirect('posts:list')
    
    #HTTP Method가 GET 인 경우
    else:
        signup_form = UserCreationForm()
    
    return render(request, 'accounts/signup.html', {'signup_form':signup_form})
  • signup.html
    • accounts\templates\accounts 내에 html 문서 생성
{% extends 'base.html' %}
{% block container %}

<h1>회원 가입</h1>

<form method="POST">
    {% csrf_token %}
    {{ signup_form }}
    <input type="submit" name="Submit"/>
</form>

{% endblock %}

로그인 & 세션

세션이란, 웹 서버에서 임시로 클라이언트의 데이터를 갈무리하는 것을 뜻한다. 쿠키와 비슷한 역할이나, 쿠키는 클라이언트 측에 데이터를 갈무리 하는 반면에, 세션은 서버측에 데이터를 갈무리한다는 차이점이 있다. 주로 로그인, 온라인 쇼핑몰의 장바구니 등에 쓰인다 (나무위키)

웹 브라우저와 서버가 HTTP 프로토콜을 통해서 하는 모든 커뮤니케이션은 무상태(stateless)라고 합니다. 프로토콜이 무상태라는 뜻은 클라이언트와 서버 사이의 메시지가 완벽하게 각각 독립적이라는 뜻입니다. 여기엔 이전 메시지에 근거한 행동이나 순서(sequence)라는 것이 존재하지 않습니다. 결국, 만약 사이트가 클라이언트와 계속적인 관계를 유지하는 것을 당신이 원한다면, 당신이 직접 그 작업을 해야합니다.

세션이라는 것은 Django 그리고 대부분의 인터넷에서 사용되는 메카니즘으로, 사이트와 특정 브라우저 사이의 “state”를 유지시키는 것입니다. 세션은 당신이 매 브라우저마다 임의의 데이터를 저장하게 하고, 이 데이터가 브라우저에 접속할 때 마다 사이트에서 활용될 수 있도록 합니다. 세션에 연결된 각각의 데이터 아이템들은 “key”에 의해 인용되고, 이는 또다시 데이터를 찾거나 저장하는 데에 이용됩니다. (장고 튜토리얼)

장고 서버는 우리가 접속하고 있는 브라우저의 정보를 임시로 가지고 있음. 따라서 어떤 클라이언트가 어떤 페이지를 보고 있는지 등의 정보를 장고에서 알 수 있음. 로그인의 경우, 마치 파이썬에서 사용한 글로벌 변수처럼, 일반적으로 페이지를 이동하더라도 유지되어야 함. 우리는 세션을 이용하여 페이지가 이동하더라도 유지되도록, 로그인 기능을 구현할 수 있음.

  • AuthenticationForm : 유저가 존재하는지를 검증하는 Django 내장 모델 폼. 사용자가 로그인 폼에 작성한 정보가 유효한지를 검증함
  • auth_login (default: login)
    • 유저 정보를 세션에 생성 및 저장하는 역할을 하는 Django 내장 함수.
    • login_form.get_user() 를 통해 login_form에 저장된 유저 정보를 갖고와 세션에 유저 정보를 생성함
    • AuthenticationForm 은 유저가 존재하는지를 검증할 뿐, 세션과는 무관함을 유의하자.
#views.py

# AuthenticationForm 모델폼 import
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm

# jango.contrib.auth 내 함수 login을 import함.
# (views.py 내 정의한 함수 login과 구분하기 위해 auth_log로 재 명명함)
from django.contrib.auth import login as auth_login

#Session Create
def login(request):
    if request.method == 'POST':
        login_form = AuthenticationForm(request, request.POST)
        if login_form.is_valid():
            auth_login(request, login_form.get_user())
        return redirect('posts:list')
    
    else:
        login_form = AuthenticationForm()
    
    return render(request, 'accounts/login.html', {'login_form' : login_form})


#urls.py
urlpatterns = [
    path('login/', views.login, name='login'),    
]
  • login.html
{% extends 'base.html' %}
{% block container %}
<h1>로그인</h1>

<form method="POST">
    {% csrf_token %}
    
    {{ login_form }}
    <input type="submit" value="Submit"/>
</form>
{% endblock %}

로그아웃

  • django.contrib.authlogout 함수를 import 하여 세션에 저장된 유저 정보를 제거
#views.py
from django.contrib.auth import logout as auth_logout

def logout(request):
    auth_logout(request)
    return redirect('posts:list')

#urls.py
path('logout/', views.logout, name='logout')


AuthenticationForm에 대한 고찰

  • AuthenticationForm 은 유저가 존재하는지를 검증하는 모델폼임.
  • AuthenticationForm 의 생성자의 메서드를 살펴보면, 아래와 같이 정의되어있음. 생성자 메서드의 첫번째 인자는 반드시 request 객체가 들어가야하며, request 객체 명 또는 None값을 적용할 수 있음.
    • 브라우저의 쿠키 support를 받아 쿠키와 세션간의 유효성 검증을 하고 싶은 경우, request 객체를 첫번째 인자로 넣어줌.
    • 유효성 검증을 위한 form data는 키워드 인자 data= 에 들어감
    def __init__(self, request=None, *args, **kwargs):
        """
        The 'request' parameter is set for custom auth use by subclasses.
        The form data comes in via the standard 'data' kwarg.
        """
        self.request = request
        self.user_cache = None
        super(AuthenticationForm, self).__init__(*args, **kwargs)
  • 이를 통해 우리가 구현할 수 있는 AuthenticationForm 포맷 예제는 다음과 같음
AuthenticationForm(data=request.POST)
AuthenticationForm(request, request.POST)
AuthenticationForm(None, request.POST)
  • Reference

http://www.janosgyerik.com/django-authenticationform-gotchas/ https://docs.djangoproject.com/en/1.8/_modules/django/contrib/auth/forms/ https://stackoverflow.com/questions/8421200/using-authenticationform-in-django