[django 쇼핑몰] Product 상품 정보 팝오버 (ListAPI, Django REST framework(DRF), serializer, onmouseenter, onmouseleave, ajax, mixins)
● DRF (Django REST framework)
- django REST API를 위한 프레임워크이다.
- REST API
백엔드와 프론트엔드를 완전히 독립적으로 진행하기 위해서는 REST API가 가장 이상적인 방법이라고 한다. 프론트엔드가 백엔드에게 정보를 요청할 때 사용하는 것
- serializer 직렬화
DRF 에서 가장 메인이 되는 기능이 serializer 직렬화이다. API 통신에서 주고 받는 데이터 타입은 대부분 JSON이다. 하지만 python에서의 모델 객체의 데이터타입은 QuerySet 이다. 더군다나 쿼리셋은 굉장히 복잡한 구조로 이루어져있기 때문에 Query Set을 JSON 타입으로 바꾸는 것(직렬화)이 어렵다. 하지만 이러한 기능을 제공하는 것이 serializer이다.
https://velog.io/@ground4ekd/django-rest-framework
Django REST Framework (DRF)
들어가기 Django 에서 REST API 를 위한 프레임워크가 있는데 그것이 DRF (Django REST Framework) 이다 실제 Django 에서 PUT, PATCH, DELETE 를 제대로 지원하지 않기 때문에 RESTful 하게 작업하려면 DRF가 필요하다 최근에 알게 된 것이지만 PUT과 PATCH는 비슷하면서 다른 뜻이다. PUT은 ...
velog.io
● 상품 정보 API
- 상품 목록 페이지에서 상품명에 마우스를 올려놓으면 해당 상품의 정보가 팝오버로 뜰 수 있도록 하기 위해 API를 사용한다.
- 일단 djangorestframework 를 설치하고 INSTALLED_APPS에 rest_framework를 추가한다.
pip install djangorestframework
INSTALLED_APPS += ['rest_framework',]
product/serializers.py
- 어떤 모델의 객체를 가져와서 정보를 직렬화할 것인지 정의한다.
- model : 어떤 모델 가져올지
- fields : 해당 모델에서 어떤 필드 정보를 가져올 것인지
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
product/views.py/ProductListAPI (mixins.ListModelMixin)
- 어떤 모델을 직렬화할지 정의한 ProductSerializer 을 가져와서 API 클래스 내의 serializer_class 에 넣는다.
- 어떤 쿼리셋을 가져올 것인지 get_queryset 함수에서 반환값으로 설정한다.
- GET 요청이 왔을 때, get 함수에서는 list 메소드를 사용하여 데이터들을 리스트형태로 반환한다.
from rest_framework import generics, mixins
from .serializers import ProductSerializer
class ProductListAPI(generics.GenericAPIView, mixins.ListModelMixin):
serializer_class = ProductSerializer
def get_queryset(self):
return Product.objects.all().order_by('id')
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
urls.py에서 해당 api와 연결하고 해당 경로로 연결하면 아래와 같은 형태로 출력된다.
실제 상품 목록 페이지에서 나타나는 정보와 동일한 것을 알 수 있다.
product/views.py/ProductDetailAPI (mixins.RetrieveModelMixin)
- 어떤 모델을 직렬화할지 정의한 ProductSerializer 을 가져와서 API 클래스 내의 serializer_class 에 넣는다.
- 어떤 쿼리셋을 가져올 것인지 get_queryset 함수에서 반환값으로 설정한다.
- GET 요청이 왔을 때, get 함수에서 retrieve 메소드를 사용하여 DB에서 특정 레코드만 반환한다.
class ProductDetailAPI(generics.GenericAPIView, mixins.RetrieveModelMixin):
serializer_class = ProductSerializer
def get_queryset(self):
return Product.objects.all().order_by('id')
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
api/product/1 의 페이지 요청 결과이다. product id 1인 상품의 상세정보가 그대로 전달된 것을 알 수 있다.
그렇다면 이제 상품목록 페이지에서 해당 상품명에 마우스를 올렸을 때 api로 받은 정보를 띄워주면 된다.
templates/product_list.html body
- 상품목록 페이지에서 상품명에 마우스를 올리면 정보가 띄워져야 하니까 상품명 정보를 출력하는 {{product.name}} 부분에 a 태그를 추가한다.
- a태그에는 해당 상품명의 아이디, 마우스 커서가 상품명 안에 들어올 때(mouseenter) 마우스가 상품명 밖을 벗어날 때(mouseleave) 의 실행할 함수, 상품명을 클릭했을 때 이동할 URL 주소. 총 4가지를 설정한다.
{% block contents %}
<div class = "row mt-5">
<div class = "col-12">
<table class = "table table-dark">
<thead class = "thead thead-dark">
<td scope = "col">No.</td>
<td scope = "col">상품명</td>
<td scope = "col">가격</td>
<td scope = "col">등록 날짜</td>
</thead>
<tbody class = "text-light">
{% for product in object_list %}
<tr>
<td scope = "col">{{product.id}}</td>
<td><a id = "product-{{product.id}}"
onmouseenter = "product_detail( {{ product.id }} );"
onmouseleave = "product_leave( {{product.id}} );"
href = "/product/{{product.id}}">{{ product.name }}</a></td>
<td>{{ product.price | intcomma }}원</td>
<td>{{ product.registered_date | date:'Y-m-d H:i'}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
templates/product_list.html header
$(document).ready
- 페이지가 로딩이 끝나고 준비가 되었을 때 이 함수를 호출한다. 상품 목록 전체를 받아오는 api에서 값을 가져와 콘솔창에 출력하도록 한다. 아래와 같이 product_list.html 페이지 로딩이 끝나고 콘솔창에 아래와 같이 출력되었다.
function product_detail
- 마우스 커서를 상품명에 올렸을 때 실행되는 product_detail 함수에서는 html, content 값을 아래와 같이 설정하여 content 를 구성하는데에 html tag를 태그 자체로 인식하게 끔(True) 설정한다.
- url에 입력한 주소와의 통신이 성공하면 result에는 서버가 반환해주는 데이터가 저장된다.
- url "/api/product/1" 의 경로로 이동하여 통신에 성공하면 해당 서버가 JSON 배열 데이터를 반환하기 때문에 product 객체에 대한 정보가 result에 저장되어 success 관련 로직에서 상품의 이름과 가격을 출력할 수 있는 것이다.
function product_leave
- 마우스 커서가 상품명 구역을 벗어났을 때에 띄워놓은 팝업창을 없앤다.
{% extends 'base.html' %}
{% load humanize %
{% block header %}
<script>
$(document).ready(function(){
$.ajax({
url : "/api/product",
success : function(result){
console.log(result)
}
});
})
function product_detail(id){
$.ajax({
url:"/api/product/"+id,
success : function(result){
$("#product-"+id).popover({
html : true,
content : result.name + "<br>" + result.price
}).popover('show');
}
})
}
function product_leave(id){
$("#product-"+id).popover('hide');
}
</script>
{% endblock %}
마우스 팝오버 실행 결과
- 1번 상품명에 커서를 올렸을 때, 다음과 같이 팝업창이 띄워지는 것을 알 수 있다.