🔥 templatetags 기능로 toggle 구현
🔥 get_or_create 매서드
✔️ 사용자가 객실의 즐겨찾기 버튼을 누르면, "List" Model에 해당 객실 정보가 저장되는 즐겨찾기 기능을 구현해보겠습니다.
✔️ "List" 모델을 "OneToOneField"와 "ManyToManyField"로 필드를 만들어 주었습니다.
from django.db import models from core import models as core_models # Create your models here. class List(core_models.TimeStampedModel): """List Model Definition""" name = models.CharField(max_length=80) # 👈 간단한 name 필드에요. user = models.OneToOneField( "users.User", related_name="list", on_delete=models.CASCADE ) # 👈 OneToOne 필드는 1:1 관계를 가집니다. room = models.ManyToManyField( "rooms.Room", related_name="list", blank=True ) # 👈 여러 객실을 즐겨찾기할 수 있기 때문에 ManyToManyField 필드입니다. def __str__(self): return self.name def count_rooms(self): return self.room.count() count_rooms.short_description = "Number of Rooms"
✔️ 모델을 확인했으니, Template Tag를 만들어 보겠습니다. 이 Template Tag의 역할은 현재 객실이 사용자의 List 모델에 이미 저장되어있으면 True, 존재하지 않으면 False를 반환해줍니다.
✔️ List 모델에 해당 객실이 존재하는지 여부를 확인하는 이유는 이 판단에 따라 "즐겨찾기 추가", "즐겨찾기 제거" 버튼을 나타내주기 위함입니다.
✔️ 템플릿 태그를 만드는 방법은 템플릿 필터를 만드는 방법과 유사합니다. 사용할 app 내에 templatetags 디렉토리를 생성 후,__init__.py
과 작업할 파일(on_fave.py) 생성해 주면 됩니다.
✔️ 탬플릿 태그를 만들 때는 simple_tag에 등록해 줍니다. 이와 함께 takes_context=True를 parameter로 전달해 요청한 사용자의 정보를 가져올 수 있습니다. 이 경우에는 만들어줄 함수의 첫번째 인자로 context를 받아와야 합니다.
✔️ user와 name 기준으로 get_or_none()을 사용해서 QuerySet에서 Object를 가져옵니다. 존재한다면, 해당 List Object에서 모든 room 중에서 현재의 room이 존재하는지 찾아 bool 값을 return해주면 됩니다.
# lists/templatetags/on_fave.py from django import template # 👈 "template" import from lists import models as list_models register = template.Library() # 👈 Library() 에 등록합니다. @register.simple_tag(takes_context=True) def on_favs(context, room): # 👈 takes_context=True이기 때문에 첫번째 인자는 context를 받아야해요:) # print(context.request, room) user = context.request.user the_list = list_models.List.objects.get_or_none( user=user, name="My Favourites Houses" ) return room in the_list.room.all() # 👈 True or False 반환
✔️ 즐겨찾기와 관련한 경로를 매핑해줍니다.
✔️ "toggle_room"은 해당 객실을 List 모델에 저장하기 위해 "room_pk"값을 argument로 전달합니다.
✔️ "SeeFavsView"는 사용자가 즐겨찾기 한 객실 목록으로 이동 시켜줍니다.
from django.urls import path from . import views app_name = "lists" urlpatterns = [ path("toggle/<int:room_pk>", views.toggle_room, name="toggle-room"), # 👈 즐겨찾기 저장 path("favs/", views.SeeFavsView.as_view(), name="see-favs"), # 👈 즐겨찾기 목록 제공 ]
✔️ "toggle btn"은 객실 상세보기(room_detail.html)에서 templatetag를 사용하며 만들어줍니다.
✔️ 이미 load 중인 항목이 있다면 아래처럼 연이어 입력해 load해 올 수 있습니다.
✔️ 위에서 만든 "on_favs" 템플릿 태그를 사용할 때, 함수로 전달할 변수가 있다면 아래 처럼 옆에 입력해줍니다.
✔️ 즉, "on_favs" 함수의 반환 값에 따라 "즐겨찾기 제거(Remove) 버튼" 또는 "즐겨찾기 저장(Save) 버튼"이 Html에 나타납니다.
# templates/rooms/room_detail.html {% extends "base.html" %} {% load is_booked on_favs i18n %} {% block page_title %} {{room.name}} {% endblock page_title %} {% block content %} ... ... {% on_favs room as on_favs_boolean %} # 👈 on_fave 함수에 room 을 전달합니다. {% if on_favs_boolean %} <a class="block mb-10 w-2/6 text-teal-600 font-bold" href="{% url 'lists:toggle-room' room.pk %}?action=remove">{% trans "Remove from Favourites" %}</a> # True일 때,, 👈 action값에 remove를 넣어줍니다. {% else %} <a class="block mb-10 w-2/6 text-teal-600 font-bold" href="{% url 'lists:toggle-room' room.pk %}?action=add">{% trans "Save to Favourites" %}</a> # 👈 False일 때, action값에 add를 넣어줍니다. {% endif %} ... ... {% endblock %}
✔️ a태그에서 설정해둔 action값을 통해 Url에서 어떤 toggle 버튼이 눌렸는지 View에서 그 값을 확인할 수 있습니다.
✔️ "get_or_create" 매서드는 튜플 형태로 생성된 객체(Object)와 생성 여부(Create)를 반환해줍니다. List 모델의 room 필드는 ManyToMnayfield이기 때문에 add를 통해 값을 추가하거나 remove를 통해 값을 제거할 수 있습니다.
✔️ 아래에서는 the_list가 생성된 Object이고, 언더바가 Bool값을 지닌 Create의 여부를 담고 있습니다. 또한 "get_or_create"는 save()를 하지 않아도 자동으로 save() 매서드를 호출해줍니다.
✔️ 즐겨찾기 목록 페이지를 위한 CBV는 TemplateView를 상속받아 간단하게 구현해주었습니다.
from django.shortcuts import redirect, reverse from django.views.generic import TemplateView from rooms import models as room_models from . import models def toggle_room(request, room_pk): action = request.GET.get("action", None) # 👈 action값 추출 room = room_models.Room.objects.get_or_none(pk=room_pk) # 👈 Room Object 가져오기 if room is not None and action is not None: the_list, _ = models.List.objects.get_or_create( user=request.user, name="My Favourites Houses" ) # 👈 List Object 생성 if action == "add": # 👈 추가 the_list.room.add(room) elif action == "remove": # 👈 제거 the_list.room.remove(room) return redirect(reverse("rooms:detail", kwargs={"pk": room_pk})) class SeeFavsView(TemplateView): # 👈 즐겨찾기 목록을 TemplateView를 상속하여 보여줍니다. template_name = "lists/list_detail.html"
✔️ nav.html에 "favs" 버튼을 만들어 즐겨찾기에 저장한 목록이 표시되도록 하였어요.
<li class="nav_link"><a href="{% url 'lists:see-favs' %}">{% trans "Favs" %} ({{user.list.room.count}})</a></li>
✔️ related_name 값인 list를 이용하여, 해당 사용자의 List Object로 접근하여 모든 room field값을 화면에 출력합니다.
{% extends "base.html" %} {% block page_title %} {{user.first_name}}'s Fav {% endblock page_title %} {% block content %} <div class="min-h-75vh"> <h3 class="mb-12 text-2xl text-center">Your Favourites</h3> <div class="container mx-auto pb-10 "> <div class="flex flex-wrap -mx-40 mb-10"> {% for room in user.list.room.all %} # 👈 related_name으로 접근 {% include 'mixins/room_card.html' with room=room %} {% endfor %} </div> </div> </div> {% endblock content %}