ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Django Auth, Login & Logout
    Django 2024. 8. 17. 06:26

    Django Auth

     

    auth로 Login, Logout 기능을 구현할 수 있다.

     

    로그인

     

    1. accounts App 생성

    python manage.py startapp accounts

     

    *계정 관련된 로직은 accounts 앱으로 하는 것이 일반적이다.

    • settings.py 가서 앱 등록
    • urls.py 만들기

     

    2. 로그인 구현하기

     

    urls.py
    from django.urls import path
    from . import views
    
    app_name = "accounts"
    urlpatterns = [
        path("login/", views.login, name="login"),
    ]

     

    views.py
    from django.shortcuts import render
    from django.contrib.auth.forms import AuthenticationForm
    
    
    def login(request):
        form = AuthenticationForm()
        context = {"form": form}
        return render(request, "accounts/login.html", context)

     

    login.html
    {% extends "base.html" %}
    
    {% block content %}
        <h1>로그인</h1>
    
        <form action="{% url 'accounts:login' %}" method="POST">
            {% csrf_token %}
            {{ form.as_p }}
            <button type="submit">로그인</button>
        </form>
    
    {% endblock content %}

     

     

    프로젝트 앱 url과 account 앱의 url 을 연결해줘야 한다. (my_first_pjt 의 urls.py 열어서 추가)
    path("accounts/", include("accounts.urls")),

     

     

    서버 실행해보면 잘 된다.🫢

     

     

    3. admin 계정 만들기

     

    근데 superuser 네이밍 정말 직관적이다ㅎㅎ🤭

    python manage.py createsuperuser

     

    그 다음은 시키는대로, 아이디와 패스워드를 입력해주면 된다. password 너무 쉬운거 입력하면 진짜 사용할건지 물어본다. 똑똑👍

     

    SQL explorer 에서 확인해보면 잘 만들어진 걸 볼 수 있다.

     

     

    💡 이제 뷰로 가서,

    GET 요청이 들어오면 → 유저한테 비어있는 로그인 페이지를 보여주면 되고

    POST 요청이 들어오면 → ID / PW 입력하고 들어온 것이기 때문에, 로그인 처리 해주고, redirect 해서 다른 페이지로 넘겨주면 된다.

     

    # accounts/views.py
    
    from django.shortcuts import render, redirect
    from django.contrib.auth import login as auth_login
    from django.contrib.auth.forms import AuthenticationForm
    
    
    def login(request):
        if request.method == "POST":
            form = AuthenticationForm(data=request.POST)
            if form.is_valid():
                auth_login(request, form.get_user())
                return redirect("articles:index ")
        else:
            form = AuthenticationForm()
        context = {"form": form}
        return render(request, "accounts/login.html", context)

     

     

    서버 실행하고

    아까 내가 만든 슈퍼유저😎 계정으로 로그인 

     

    자 이제, 좌상단에 로그인 바로가기 링크하나 만들어주자.

     

    4. 로그인 링크 달아주기 (base.html)

     

    기본 코드

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
    
        <div class="navbar">
            <a href="{% url 'accounts:login'%}">로그인</a>
        </div>
    
        <div class="container">
            {% block content %}
            {% endblock content %}
        </div>
    
    </body>
    </html>

     

     

     

    base.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <!-- CSS 스타일 -->
        <style>
            .navbar {
                padding: 8px;
                background-color: #eef5fb;
                display: flex;
                justify-content: flex-start; /* 왼쪽 정렬 */
            }
    
            .navbar a {
                font-weight: bold; /* 글씨체 볼드로 */
                font-size: 16px; /* 글씨 크기 */
                color: #334c73; /* 텍스트 색상 */
                text-decoration: none; /* 밑줄 제거 */
                padding: 8px 16px; /* 좌우 여백 */
                font-family: Arial, sans-serif; /* 글씨체 */
            }
    
            .navbar a:hover {
                color: #2889f2; /* 호버 시 텍스트 색상 */
            }
    
            .container {
                padding: 16px;
            }
        </style>
    </head>
    <body>
    
        <div class="navbar">
            <a href="{% url 'accounts:login' %}">로그인</a>
        </div>
    
        <div class="container">
            {% block content %}
            {% endblock content %}
        </div>
    
    </body>
    </html>

     

    이제 어딜 가도 로그인 페이지로 바로 이동 할 수 있다👌


     

    이제 로그아웃 구현해보자.

     

    로그아웃

     

     accounts/urls.py

    from django.urls import path
    from . import views
    
    app_name = "accounts"
    urlpatterns = [
        path("login/", views.login, name="login"),
        path("logout/", views.logout, name="logout"),
    ]

     

    acoounts/views.py

    def logout(request):
        if request.method == "POST":
            auth_logout(request)
        return redirect("index")

     

    base.html (로그인 코드 아래 추가해준다)

        <form action="{% url 'accounts:logout' %}" method="POST">
            {% csrf_token %}
            <input type="submit" value="로그아웃">
        </form>

     

     

    로그아웃 버튼..바꾸고 싶..  일단 서버에 잘 반영됨

     

    💡 여기서 잠깐, 서버 오류를 처리하는 방법에 대해 알아보자!

     

    Django가 HTTP를 처리하는 다양한 방법이 있는데, 템플릿을 렌더링해서 전달하는 render()와 특정 경로로 요청을 전달하는 redirect()는 우리가 계속 써왔던거고, 오류를 처리하는 방법에 대해 알아보자👀

     

    📌  statust code400번대로 처리(클라이언트 오류)할  수 있다👌 

    •  get_object_or_404()
      • get을 호출한 후 객체가 없다면 404 에러를 raise하여 404 페이지로 이동시킵니다.
    • get_list_or_404()
      • filter를 호출한 후 빈 리스트라면 404 에러를 raise하여 404페이지로 이동합니다.

     

    get_object_or_404로 수정해주면

    def article_detail(request, pk):
    	article = get_object_or_404(Article, pk=pk)
    	context = {
    		"article": article,
    		}
    	return render(request, "articles/article_detail.html", context)

     

    999번째 글로 호출 한번 해보면 이제 404 오류로 잘 수정되었다.

     

    나머지, delete 함수와 update 함수도 다 수정

     

      

     

     

     

       

     


     

    View Decorators

     

    Django에서도 데코레이터 함수를 쓸 수 있다.

     

    • require_http_methods()
      • view 함수를 특정한 method 요청에 대해서만 허용
    • require_POST()
      • POST 요청만 허용

     

    아래 코드의 경우,

    데코레이터로 require_POST() 를 써주면 if문이 필요없다🤔

    def logout(request):
        if request.method == "POST":
            auth_logout(request)
        return redirect("index")

     

       ↓ 

    @require_POST()
    def logout(request):
        auth_logout(request)
        return redirect("index")

     

     

    로그인 코드랑 import해줘야 될 것들도 모두 수정해주자

    from django.shortcuts import render, redirect
    from django.contrib.auth.forms import AuthenticationForm
    from django.contrib.auth import login as auth_login
    from django.contrib.auth import logout as auth_logout
    from django.views.decorators.http import require_POST, require_http_methods
    
    @require_http_methods(["GET", "POST"])
    def login(request):
        if request.method == "POST":
            form = AuthenticationForm(data=request.POST)
            if form.is_valid():
                auth_login(request, form.get_user())
                return redirect("index") 
        
        else:
            form = AuthenticationForm()
        context = {"form": form}
        return render(request, "accounts/login.html", context)
    
    @require_POST
    def logout(request):
        auth_logout(request)
        return redirect("index")

     


     

    Template with Auth

     

    그런데 생각해보면🤔

    우리는 로그인을 하면 로그아웃 버튼이 보이고, 로그아웃 상태면 로그인 할 수 있는 입력창이 보인다. 

    그렇게 바꿔주려면 어떻게 해야할까?

     

    request.user는 '로그인 한 사용자'라는 객체를 가지고 있다. 즉, auth.User 클래스의 인스턴스를 가지고 있는데 

    로그인 했다면 request.user 인스턴스를 가지고 있고, 로그인하지 않았다면 AnonymousUser라는 인스턴스를 가지고 있다.

     

    그 속성을 이용해서 로그인했을때 로그아웃만 보이고 로그인 안하면 로그인만 보이도록 수정해보자.

     

    base.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
    
        <div class="navbar">
            {% if request.user.is_authenticated %}
                <h3>안녕하세요, {{ request.user }}님</h3>
                <form action="{% url 'accounts:logout' %}" method="POST">
                    {% csrf_token %}
                    <input type="submit" value="로그아웃">
                </form>
            {% else %}
                <a href="{% url 'accounts:login'%}">로그인</a>
            {% endif %}
        </div>
    
        <div class="container">
            {% block content %}
            {% endblock content %}
        </div>
    
    </body>
    </html>

    *request.user 대신에 user 만 사용해도 된다.

     


     

    접근 제한

     

    1. is_authenticated 

     

    accounts/viewspy

    @require_POST
    def logout(request):
        if request.user.is_authenticated:
            auth_logout(request)
        return redirect("index")

    이러면 로그인 되었있을때만 로그아웃 되도록 수정한거다.

     

     

    근데 현재 로그아웃 상태에서도 새글 작성하기가 가능하다... 심지어 남의 글 수정도, 삭제도 가능함ㅋㅋ😂

     

    articles/articles.html

    else 하나 넣어주고(== 로그인이 되어있지 않으면)

    로그인하고 글 작성하기 텍스트를 하나 넣어주고, 로그인 하는 페이지로 넘어가도록 코드를 추가하면 된다.

     

    확인해보면, 로그아웃 상태에서는 새 글 작성하기가 아니라 로그인하고 글 작성하기가 나온다.

     

     

    ✏️ 그런데 코드를 짜다보면 이런게 엄청 많을거다. '로그인 되어있으면' 이라는 조건을 사용할 일이 정말 많을 것이기 때문에, 이때 사용하면 좋은게 바로 데코레이터다. 우리가 쓰고싶은 건 로그인 데코레이터!

     

    2. @login_required

     

    이걸 쓰면, 로그인 되어있지 않은 상태에서 접근할 때, settings.LOGIN_URL 에 설정된 경로로 이동시킨다.

    기본값은 /accounts/login/  그래서 통상적으로 accounts라고 쓰는거다.

    settings.py에 가서 LOGIN_URL = "/ ~ /" 이렇게 우리가 설정해줘도 된다. LOGIN_URL 대문자여야함!

     

    맨 위에 login_required import 해줘야함

    from django.shortcuts import redirect, render, get_object_or_404
    from .models import Article
    from .forms import ArticleForm
    from django.contrib.auth.decorators import login_required
    from django.views.decorators.http import require_http_methods, require_POST

     

    현재 로그아웃 상태에서도 남의 글 수정, 삭제가 다 되기 때문에😂, delete 함수update 함수에도 @login_required 를 넣어줬다.

    @login_required
    def create(request):
    	if request.method == "POST":
    		form = ArticleForm(request.POST)
    		if form.is_valid():
    			article = form.save()
    			return redirect("articles:article_detail", article.pk)
    	else:
    		form = ArticleForm()
    	
    	context = {"form": form}
    	return render(request, "articles/create.html", context)
    
    
    @login_required
    def update(request, pk):
    	article = get_object_or_404(Article, pk=pk)
    	if request.method == "POST":
    		form = ArticleForm(request.POST, instance=article)
    		if form.is_valid:
    			article = form.save()
    			return redirect("articles:article_detail", article.pk)
    	else:
    		form = ArticleForm(instance=article)
    
    	context = {
    		"form": form,
    		"article": article,
    		}
    	return render(request, "articles/update.html", context)
    
    @login_required
    def delete(request, pk):
      article = get_object_or_404(Article, pk=pk)
      if request.method == "POST":
          article.delete()
          return redirect("articles:articles")
      return redirect("articles:article_detail", article.pk)

     

     

    근데 또 문제가... 

     

    유저가 새 글 작성하기를 했는데... 로그아웃상태라서 로그인 페이지로 이동돼서 유저가 로그인을 한 이후다. 안그래도 로그인 하라그래서 귀찮았는데 기껏 로그인했더니 인덱스 페이지로 보내??? 😤 

     

    로그인을 하면 유저가 원래 하려던 '새 글 작성하기' 페이지로 보내져야 한다. 

     

    코드를 수정해보자.

     

    accounts/views.py

    @require_http_methods(["GET", "POST"])
    def login(request):
        if request.method == "POST":
            form = AuthenticationForm(data=request.POST)
            if form.is_valid():
                auth_login(request, form.get_user())
                next_url = request.GET.get("next") or "index"
                return redirect(next_url) 
        
        else:
            form = AuthenticationForm()
        context = {"form": form}
        return render(request, "accounts/login.html", context)

     

     

     

    accounts/login.html

    {% extends "base.html" %}
    
    {% block content %}
        <h1>login</h1>
        <form action="" method="POST">
            {% csrf_token %}
            {{ form.as_p }}
            <button type="submit">로그인</button>
        </form>
    
    {% endblock content %}

     

    next 있는 현재 url 사용하라고 비워주기

     

     

    자 이렇게 하면,

    로그아웃 상태에서, 새 글 작성하기 하면 > 바로 로그인 하기로 넘어가고 > 로그인을 하면 > 바로 새 글 작성하기 로 넘어가게 됐다. 휴

     

     

    그리고 교재에, 에러페이지에 대한 걸 살펴보자. 위에서 내가 작성한 delete 함수 코드는.. 음.. 강의 듣다가 404 오류 잡는 코드 수정 중에 .. 교재랑 코드가 좀 달라졌다..🫠 암튼..

     

    delete 함수를 이렇게 쓰여져 있을때,

    @login_required
    @require_POST
    def delete(request, pk):
    	article = get_object_or_404(Article, pk=pk)
    	article.delete()
    	return redirect("articles:articles")

     

    로그아웃 상태에서 > 글 상세페이지에 들어가서 > 글 삭제하기를 누르면, 아래와 같은 오류페이지가 뜬다.

     

     

    사실 로그아웃 상태면 삭제나 수정 버튼이 보이면 안되지만.. ㅋㅋ

     

    이 오류를 해결해보자.

     

    delete 함수 안에 is_authenticated 를 넣어주는거다.

    @require_POST
    def delete(request, pk):
    	if request.user.is_authenticated:
    		article = get_object_or_404(Article, pk=pk)
    		article.delete()
    	return redirect("articles:articles")

     

    서버 실행해보면, 

     

    ????

     

    근데.. 아무리 찾아봐도 이유를 모르겠다. 어나니머스유저한테 is_authenticated 속성이 없다는 말이 뭔데.. 속성값을 '로그인 안되어있으면'으로 지정하라는건가..🫠 이게 뭔데..

     

    그래서 한참 또.. 설마 또 오타..? 하.. u 가 빠졌다. 정말 화가 났다..

    오타 자동으로 바로잡아주는 기능 같은 건 없나..

     

    수정하고 실행하면, 이제 삭제버튼을 눌러도 눌러지기만 하고 삭제는 안된다. 휴...

     

     

     

    AUTH 끝...🙌

     

     

    'Django' 카테고리의 다른 글

    Django Static & Media  (0) 2024.08.19
    Django 회원기능 구현하기  (0) 2024.08.18
    Django Form Class  (0) 2024.08.17
    Django Model, Migration, ORM, Database API  (0) 2024.08.16
    Django HTTP Form  (0) 2024.08.14
Designed by Tistory.