[멋사] OnlineShoppingmall - 장바구니

김지연·2023년 7월 29일
0

멋쟁이사자처럼_hywu

목록 보기
15/17
post-thumbnail

지난 시간 : 로그인까지 구현

오늘은 장바구니를 구현할 것이다.

일단 장바구니를 구현하기 위해서 cart라는 앱을 추가하자.

python manage.py startapp cart

이후 config/settings.py 에 등록하자.

INSTALLED_APPS = [
    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.naver',
    'allauth.socialaccount.providers.kakao',
    'shop',
    'cart',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.humanize',
    'disqus'
]

장바구니

cart = 선택한 상품을 주문하기 위해 보관 하는 기능

cart/cart.py 생성

from decimal import Decimal

from django.conf import settings
from shop.models import Product

class Cart(object):
	# 생성자 역할, 클래스 객체를 선언하면 자동으로 실행해 객체를 생성
    def __init__(self, request):
        self.session = request.session
        cart = self.session.get(settings.CART_ID) # settings에 CART_ID와 연결
        if not cart : # cart 정보가 없으면
            cart = self.session[settings.CART_ID] = {} # 새 딕셔너리 생성
        self.cart = cart

	
    def __len__(self): # 장바구니에 있는 상품의 수량을 전부 더한 결과
        return sum(item['quantity'] for item in self.cart.values())

    def __iter__(self): # for문 사용시 어떤 요소를 건내줄지 지정
        product_ids = self.cart.keys() # 제품 번호 목록을 가져옴

        products = Product.objects.filter(id__in=product_ids) # 장바구니에 있는 제품 정보만 Product db에서 가져옴

        for product in products: # 제품 정보를 하나씩 읽어와서
            self.cart[str(product.id)]['product'] = product # session에 키 값들을 넣을 때 문자로 넣는다

        for item in self.cart.values(): # 장바구니에 있는 제품을 하나씩 꺼내
            item['total_price'] = item['price'] * item['quantity'] # 가격 x item 수를 총 가격에 넣고
            item['price'] = Decimal(item['price']) # 가격에 숫자형으로 바꿔 item에 넣는다

            yield item

    # 장바구니에 넣기
    def add(self, product, quantity=1, is_update=False): # 제품 정보를 업데이트하는지 아닌지 확인
        product_id = str(product.id)
        if product_id not in self.cart:
            self.cart[product_id] = {'quantity':0, 'price':str(product.price)}

        if is_update:
            self.cart[product_id]['quantity'] = quantity
        else:
            self.cart[product_id]['quantity'] += quantity
        self.save()

    # 장바구니 저장하기
    def save(self):
        self.session[settings.CART_ID] = self.cart
        self.session.modified = True

    # 장바구니에서 삭제하기
    def remove(self, product):
        product_id = str(product.id)
        if product_id in self.cart: # 장바구니에 제품이 있다면
            del(self.cart[product_id]) # 해당하는 제품을 삭제하고
            self.save() # 현재 장바구니를 저장한다

    # 장바구니 비우기
    def clear(self):
        self.session[settings.CART_ID] = {}
        self.session.modified = True

    # 장바구니에 있는 제품의 총 가격 계산
    def get_product_total(self):
        return sum(Decimal(item['price']) * item['quantity'] for item in self.cart.values())

config/settings.py
CART_ID = 'cart_in_session'

세션으로 사용하는 방식이기 때문에 request.session에 데이터를 저장하고 꺼내오는 구조

세션에 값을 저장하려면 키 값을 설정해야 하는데 settings.py 에 CART_ID 라는 변수를 만들고
거기에 설정된 값을 가져다 사용하기 위해 settings.pyCART_ID 라는 변수를 사용


장바구니 - form

cart/forms.py

from django import forms


class AddProductForm(forms.Form):
    quantity = forms.IntegerField()
    is_update = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput)

is_update == 상세 페이지에서 추가할 때와 장바구니에서 수량을 바꿀 때 동작하는 방식이 다르도록 하기 위해 사용


장바구니 - view

cart/vies.py

from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_POST

from shop.models import Product
from .forms import AddProductForm
from .cart import Cart

# Create your views here.

@require_POST
def add(request, product_id):
    cart = Cart(request)
    product = get_object_or_404(Product, id=product_id)

    form = AddProductForm(request.POST)
    if form.is_valid():
        cd = form.cleaned_data
        cart.add(product=product, quantity=cd['quantity'], is_update=['is_update'])

    return redirect('cart:detail')

def remove(request, product_id) :
    cart = Cart(request)
    product = get_object_or_404(Product, id=product_id)
    cart.remove(product)
    return redirect('cart:detail')

def detail(request):
    cart = Cart(request)

    for product in cart :
        product['quantity_form'] = AddProductForm(initial={'quantity':product['quantity'], 'is_update':True})
    return render(request, 'cart/detail.html', {'cart':cart})

장바구니 - url

cart/urls.py

from django.urls import path
from .views import *

app_name = 'cart'

urlpatterns = [
    path('', detail, name='detail'),
    path('add/<int:product_id>', add, name='product_add'),
    path('remove/<product_id>', remove, name='product_remove'),
]

config/urls.py

from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('allauth.urls')),
    path('cart/', include('cart.urls')),
    path('', include('shop.urls')),
]

장바구니 - template

cart/templates/cart/detail.html

{% extends 'base.html' %}
{% load static %}

{% block title %}
    Shopping cart
{% endblock %}

{% block content %}
    <table class="table table-striped">
        <thead>
            <tr>
                <th scope="col">Image</th>
                <th scope="col">Product</th>
                <th scope="col">Quantity</th>
                <th scope="col">Remove</th>
                <th scope="col">Unit price</th>
                <th scope="col">Price</th>
            </tr>
        </thead>
        <tbody>
        {% for item in cart %}
            {% with product=item.product %}
            <tr>
                <th scope="row">
                    <a href="{{ product.get_absolute_url }}">
                        <img src="{{ product.image.url }}" class="img-thumbnail">
                    </a>
                </th>
                <td>{{ product.name }}</td>
                <td>
                    <form action="{% url 'cart:product_add' product.id %}" method="post">
                        {{ item.quantity_form.quantity }}
                        {{ item.quantity_form.is_update }}
                        <input type="submit" class="btn btn-primary" value="Update">
                        {% csrf_token %}
                    </form>
                </td>
                <td><a href="{% url 'cart:product_remove' product.id %}">Remove</a></td>
                <td class="num">${{ item.price }}</td>
                <td class="num">${{ item.total_price }}</td>
            </tr>
            {% endwith %}
        {% endfor %}
        {% if cart.coupon %}
            <tr class="subtotal">
                <td>Subtotal</td>
                <td colspan="4"></td>
                <td >${{ cart.get_product_total }}</td>
            </tr>
            <tr>
                <td>"{{ cart.coupon.code }}" coupon (${{ cart.coupon.amount }})</td>
                <td colspan="4"></td>
                <td >- ${{ cart.get_discount_total|floatformat:"2" }}</td>
            </tr>
        {% endif %}
        <tr class="total">
            <td>Total</td>
            <td colspan="4"></td>
            <td class="num">${{ cart.get_total_price|floatformat:"2" }}</td>
        </tr>
        </tbody>
    </table>

    <p class="text-right">
        <a href="{% url 'shop:product_all' %}" class="btn btn-secondary">Continue shopping</a>
        <a href="#" class="btn btn-secondary">Checkout</a>
    </p>
{% endblock %}

장바구니에 담는 기능은 상품의 상세 페이지에서 동작

shop/views.py의 product_detail 수정

from cart.forms import AddProductForm

# 상품 상세 페이지 뷰
def product_detail(request, id, product_slug=None) :
    product = get_object_or_404(Product, id=id, slug=product_slug)
    add_to_cart = AddProductForm(initial={'quantity':1})
    return render(request, 'shop/detail.html', {'product':product, 'add_to_cart':add_to_cart})
profile
천천히 꾸준히 하는 블로그

1개의 댓글

comment-user-thumbnail
2023년 7월 29일

좋은 글 감사합니다.

답글 달기