1. Django Tutorial(Airbnb) - Django Messages

🌈 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.')
# 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;
    }
}

좋은 웹페이지 즐겨찾기