1. Django Tutorial(Airbnb) - Django Messages

ID์งฑ์žฌยท2021๋…„ 8์›” 17์ผ
0

Django

๋ชฉ๋ก ๋ณด๊ธฐ
24/43
post-thumbnail

๐ŸŒˆ Django Messages

๐Ÿ”ฅ Messages Framework ์‚ฌ์šฉํ•ด๋ณด๊ธฐ


1. Django์˜ Messages Framework

1) Messages Tag

  • Django์˜ Messages Framework๋ฅผ ํ†ตํ•ด ์˜ˆ์™ธ์ฒ˜๋ฆฌ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ๋งˆ๋‹ค ํ…œํ”Œ๋ฆฟ์— ๋‚˜ํƒ€๋‚ผ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์†์‰ฝ๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด์š”. ๋‚ด์žฅ๋˜์–ด ์žˆ๊ธฐ ๋–„๋ฌธ์— Project๋ฅผ ์„ค์น˜ํ•  ๋•Œ ๋ถ€ํ„ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , ์•„๋ž˜์™€ ๊ฐ™์€ ํƒœ๊ทธ๋“ค์ด ์ค€๋น„๋˜์–ด ์žˆ์–ด์š”.
messages.debug(request, '%s SQL statements were executed.' % count)
messages.info(request, 'Three credits remain in your account.')
messages.success(request, 'Profile details updated.')
messages.warning(request, 'Your account expires in three days.')
messages.error(request, 'Document deleted.')
  • ์ฐธ๊ณ  : https://docs.djangoproject.com/en/2.2/ref/contrib/messages/
  • ์šฐ์„  ๋ฉ”์‹œ์ง€๋ฅผ ํƒ€๋‚˜๋‚ผ ํ…œํ”Œ๋ฆฟ์„ partials ์•ˆ์— ๋งŒ๋“ค๊ณ , base.html์— include์‹œํ‚ค๊ฒ ์Šต๋‹ˆ๋‹ค:)
  • message.tags๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด ๊ทธ ์ด๋ฆ„์„ class๋ช…์œผ๋กœ ๊ฐ–๊ฒŒ ํ–ˆ์–ด์š”.
# partials/messages.html
{% if messages %}
<ul class="messages">
    {% for message in messages %}
    <li class="message {% if message.tags %}{{ message.tags }}{% endif %}">{{ message }}</li>
    {% endfor %}
</ul>
{% endif %}

2) using messages

  • Messages Framework์€ ์•„๋ž˜ ์œ„์น˜ํ•ด ์žˆ์–ด์š”.
    • ๐Ÿ”Ž from django.contrib import messages
  • ๊ฐ ์—๋Ÿฌ ๋‚ด์šฉ์— ๋งž๊ฒŒ ์—๋Ÿฌ ์•ˆ์— ๋‚˜ํƒ€๋‚ผ ๋ฉ”์‹œ์ง€๋ฅผ ๋‹ด๊ณ , except ๊ตฌ๋ฌธ์—์„œ ํ•ด๋‹น ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ž‘์„ฑํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
    • ๐Ÿ”Ž messages.error(request, e)
  • ์„ฑ๊ณต๋ฉ”์‹œ์ง€๋Š” login() ๋งค์„œ๋“œ ๋ฐ”๋กœ ์•„๋ž˜ ์ถ”๊ฐ€ํ•˜์˜€์–ด์š”:)
# from django.views import View
import os
import requests
from django.views.generic import FormView
from django.urls import reverse_lazy
from django.shortcuts import render, redirect, reverse
from django.contrib.auth import authenticate, login, logout
from django.core.files.base import ContentFile
from django.contrib import messages # ๐Ÿ‘ˆ "messages"๋ฅผ import ํ•ด์š”:)
from . import forms, models
...
...
def log_out(request):
    messages.info(request, f"See you later") # ๐Ÿ‘ˆ info ๋ฉ”์‹œ์ง€
    logout(request)
    return redirect(reverse("core:home"))
def github_login(request):
    client_id = os.environ.get("GITHUB_ID")
    redirect_uri = "http://127.0.0.1:8000/users/login/github/callback"
    return redirect(
        f"https://github.com/login/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&scope=read:user"
    )
class GithubException(Exception):
    pass
def github_callback(request):
    try:
        client_id = os.environ.get("GITHUB_ID")
        client_secret = os.environ.get("GITHUB_SECTET")
        code = request.GET.get("code", None)
        if code is not None:
            token_request = requests.post(
                f"https://github.com/login/oauth/access_token?client_id={client_id}&client_secret={client_secret}&code={code}",
                headers={"Accept": "application/json"},
            )
            token_json = token_request.json()
            error = token_json.get("error", None)
            if error is not None:
                raise GithubException("Can't get authoriztion code.")  # ๐Ÿ‘ˆ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
            else:
                access_token = token_json.get("access_token")
                profile_request = requests.get(
                    "https://api.github.com/user",
                    headers={
                        "Authorization": f"token {access_token}",
                        "Accept": "application/json",
                    },
                )
                profile_json = profile_request.json()
                username = profile_json.get("login", None)
                if username is not None:
                    name = profile_json.get("name")
                    email = profile_json.get("email")
                    bio = profile_json.get("bio")
                    try:
                        user = models.User.objects.get(email=email)
                        if user.login_method != models.User.LOGIN_GITHUB:
                            raise GithubException(
                                f"Please log in with : {user.login_method}"
                            ) # ๐Ÿ‘ˆ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
                    except models.User.DoesNotExist:
                        user = models.User.objects.create(
                            email=email,
                            first_name=name,
                            username=email,
                            bio=bio,
                            login_method=models.User.LOGIN_GITHUB,
                            email_verified=True,
                        )
                        user.set_unusable_password()
                        user.save()
                    login(request, user)
                    messages.success(request, f"Welcome back! {user.first_name}") # ๐Ÿ‘ˆ ์„ฑ๊ณต ๋ฉ”์‹œ์ง€
                    return redirect(reverse("core:home"))
                else:
                    raise GithubException("Can't get your profile") # ๐Ÿ‘ˆ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
        else:
            raise GithubException("Can't get code") # ๐Ÿ‘ˆ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
    except GithubException as e:
        messages.error(request, e) # ๐Ÿ‘ˆ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋‹ด์•„ ์ „๋‹ฌ
        return redirect(reverse("users:login"))
def kakao_login(request):
    client_id = os.environ.get("KAKAO_ID")
    redirect_uri = "http://127.0.0.1:8000/users/login/kakao/callback"
    return redirect(
        f"https://kauth.kakao.com/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code"
    )
class KakaoException(Exception):
    pass
def kakao_callback(request):
    try:
        code = request.GET.get("code")
        client_id = os.environ.get("KAKAO_ID")
        redirect_uri = "http://127.0.0.1:8000/users/login/kakao/callback"
        token_request = requests.get(
            f"https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={client_id}&redirect_uri={redirect_uri}&code={code}"
        )
        # print(token_request.json())
        token_json = token_request.json()
        error = token_json.get("error", None)
        if error is not None:
            raise KakaoException("Can't get authoriztion code.")  # ๐Ÿ‘ˆ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
        access_token = token_json.get("access_token")
        profile_request = requests.get(
            "https://kapi.kakao.com//v2/user/me",
            headers={"Authorization": f"Bearer {access_token}"},
        )
        profile_json = profile_request.json()
        email = profile_json.get("kakao_account").get("email")
        if email is None:
            raise KakaoException("Please also give me your email")  # ๐Ÿ‘ˆ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
        properties = profile_json.get("kakao_account").get("profile")
        nickname = properties.get("nickname")
        profile_image = properties.get("profile_image_url")
        print(nickname, profile_image)
        try:
            user = models.User.objects.get(email=email)
            if user.login_method != models.User.LOGIN_KAKAO:
                raise KakaoException(f"Please log in with : {user.login_method}")  # ๐Ÿ‘ˆ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
        except models.User.DoesNotExist:
            user = models.User.objects.create(
                email=email,
                first_name=nickname,
                username=email,
                login_method=models.User.LOGIN_KAKAO,
                email_verified=True,
            )
            user.set_unusable_password()
            user.save()
            if profile_image is not None:
                photo_request = requests.get(profile_image)
                user.avatar.save(
                    f"{nickname}-avatar", ContentFile(photo_request.content)
                )
        login(request, user)
        messages.success(request, f"Welcome back {user.first_name}")  # ๐Ÿ‘ˆ ์„ฑ๊ณต ๋ฉ”์‹œ์ง€
        return redirect(reverse("core:home"))
    except KakaoException as e:
        messages.error(request, e) # ๐Ÿ‘ˆ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋‹ด์•„ ์ „๋‹ฌ
        return redirect(reverse("users:login"))

3) Design Messages

  • ๋ฉ”์‹œ์ง€๊ฐ€ ์ƒ๋‹จ์— ๋…ธ์ถœ๋˜๋„๋ก, "partials/messages.html"์ด "base.html" head ์œ„์— ๋ฐฐ์น˜ํ–ˆ์–ด์š”:)
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css">
    <title>{% block page_title %}{% endblock page_title %}| Nbnb</title>
</head>
<body class="text-gray-800 mt-24 font-light">
    {% include "partials/messages.html" %}  # ๐Ÿ‘ˆ ์ถ”๊ฐ€
    <header class="container max-w-full inset-0 flex items-center justify-between px-6 h-20 border-b border-gray-400 fixed bg-white">
        <div class="flex items-center w-1/3">
            <a href="{% url "core:home" %}" class="mr-6">
                <img class="w-8" src="{% static 'img/airbnb_logo.png' %}">
            </a>
            {% block search-bar %}
            <form method="get" action="{% url "rooms:search" %}" class="w-9/12">
                <input class="search-box border px-5 w-full font-medium text-gray-900 placeholder-gray-600 py-3 rounded-sm shadow-md hover:shadow-lg focus:outline-none" name="city" placeholder="Search by City" type="text">
            </form>
            {% endblock search-bar %}
        </div>
        {% include "partials/nav.html" %}
    </header>
    {% block content %}{% endblock content %}
    {% include "partials/footer.html" %}
</body>
</html>
  • "partials/messages.html"์— TailwindCSS๋ฅผ ์ ์šฉ์‹œ์ผœ๋ณผ๊ป˜์š”.
{% if messages %}
<ul class="absolute top-0 mx-auto left-0 right-0 flex justify-center">
    {% for message in messages %}
    <li class="message font-medium bg-gray-700 z-10 rounded-full text-white py-4 px-6 w-64 text-center {% if message.tags %}{{ message.tags }}{% endif %}">{{ message }}</li>
    {% endfor %}
</ul>
{% endif %}
  • Message Tag ๋ณ„๋กœ ์ƒ‰๊น”์„ ๋‹ค๋ฅด๊ฒŒ์ฃผ๊ณ , ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:)
@tailwind base;
@tailwind components;
@tailwind utilities;
...
...
...
@keyframes messageFadeIn {
    0% {
      opacity: 0;
      transform: translateY(-50px);
    }
    5% {
      opacity: 1;
      transform: translateY(50px);
    }
    95% {
      opacity: 1;
      transform: translateY(50px);
    }
    100% {
      opacity: 0;
      transform: translateY(-50px);
    }
  }
.message{
    animation: messageFadeIn 5s ease-in-out forwards; # forwards๋Š” css๊ฐ’์„ ์œ ์ง€์‹œ์ผœ์ค๋‹ˆ๋‹ค:)
    &.error{
        @apply bg-red-600;
    }
    &.info{
        @apply bg-blue-500;
    }
    &.success{
        @apply bg-green-500;
    }
    &.warning{
        @apply bg-yellow-400;
    }
}

profile
Keep Going, Keep Coding!

0๊ฐœ์˜ ๋Œ“๊ธ€