Django

쇼핑몰 만들기 프로젝트

위니윈 2023. 3. 14. 08:32

1. shop 앱 만들기

 

2. shop > models.py

 

from django.db import models

from django.urls import reverse

# 카테고리 모델
class Category(models.Model):

    # 카테고리 이름 결정, db_index를 true로 설정하면 카테고리 정보 저장 테이블은 이 이름 열을 인덱스로 설정
    name = models.CharField(max_length=200, db_index=True)

    # SEO(search engine optimization) => 요즘은 OG(open graph)도 넣음
    meta_description = models.TextField(blank=True)

    # 카테고리와 상품 모두에 설정, 상품명을 이용해서 url 만들기
    # allow_unicode는 영문 외 모든 언어 사용 가능
    slug = models.SlugField(max_length=200, db_index=True, unique=True, allow_unicode=True)

    # 관리자 페이지에서 보여지는 객체가 단수일 때, 복수일 때 표현하는 값 결정
    class Meta:
        ordering = ['name']
        verbose_name = 'category'
        verbose_name_plural = 'categories'

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('shop:product_in_category', args=[self.slug])


# 상품 모델
class Product(models.Model):

    # 상품 모델은 foreignkey 필드를 이용해서 카테고리 모델과 관계 설정
    # 카테고리를 삭제해도 상품은 남아있어야 하니 on_delete를 set_null로 설정
    # null 값 저장 가능하기 위해 null = true로 설정
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='products')
    name = models.CharField(max_length=200, db_index=True)

    slug = models.SlugField(max_length=200, db_index=True, unique=True, allow_unicode=True)

    image = models.ImageField(upload_to='products/%Y/%m/%d',blank=True)
    description = models.TextField(blank=True)
    meta_description = models.TextField(blank=True)

    # price: 제품 가격, stock: 재고
    price = models.DecimalField(max_digits=10,decimal_places=2)
    stock = models.PositiveIntegerField()

    # available_display: 상품 노출 여부, available_order: 상품 주문 가능 여부
    available_display = models.BooleanField('Display', default=True)
    available_order = models.BooleanField('Order', default=True)

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    # index_together은 멀티 컬럼 색인 기능으로 id와 slug 필드를 묶음
    class Meta:
        ordering = ['-created']
        index_together = [['id','slug']]

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('shop:product_detail', args=[self.id, self.slug])

 

3. makemigrations와 migrate로 데이터베이스에 반영하기

 

갑자기 오류가 나서 python -m pip install Pillow를 했더니 해결

 

4. 이제 view를 만들어보자

 

from django.shortcuts import render, get_object_or_404

from .models import *

# get_object_or_404는 찾는 객체가 없을 경우 자동으로 404를 보여줌

# 카테고리 페이지
def product_in_category(request, category_slug=None):
    current_category = None
    categories = Category.objects.all()
    products = Product.objects.filter(available_display=True)

    # url로부터 category_slug를 찾아서 현재 어느 카테고리를 보여주는 것인지 판단
    # 선택한 카테고리가 없으면 전체 상품 목록 노출
    if category_slug:
        current_category = get_object_or_404(Category, slug=category_slug)
        products = products.filter(category=current_category)

    # filter 메서드는 여러번 쓰이지만, 실제 DB에는 단 한번 전달 (Lazy evalutation 사용하기 때문)

    return render(request, 'shop/list.html',
                  {'current_category': current_category, 'categories': categories, 'products': products})

# 제품 상세 뷰
# URL에서 slug 값을 읽어와서 해당 제품을 찾고 노출
def product_detail(request, id, product_slug=None):
    product = get_object_or_404(Product, id=id, slug=product_slug)
    return render(request, 'shop/detail.html', {'product': product})

 

5. URL 연결

 

- 만든 뷰를 동작시키려면 urls.py에서 URL을 연결해 줘야 함

- shop 폴더에 urls.py 생성

 

from django.urls import path
from .views import *

app_name = 'shop'

urlpatterns = [
    # 전체 제품 노출
    path('', product_in_category, name='product_all'), 

    # 카테고리 선택이 있는 경우
    path('<slug:category_slug>/', product_in_category, name='product_in_category'),

    # 제품 상세
    path('<int:id>/<product_slug>/', product_detail, name='product_detail'),
]

 

6. 템플릿 만들기

 

templates > base.html에 만들기

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %}</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

    <!-- jquery slim 지우고 minified 추가 -->
    <script
  src="https://code.jquery.com/jquery-3.3.1.min.js"
  crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>

    {% block script %}
    {% endblock %}

    {% block style %}
    {% endblock %}
</head>



<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="/">Django Shop</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>

  <div class="collapse navbar-collapse justify-content-end" id="navbarSupportedContent">
    <ul class="navbar-nav justify-content-end">
        <li class="nav-item">
            {% if user.is_authenticated %}
            <a class="nav-link" href="{% url 'account_logout' %}">Logout</a>
            {% else %}
            <a class="nav-link" href="{% url 'account_login' %}">Login</a>
            {% endif %}
        </li>
        <li class="nav-item ">
        <a class="nav-link btn btn-outline-success" href="{% url 'cart:detail' %}">Cart
            {% if cart|length > 0 %}
                ${{ cart.get_total_price }} with {{cart|length}} items
            {% else %}
                : Empty
            {% endif %}
        </a>
      </li>

    </ul>
  </div>
</nav>
<div class="container">
{% block content %}
{% endblock %}
</div>

</body>
</html>

 

 

{% extends 'base.html' %} {% block title %}Category Page{% endblock %} {% block content %}
All {% for c in categories %} {{c.name}} {% endfor %}
{% for product in products %}
Product Image
{{product.name}}

{{product.description}} ${{product.price}}

View Detail
{% endfor %}
{% endblock %}

 

{% extends 'base.html' %} {% block title %}Product Detail{% endblock %} {% block content %}

{{product.name}}

Price {{product.price}}

{{add_to_cart}} {% csrf_token %}
Description{{product.description|linebreaks}}
{% endblock %}

 

7. 서버 실행

python manage.py runserver

 

8. 관리자 페이지 등록

from django.contrib import admin

from .models import *

# 카테고리와 제품 모두 등록해서 관리하겠다
class CategoryAdmin(admin.ModelAdmin):
    list_display = ['name','slug']
    prepopulated_fields = {'slug':('name',)}

admin.site.register(Category, CategoryAdmin)

class ProductAdmin(admin.ModelAdmin):
    list_display = ['name','slug','category','price','stock','available_display','available_order','created','updated']
    list_filter = ['available_display','created','updated','category']
    prepopulated_fields = {'slug': ('name',)}
    list_editable = ['price','stock','available_display','available_order']

admin.site.register(Product, ProductAdmin)

 

9. 소셜 로그인 추가

 

- pip install django-allauth

 

'Django' 카테고리의 다른 글

Getting Started with Amazon Personalize  (0) 2023.03.20
REST API 구축  (0) 2023.03.14
쇼핑몰 만들기 프로젝트 - aws 설정  (0) 2023.03.13
장고를 시작하며  (0) 2023.03.13
python2 vs python3  (0) 2023.02.25