오늘은 db연결 및 쇼핑몰 만들기를 배웠다.
pip install django
django-admin startproject config .
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는 엔드포인트
python manage.py migrate
python manage.py createsuperuser
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'
'storages',
pip install django-storages
python manage.py collectstatic
버킷 확인하기
config/settings.py
DEFAULT_FILE_STORAGE = 'config.asset_storage.MediaStorage'
from storages.backends.s3boto3 import S3Boto3Storage
class MediaStorage(S3Boto3Storage):
location = 'media'
file_overwrite = False
settings.py에 shop 추가
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])
pip install pillow
python manage.py makemigrations
python manage.py migrate
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})
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'),#슬러그를 받을수도있고 안받을수도있다.
]
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('shop.urls')),
]
<!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>© 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>
import os
DIR
'DIRS': [os.path.join(BASE_DIR, 'templates')],
{% 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 %}
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)
{% 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 %}
pip install django-allauth
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')),
애플리케이션 등록 -> 이름입력 -> 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
<!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>© 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>
settings.py
CART_ID = 'cart_in_session'
(세션방식으로 호출)
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())
from django import forms
class AddProductForm(forms.Form):#제품 추가할때 갯수를 넣게한다.
quantity = forms.IntegerField()
is_update = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput)
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'),
]
CARD_ID = 'cart in session'
{% 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 %}
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로 넘겨줌
{% 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 %}