1. 상점 앱 만들기
2. 쇼핑 > 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를 사용하여 데이터베이스에 적용 및 마이그레이션
4. 이제 보기를 만들어 보겠습니다.
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. 템플릿 만들기
템플릿 > 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="http://win2dvp21.">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>
{% 확장된 ‘base.html’ %} {% 블록 제목 %}카테고리 페이지{% endblock %} {% 블록 콘텐츠 %}
모든 {% 카테고리 %} {{c.name}} {% endfor %}
{% 끝 블록 %}
{% 확장된 ‘base.html’ %} {% 블록 제목 %}제품 세부정보{% endblock %} {% 블록 콘텐츠 %}
{{상품명}}
가격 {{제품 가격}}
설명{{product.description|줄 바꿈}}
{% 끝 블록 %}
7. 서버 실행
python manage.py 런서버
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 설치 django-allauth