Online Shop_2

박다현·2023년 7월 16일
0

likelion

목록 보기
13/23
post-thumbnail

01 shop App 생성


STEP 1 shop app 생성

python manage.py startapp shop

STEP 2 settings.py INSTALLED_APP 에 shop 등록

INSTALLED_APPS = [
	'shop',
]


02 shop Model


STEP 1 shop app 에 model 등록

from django.db import models
from django.urls import reverse

# Create your models here.

# 카테고리 모델
class Category(models.Model):
    name = models.CharField(max_length=30, db_index=True)
    meta_description = models.TextField(blank=True)

    slug = models.SlugField(max_length=200, db_index=True, allow_unicode=True) // 상품명 등을 이용해서 url을 만드는 방식

    class Meta: // 관리자 페이지에서 보여질 객체의 단복수 유무에 따라 나눔
        ordering=['name']
        verbose_name = 'category'
        verbose_name_plural = 'categories'

    def __str__(self):
        return self.name

    def get_absolute_url(self): // 원하는 모델을 찾을 수 있는 url
        return reverse('shop:product_in_category', args=[str(self.slug)])    
# 상품 모델
class Product(models.Model): // 카테고리와 외래키 관계 ( 카테고리를 삭제해도 상품은 삭제되지 않도록 함 )
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='products')
    name = models.CharField(max_length=100, db_index=True)
    slug = models.SlugField(max_length=100, db_index=True, unique=True, allow_unicode=True)

    image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True)
    baeminImage = models.ImageField(upload_to='productsQR/%Y/%m/%d', blank=True)
    description = models.TextField(blank=True)
    meta_description = models.TextField(blank=True)
    url = models.URLField(default='정보없음')

    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.PositiveIntegerField()

	## 상품 홍보를 목적으로 재고가 없는 상품을 노출하는 경우도 있으므로 변수를 나눠서 관리
    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)

    class Meta:
        ordering = ['-created']
        index_together = [['id', 'slug']] // id + slug 묶어서 탐색이 가능하도록 설정

    def __str__(self):
        return self.name

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

STEP 2 migration DB에 반영

python manage.py makemigrations shop
python manage.py migrate shop


03 shop view / URL


STEP 1 카테고리 페이지 뷰 작성

from django.shortcuts import render, get_object_or_404

from .models import*
# Create your views here.
def product_in_category(request, category_slug=None):
    current_category = None
    categories = Category.objects.all() // 관리자 페이지에서 등록된 카테고리 객체들을 categories 변수에 저장
    products = Product.objects.filter(available_display=True) // 관리자 페이지에서 등록된 상품 객체들 중 노출 여부가 가능한 객체만 products 변수에 저장

    if category_slug: // category_slug를 찾아 현재 어느 카테고리를 보여주는지 판단
        current_category = get_object_or_404(Category, slug = category_slug)
        products = products.filter(category =current_category)

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

STEP 2 상품 상세 페이지 뷰 작성

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} )
    

URL로부터 slug 값 읽어오고 해당 상품을 찾아 그 상품을 노출하는 방식

STEP 3 shop URL 작성

// shop/urls.py

from django.urls import path
from .views import *

app_name='shop'

urlpatterns=[
    path('',product_in_category, name='product_all'),
    path('<category_slug>/',product_in_category, name='product_in_category'),
    path('<int:id>/<product_slug>/',product_detail,name='product_detail'),

]
// config/urls.py 의 urlpatterns에 적어주는 것

path('',include('shop.urls')),


04 shop Template


중복되는 템플릿이 있을 때 기준이 되는 템플릿을 만들고 이를 확장하는 형식

ex ) 어느 페이지를 가던 메뉴바가 보이므로 기준 템플릿에서 이를 미리 작성할 것 

STEP 1 프로젝트 루트에 templates 폴더 생성 > base.html 생성

부트스트랩 사용 > slim 버전 말고 minified 버전 사용 
: slim 버전은 ajax 기능이 없어서 쇼핑몰에 부적합
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>onlineShop</title>
    <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
    <link rel="icon" href="/favicon.ico" type="image/x-icon">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>
    <!--    jQuery minified 버전-->
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"
            integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>

    {% block script %}
    {% endblock %}

    {% block style %}
    {% endblock %}

</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <a class="navbar-brand" href="/"></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">
            {% if user.is_authenticated %}
            <li class="nav-item">
                <a class="nav-link btn btn-outline-info" href="{% url 'account_logout' %}">로그아웃</a>
                {% else %}
                <a class="nav-link btn btn-outline-info" href="{% url 'account_login' %}">로그인</a>
                {% endif %}
            </li>
            &nbsp;&nbsp;
            <li class="nav-item">
                <a class="nav-link btn btn-outline-info" href="">장바구니
                    {% if cart|length > 0 %}
                        <span class="badge badge-pill badge-primary">{{cart|length}}</span>
                    {% else %}
                        : 비어있음
                    {% endif %}
                </a>
            </li>
        </ul>
        &nbsp;&nbsp;
        <button class="btn btn-outline-info my-2 my-sm-0" type="submit">검색</button>
    </div>
</nav>
<div class="container">
    {% block content %}
    {% endblock %}
</div>

</body>
</html>

STEP 2 config/settings.py 의 TEMPLATES 부분 수정

'DIRS': [os.path.join(BASE_DIR,'templates'),

// 템플릿을 확장할 때 base.html을 찾을 수 있도록 미리 경로 설정

STEP 3 shop/templates/shop/list.html 생성

{% extends 'base.html' %} // base.html 확장
{% block title %}Category Page{% endblock %}
{% block content %}
<br>
<div class="row">
    <div class="col-2"> <!--col-2 : 2조각-->
        <div class="list-group">
            <a href="/" class="list-group-item list-group-item-action list-group-item-secondary
{% if not current_category%}active{% endif %}">All</a>
            {%for c in categories %}
            <a href="{{c.get_absolute_url}}" class="list-group-item list-group-item-action list-group-item-secondary
{% if current_category.slug == c.slug %}active{% endif %}">
                {{c.name}}</a>
            {% endfor %}
        </div>
    </div>
    <div class="col">
        <div class="alert alert-danger" role="alert">
            {% if current_category %}{{current_category.name}}를/을 판매하고 있는 스토어입니다.
            {% else %} 모든 스토어 {% endif %}
        </div>
        <div class="row">
            {% for product in products %}
            <div class="col-sm-4">
                <div class="card">
                    <img class="card-img-top" src="{{product.image.url}}" alt="Product image" width="466px" height="270">
                    <div class="card-body" style="text-align:center">
                        <h5 class="card-title">{{product.name}}</h5>
                        {% load humanize %} 
                        <img class="card-img" src="{{product.baeminImage.url}}" alt="QRcode image" style="width:70px; height:70px; text-align:center;"><p/>
                            <h4 class="badge badge-light">
&#8361;{{product.price | floatformat:'0' | intcomma}}</h4><p/>
                        <a href="{{product.get_absolute_url}}" class="btn btn-outline-info">View Detail</a>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</div>
{% endblock %}

STEP 4 config/settings.py에 app 등록

{% load humanize %} // humanize에서 제공하는 필터 사용

INSTALLED_APPS =[
	'shop',
    'django.contrib.humanize',
]

STEP 5 shop/templates/shop/detail.html 생성

{% extends 'base.html' %}
{% block title %}Product Detail{% endblock %}
{% block content %}
<br>
<div class="container">
    <div class="row">
        <div class="col-4">
            <img src="{{product.image.url}}" width="300px" height="300px">
        </div>
        <div class="col">
            <h2 class="display-6">{{product.name}}</h2>
            <p><span class="badge badge-light">가격</span>
                {% load humanize %}
                &#8361;{{product.price | floatformat:'0' | intcomma}}</p>
            <form action="{% url 'cart:product_add' product.id %}" method="post">
                {{add_to_cart}}
                {% csrf_token %}
                <input type="submit" class="btn btn-outline-info btn-sm" value="장바구니에 담기">
            </form>
            <br>
            <h5><span class="badge badge-light"><a href="{{product.url}}">홈페이지 바로가기</a></span></h5>
            <br>
            <h5><span class="badge badge-secondary">설명</span>{{product.description|linebreaks}}</h5>
        </div>
    </div>
    <br><br>
            {% load disqus_tags %}
            {% disqus_show_comments %}
</div>
{% endblock %}

STEP 6 shop/admin.py

# category option

from django.contrib import admin
from .models import *


# Register your models here.
class CategoryAdmin(admin.ModelAdmin):
    list_display=['name','slug']
    prepopulated_fields = {'slug':('name',)} // slug 필드는 name 필드 값에 따라 자동으로 설정

admin.site.register(Category,CategoryAdmin)
# shop app

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)

- prepopulated_fields : slug 필드는 name 필드 값에 따라 자동으로 설정됨
- list_editable : 목록에서도 주요 값들을 바로바로 변경할 수 있게 설정

STEP 7 관리자 페이지에서 상품 등록

python manage.py runserver


05 Login


STEP 1 django_allauth 다운로드

// 회원가입을 편리하게 해주는 장고의 기능
소셜 로그인 > 기존에 있는 유명한 앱을 다운로드 해서 사용

pip install django_allauth

STEP 2 INSTALLED_APPS 에 등록

'django.contrib.sites', // 사이트 설정을 위한 것
'allauth', // allauth와 관련해 필요한 앱 등록
'allauth.account', // 가입한 계정 관리
'allauth.socialaccount', // 소셜 계정으로 가입한 계정 관리
'allauth.socialaccount.providers.naver',

STEP 3 config/settings.py 에 설정 추가

AUTHENTICATION_BAKENDS = (
    'django.contrib.auth.backends.ModelBackend', // 관리자 페이지 등에 사용자명으로 로그인 하기 위해 장고 기본 ModelBackend 추가
    'allauth.account.auth_backends.AuthenticationBackend', // alluath 방식 추가하기 위해 AuthenticationBackend 추가
)

SITE_ID = 1 // 여러 사이트를 운영할 때 각각의 사이트의 ID
LOGIN_REDIRECT_URL = '/' // 로그인 후 이동할 페이지 = 메인 페이지

STEP 4 config/urls.py에 경로 추가

path('accounts/',include('allauth.urls')),

STEP 5 allauth DB에 반영

python manage.py migrate


06 API Download


STEP 1 애플리케이션 등록

STEP 2 django-admin에 client ID / Secret 등록

STEP 2 INSTALLED_APPS 에 등록

'allauth.socialaccount.providers.naver',

Kakao login

STEP 1 내 애플리케이션 > 애플리케이션 추가하기

STEP 2 엡 설정 > 플랫폼

STEP 3 제품 설정 > 카카오 로그인

STEP 4 제품 설정 > 카카오 로그인 > 동의항목

STEP 5 앱 키 발급

STEP 2 django-admin에 REST API > client ID / Admin > Secret 등록

STEP 5 INSTALLED_APPS 에 등록

'allauth.socialaccount.providers.kakao',

두 개의 social accounts가 정상적으로 생성된 것

result ( 로그인 되어있는 상태 )



기존에 학습 해왔던 방식을 토대로 틀을 작성하되 로그인 기능까지 넣어 구현하는 것을 배우고 익히는 것에 집중하여 스터디 진행


0개의 댓글