timeattack_0610
을 내 로컬에 생성해준다. pyCharm
또는 VS Code
를 키고 내가 생성한 timeattack_0610
를 연다.timeattack_0610
폴더를 열게 되면 아~~무 것도 없다.(넣은게 없으니까!)가상환경
세팅이다. 나는 여러 프로젝트 및 개인 연습을 진행하면서 conda에 만들어 놓은 가상환경을 쓰도록 하겠다. (과로사해도 할 말이 없다)가상환경
까지 세팅이 되었으니 아래 명령어를 terminal에 입력하면 알아서 프로젝트가 생성된다. $ django-admin startproject timeattack_0610 .
user
와 product
app을 생성할 것이다. $ django-admin startapp user
$ django-admin startapp product
timeattack_0610/settings.py
에다가 알려줘야 합니다!#### 상단 생략 ####
INSTALLED_APPS = [
'product',
'user',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
#### 하단 생략 ####
나의 편안함
을 추구하느라 CharField
를 주로 사용해 왔다. SQLD까지 딴 사람이 너무 안일했다 생각하며 반성한다. 이번 프로젝트는 다른 것을 다 떠나 model
을 짜는 연습을 더 하고 싶다 생각했기 때문에 좀 더 심혈을 기울여 궁금한게 나오면 바로 찾아보았다. 사용자의 정보
를 담을User
모델이 있어야 된다. user/models.py
from django.db import models
# Create your models here.
class User(models.Model):
class Meta:
db_table = 'user'
email = models.EmailField(max_length=100, null=False, unique=True)
password = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
자 여기서 궁금증이 생겼다. 도대체 auto_now_add
와 auto_now
는 뭔 차이냐?? add가 없다는 것으로 추론이 되지 않았다. 그래서 아래의 블로그를 참조해 답을 얻었다.
auto_now_add
: model 이 최초 저장(insert) 시에만
현재날짜(date.today())를 적용
auto_now
: model이 save 될 때마다
현재날짜(date.today())로 갱신
뜻을 보니 이해가 갔다. 계정이 언제 생성되었는지
에 대한 정보와 계정에 언제 변화가 있었는지
에 대한 정보를 제공하기 위함인 것 같다.
model을 생성했으니 admin 페이지에서 사용
할 수 있도록 등록해보자.
from django.contrib import admin
from .models import User
# Register your models here.
admin.site.register(User)
product
model과 category
model을 만들어야 한다. 그런데 이때 product
가 category
의 내용을 ForeignKey
를 통해 받아 사용해야 한다. 그러므로 나는 category
를 먼저 만들도록 하겠다. product/models.py
from django.db import models
# Create your models here.
class Category(models.Model):
class Meta:
db_table = 'category'
name = models.CharField(max_length=100)
class product(models.Model):
class Meta:
db_table = 'product'
name = models.CharField(max_length=100)
# Category 모델이 사라져도 해당 부분은 사라지면 안되니까 models.SET_NULL로 설정
# null=True : 필드의 값이 null로 저장되는 것을 허용 (기본값 : False)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
# blank=True : 필드가 폼(입력 양식)에서 빈 채로 저장되는 것을 허용 (기본값 : False)
# null=True와 blank=True를 모두 설정하면 어떤 조건으로든 빈 값 가능 (CharField와 TextField 제외)
image = models.ImageField(upload_to='product/%Y/%m/%d', blank=True)
desc = models.TextField(blank=True)
# 10 자리의 숫자 중 2자리까지의 소숫점 이하 허용
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# 쿼리셋으로 object를 불러올 때 모든 내용이 나오지 않고 name으로 나오게 해줌 (ex. <Queryset: apple macbook>)
def __str__(self):
return self.name
ForeignKey의 on_delete
종류CASCADE
: ForeignKeyField를 포함하는 모델 인스턴스(row)도 같이 삭제
PROTECT
: 해당 요소가 같이 삭제되지 않도록
ProtectedError를 발생SET_NULL
: ForeignKeyField 값을 NULL로 바꿈
(null=True
일 때만 사용)SET_DEFAULT
: ForeignKeyField 값을 default 값으로 변경
(default 값이 있을 때만 사용)SET(함수)
: ForeignKeyField 값을 SET에 설정된 함수 등에 의해 설정
DO_NOTHING
: 아무런 행동을 취하지 않음 (참조 무결성을 해칠 수 있음)ImageField
관련 자료OrderStatus
model 생성주문 진행 상태
에 대한 내용을 담는다. class OrderStatus(models.Model):
class Meta:
db_table = 'order_status'
ProductOrder
model 생성Product
model을 만들어 놓았다. 그렇다면 유저가 주문한 상품의 개수를 저장하는 해당 모델은 Product
를 ForeignKey
로 받아와 사용하면 될 것 같다. 일대다 관계
라 생각한다. 어떤 유저가 주문했는지
에 대해서도 표시해주어야 하기 때문에 아래와 같이 작성했다. class ProductOrder(models.Model):
class Meta:
db_table = 'product_order'
# null=True와 blank=True를 모두 설정하면 어떤 조건으로든 빈 값 가능 (CharField와 TextField 제외)
product = models.ForeignKey(Product, on_delete=models.SET_NULL, blank=True, null=True)
product_cnt = models.IntegerField()
user_order = models.ForeignKey('UserOrder', on_delete=models.SET_NULL, blank=True, null=True)
UserOrder
model 생성User
모델에서 사용자의 정보를 받아와야 하며, 하나의 유저에 여러 주문이 있을 수 있지만 한 주문에 여러 명의 유저가 있으면 안되기에 일대다
로 설정하였다. OrderStatus
모델에서 정보를 받아와 사용자의 주문이 어떻게 진행되고 있는지 보면 좋겠다고 생각해 추가하였다. 하나의 주문에 여러 개의 진행 상태가 있을 수 있다 생각했다. 왜냐면 실생활에서도 한 번에 주문한다 해서 꼭 다 같이 오는 것은 아니기 때문이다. 하지만 하나의 주문 상태에는 하나의 유저만이 링크되어야 한다고 생각해 일대다
로 설정했다. ProductOrder
에서 정보를 받아오도록 설정했다. UserOrder
와 ProductOrder
는 하나로 합쳐져도 좋을 것 같다 생각했다. (다음에 시도해보자!)class UserOrder(models.Model):
class Meta:
db_table = 'user_order'
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
order_status = models.ForeignKey(OrderStatus, on_delete=models.SET_NULL, null=True)
product_order = models.ForeignKey(ProductOrder, on_delete=models.SET_NULL, null=True)
delivery_address = models.CharField(max_length=1000)
order_time = models.DateTimeField()
total_price = models.DecimalField(max_digits=20, decimal_places=2)
discount = models.DecimalField(max_digits=20, decimal_places=2)
final_price = models.DecimalField(max_digits=20, decimal_places=2)
active = models.BooleanField()
product/admin.py
에 등록하도록 하겠다. from django.contrib import admin
from . import models
# Register your models here.
admin.site.register(models.Category)
admin.site.register(models.Product)
admin.site.register(models.ProductOrder)
admin.site.register(models.UserOrder)
admin.site.register(models.OrderStatus)
get_absolute_url()
을 사용할 수 있다. 해당 부분을 포함한 전체 코드는 다음과 같다. from django.db import models
from django.urls import reverse
from user.models import User
# Create your models here.
class Category(models.Model):
class Meta:
db_table = 'category'
name = models.CharField(max_length=100)
def get_absolute_url(self):
print("get_url = ", end=""), print(reverse('product:list', args=[self.name]))
return reverse('product:list', args=[self.name]) # product/list/laptop
class Product(models.Model):
class Meta:
db_table = 'product'
name = models.CharField(max_length=100)
# Category 모델이 사라져도 해당 부분은 사라지면 안되니까 models.SET_NULL로 설정
# null=True : 필드의 값이 null로 저장되는 것을 허용 (기본값 : False)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
# blank=True : 필드가 폼(입력 양식)에서 빈 채로 저장되는 것을 허용 (기본값 : False)
# null=True와 blank=True를 모두 설정하면 어떤 조건으로든 빈 값 가능 (CharField와 TextField 제외)
image = models.ImageField(upload_to='product/%Y/%m/%d', blank=True)
desc = models.TextField(blank=True)
# 10 자리의 숫자 중 2자리까지의 소숫점 이하 허용
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# 쿼리셋으로 object를 불러올 때 모든 내용이 나오지 않고 name으로 나오게 해줌 (ex. <Queryset: apple macbook>)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('product:product_detail', args=[self.id, self.name])
class OrderStatus(models.Model):
class Meta:
db_table = 'order_status'
class ProductOrder(models.Model):
class Meta:
db_table = 'product_order'
# null=True와 blank=True를 모두 설정하면 어떤 조건으로든 빈 값 가능 (CharField와 TextField 제외)
product = models.ForeignKey(Product, on_delete=models.SET_NULL, blank=True, null=True)
product_cnt = models.IntegerField()
user_order = models.ForeignKey('UserOrder', on_delete=models.SET_NULL, blank=True, null=True)
class UserOrder(models.Model):
class Meta:
db_table = 'user_order'
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
order_status = models.ForeignKey(OrderStatus, on_delete=models.SET_NULL, null=True)
product_order = models.ForeignKey(ProductOrder, on_delete=models.SET_NULL, null=True)
delivery_address = models.CharField(max_length=1000)
order_time = models.DateTimeField()
total_price = models.DecimalField(max_digits=20, decimal_places=2)
discount = models.DecimalField(max_digits=20, decimal_places=2)
final_price = models.DecimalField(max_digits=20, decimal_places=2)
active = models.BooleanField()