오늘은 db연결 및 쇼핑몰 만들기를 배웠다.

  1. pip install django

  2. django-admin startproject config .

  3. pip install pymysql

4.. settings.py

import pymysql
pymysql.install_as_MySQLdb()


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '',
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '3306'
    }
}#데이터베이스 이름 적는다, host는 엔드포인트
  1. python manage.py migrate

  2. python manage.py createsuperuser

  3. config/settings.py

# S3를 사용할 수 있는 곳
# 1. Static

AWS_ACCESS_KEY_ID = ''
AWS_SECRET_ACCESS_KEY = ''
AWS_REGION = 'ap-northeast-2'
AWS_STORAGE_BUCKET_NAME = ''

AWS_S3_CUSTOM_DOMAIN = '%s.s3.%s.amazonaws.com' % (AWS_STORAGE_BUCKET_NAME, AWS_REGION)

AWS_S3_FILE_OVERWRITE = False
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl':'max-age=86400',
}
AWS_DEFAULT_ACL = 'public-read'
AWS_LOCATION = 'static'

STATIC_URL = 'http://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
DEFAULT_FILE_STORAGE = 'config.s3media.MediaStorage'
  1. pip install boto3
  1. settings.py
'storages',
  1. pip install django-storages

  2. python manage.py collectstatic

  3. 버킷 확인하기

  4. config/settings.py

DEFAULT_FILE_STORAGE = 'config.asset_storage.MediaStorage'
  1. asset_storage.py 생성
from storages.backends.s3boto3 import S3Boto3Storage


class MediaStorage(S3Boto3Storage):
    location = 'media'
    file_overwrite = False
  1. python manage.py startapp shop
  1. settings.py에 shop 추가

  2. shop/models.py

from django.db import models

from django.urls import reverse


class Category(models.Model):
    name = models.CharField(max_length=200, db_index=True)
    meta_description = models.TextField(blank=True)  # 설명문

    slug = models.SlugField(max_length=200, db_index=True, unique=True, allow_unicode=True)  # unicode = 한글 안꺠지게

    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):
    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)  # unicode = 한글 안깨지게

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

    price = models.PositiveIntegerField()
    sale = 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']]

    def __str__(self):
        return self.name

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

  2. python manage.py makemigrations

  3. python manage.py migrate

  4. views.py

from django.shortcuts import render, get_object_or_404

from .models import *


def product_in_category(request, category_slug=None):
    current_category = None
    categories = Category.objects.all()
    products = Product.objects.filter(available_display=True)

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


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})
  1. 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'),#슬러그를 받을수도있고 안받을수도있다.


]
  1. config/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('shop.urls')),
]
  1. base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta charset="UTF-8">
    <title>DjangoShop {% block title %}{% endblock %}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
    {% block style %}
    {% endblock %}
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container">
        <a class="navbar-brand" href="/">DShop</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse justify-content-end" id="navbarNav">
          <ul class="navbar-nav justify-content-end">
            <li class="nav-item active">
              <a class="nav-link btn btn-outline-success" href="">CART
                  {% if cart|length > 0 %}
                    {{ cart.get_product_total }} with {{ cart | length }} items<!--담겨있는 갯수 출력-->
                  {% else %}
                    : 비어있음
                  {% endif %}
              </a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
    <div class="container mt-5">
        {% block content %}

        {% endblock %}
    </div>
    <footer class="container-fluid footer mt-5">
        <p>&copy; 2021 Bigdata. Powered By YOO</p>
    </footer>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>

</body>
</html>
  1. settings.py
import os

DIR
'DIRS': [os.path.join(BASE_DIR, 'templates')],
  1. list.html
{% extends 'base.html' %}
{% block title %}
    Category Page

{% endblock %}

{% block content %}
<div class="row">
    <div class="col-2">
        <div class="list-group">
            <a href="/" class="list-group-item {% if not current_category %}active{% endif %}">ALL</a>
            {% for category in categories %}
            <a href="{{category.get_absolute_url}}" class="list-group-item {% if not current_category_slug == category.slug %}active{% endif %}">{{category.name}}</a>
            {% endfor %}
        </div>
    </div>
    <div class="col">
        <div class="alert alert-info" role="alert">
            {% if current_category %}
                {{ current_category_name }}
            {% else %}
                ALL Products
            {% endif %}
        </div>
        <div class="row">
            {% for product in products %}
                <div class="col-4">
                    <div class="card">
                        {% if product.image %}
                            <img src="{{product.image.url}}" class="card-img-top" alt="{{product.name}}">
                        {% endif %}
                        <div class="card-body">
                            <h5 class="card-title">{{product.name}}</h5>
                            <p class="card-text">{{product.description}} <span class="badge bg-secondary">{{product.price}}</span></p>
                            <a href="{{product.get_absolute_url}}" class="btn btn-primary">View Detail</a>
                        </div>
                    </div>
                </div>
            {% endfor %}
        </div>
    </div>

</div>


{% endblock %}

{% endblock %}
  1. shop/admin.py
from django.contrib import admin
from .models import *


class CategoryAdmin(admin.ModelAdmin):
    list_display = ['id', 'name', 'slug']
    prepopulated_fields = {'slug':['name']}


admin.site.register(Category, CategoryAdmin)


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


admin.site.register(Product, ProductAdmin)
  1. detail.html
{% extends 'base.html' %}
{% block title %}
    Product Detail {{product.name}}

{% endblock %}

{% block content %}
<div class="row">
    <div class="col-sm-12 col-md-4">
        <img src="{{product.image.url}}" alt="{{product.name}}" width="100%">
    </div>
    <div class="col-sm-12 col-md-8">
        <h2 class="display-6">{{product.name}}</h2>
        <p style="font-size:1.2rem; color:black"><span class="badge bg-secondary">Price</span>{{product.price}}</p>
        <form action="" method="POST">
            {% csrf_token %}
            <input type="submit" class="btn btn-primary btn-sm" value="Add to Cart">
        </form>
        <p><span class="badge bg-secondary">Description</span>{{ product.description|linebreaks }}</p>
    </div>


</div>
{% endblock %}

소셜로그인

  1. pip install django-allauth

  2. config/settions.py

   [ 'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.naver',]


AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
    'allauth.account.auth_backends.AuthenticationBackend',

)

SITE_ID = 1

LOGIN_REDIRECT_URL = '/'

config/urls.py

path('accounts/',include('allauth.urls')),
  1. python manage.py migrate
  1. naver developer 사용신청

애플리케이션 등록 -> 이름입력 -> api : 네아로 -> 이름, 이메일 -> pc웹 -> http://127.0.0.1:8000/
-> http://127.0.0.1:8000/accounts/naver/login/callback/
id :
secret :
/admin
social applications -> add -> id, key 넣기 ->save

  1. base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta charset="UTF-8">
    <title>DjangoShop {% block title %}{% endblock %}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
    {% block style %}
    {% endblock %}
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container">
        <a class="navbar-brand" href="/">DShop</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse justify-content-end" id="navbarNav">
          <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="">CART
                  {% if cart|length > 0 %}
                    {{ cart.get_product_total }} with {{ cart | length }} items<!--담겨있는 갯수 출력-->
                  {% else %}
                    : 비어있음
                  {% endif %}
              </a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
    <div class="container mt-5">
        {% block content %}

        {% endblock %}
    </div>
    <footer class="container-fluid footer mt-5">
        <p>&copy; 2021 Bigdata. Powered By YOO</p>
    </footer>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>

</body>
</html>
  1. python manage.py collectstatic
  1. python manage.py startapp cart
  1. settings.py에 앱 등록
settings.py
CART_ID = 'cart_in_session'
  1. 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)
        if not 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):
        product_ids = self.cart.keys()  # 카트에있는 각각 키값
        products = Product.objects.filter(id__in=product_ids)

        for product in products:  # 있는것만큼 카트에 담아라
            self.cart[str(product.id)]['product'] = product

            # 데이터 베이스에 있는 목록중 카트에 담기로한 제품 목록을 하나씩 가지고 와서 로컬에 담고 그 담은걸 그냥 못쓰니까 세션에 담기(카트에 담기)

        for item in self.cart.values():
            item['price'] = Decimal(item['price'])  # 객체형태로 만드는것
            item['total_price'] = item['price'] * item['quantity']

            yield item

    def add(self, product, quantity=1, is_update=False):  # 1은 default값이다. 값이 없으면 한개이다.
        product_id = str(product.id)
        if product_id not in self.cart:  # pro아이디가 없으면
            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 not 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())
  1. 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)
  1. cart/views.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


@require_POST  # 이게 있으면 포스트 있으면 아래가 실행이된다.
def add(request, product_id):  # id 호출받으면 카트에담는다
    cart = Cart(request)
    product = get_object_or_404(Product, id=product_id)

    form = AddProductForm(request.POST)
    if form.is_valid():  # 값이 있으면
        cd = form.cleanded_data  # 한번 비우고
        cart.add(product=product, quantity=cd['quantity'], is_update=cd['is_update'])  # 추가한다(cart.py에 있는 내용)

    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={  # add를 실행할때  받을값
            'quantity': product['quantity'], 'is_update': True
        })

    return render(request, 'cart/detail.html', {'cart':cart})

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/<int:product_id>', add, name='product_remove'),


]
  1. setting
CARD_ID = 'cart in session'
  1. cart/detail.html
{% extends 'base.html' %}
{% block title %}
    Category Page

{% 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">UmitPrice</th>
                <th scope="col">Price</th>
            </tr>
        </thead>

        <tbody>
        {% for item in cart %}
        {% with product=item.product %}
         <tr>
             <td scope="row">
                    <a href="{{ product.get_absolute_url }}">
                        <img src="{{ product.image.url }}" class="img-thumnail">
                    </a>
                </td>
                <td>{{ product.name }}</td>
                <td>
                    <form action="{% url 'cart:product_add' product.id%}" method="post">
                        {% csrf_token %}
                        {{ item.quantity_form.quantity }}
                        {{ item.quantity_form.is_update }}
                        <input type="submit" class="btn btn-primary" value="Update">
                    </form>
                </td>
                <td>
                    <a href="{% url 'cart:product_remove' product.id %}">Remove</a>
                </td>
                <td>{{ item.price }}</td>
                <td>{{ item.total_price }}</td>
            </tr>
        {% endwith %}
        {% endfor %}
        </tbody>
    </table>

{% endblock %}
  1. shop.view.py
from django.shortcuts import render, get_object_or_404

from .models import *
from cart.forms import AddProductForm

def product_in_category(request, category_slug=None):
    current_category = None
    categories = Category.objects.all()
    products = Product.objects.filter(available_display=True)

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


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})#product로 넘겨줌
  1. shop/detail.html
{% extends 'base.html' %}
{% block title %}
    Product Detail {{product.name}}

{% endblock %}

{% block content %}
<div class="row">
    <div class="col-sm-12 col-md-4">
        <img src="{{product.image.url}}" alt="{{product.name}}" width="100%">
    </div>
    <div class="col-sm-12 col-md-8">
        <h2 class="display-6">{{product.name}}</h2>
        <p style="font-size:1.2rem; color:black"><span class="badge bg-secondary">Price</span>{{product.price}}</p>
        <form action="{% url 'cart:product_add' product.id %}" method="POST">
            {% csrf_token %}
            <input type="submit" class="btn btn-primary btn-sm" value="Add to Cart">
        </form>
        <p><span class="badge bg-secondary">Description</span>{{ product.description|linebreaks }}</p>
    </div>


</div>
{% endblock %}
profile
smilegate megaport infra

0개의 댓글