Wecode-最初のプロジェクト後期-Trendi
위코드 부트 캠프
最初のプロジェクトが終わりました.Backend
で関連APIに参加し実現した.初めてなので、これまで勉強してきたPython
とDjango
のORMに対して実習のコンセプトです.思ったほど簡単ではありません.思ったより開発時間が短く、モデリングに時間がかかりました.Wecode第一項目:Brandi Service PartClone
https://www.brandi.co.kr/
1.モデリング
まずモデリングをします.バックエンド開発者として、RDBMSを理解する必要があります.開発期間を定め、
어디까지 기능 구현을 할 것 인가
を追及し、その放棄すべきものを果敢に放棄した.また、進行に伴ってコラムを修正したり追加したりすることがよくあります.これまで作成してきた表関係図を見てDMLだけを書きました.最初からデータベースモデリングを考慮する機会はあまりありません.だからおもしろい
鉄塔の名称や属性、長さ、制限事項を特定する際には、苦悩と根拠が必要である.また、政策は基礎となるべきだ.
これらの部分を短時間で特定するのは難しい.でもとにかく
Agile-Scrum
どこかで会ったことがあります...프로그래밍하고 싶어? 그럼 무조껀 만들고 배포해봐!
この言葉はある程度正しいので、これらを全部計算すれば、数ヶ月かかっても配置のレベルに達することはできません.どうせやってみよう.最終版は以下の通り.これから見るときは恥ずかしがってほしいこれはモデリングが成長したことを意味します...
2.初期データを作成します。
API
を実装し、テストを行うには、データが必要です.最も困難な部分は著作権のないイメージを探すことだ.
データの担当者この仕事をしているうちに、ORMの便利さと速さを感じました.ORMは便利すぎるのではないかと思うので、初期の大量のデータを作るのにあまり時間がかかりませんでした.
アクセルを用いてデータをcsvにし,データを表に読み込む操作を考えると,どれだけの攻守が必要かを考える.気をつけないとまた修正します...
ORM
万歳.一般データの入力は問題なく、注文情報の入力に手間がかかります.フィルタリングおよびソート機能を実現するために、注文情報テーブルの
생성 날짜
または갱신 날짜
の値はランダムでなければならない.created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
Objects.save()
のため、日付値を更新できないという問題が発生しました.長い間探していたが...editable=True
属性を与えなければsave()メソッドを使用して更新できません.editable=True
をプロパティに追加したくない場合は、update()メソッドで変更できます.やはりそう簡単にはいかない.このような試行錯誤はいつでも歓迎される.
実際、データ作成の過程でORMやPythonの様々な方法を学びました.
つまり、10000個を超える基本データの受注情報を迅速に生成することができる.10000枚のレコードを生成する必要はありません...フロンテンドで非同期の練習もできます
insert_data.py, delete_data.py
上に2つのファイルを添付したいのですが、添付の機能がないようなので、コードの大まかな流れしか使えません...
insert_data.py
import csv
import os
import django
import random
from django.db.models import Max, Q
from datetime import date, datetime, timedelta
from django.utils import timezone
import pytz
# unset constraint =======================================================
# SET FOREIGN_KEY_CHECKS = 0;
# SET FOREIGN_KEY_CHECKS = 1;
# reset AI =======================================================
# ALTER TABLE categories AUTO_INCREMENT = 1
# ALTER TABLE sub_categories AUTO_INCREMENT = 1
# ALTER TABLE orders AUTO_INCREMENT = 1;
# ALTER TABLE order_lists AUTO_INCREMENT = 1;
# ALTER TABLE deliveries AUTO_INCREMENT = 0;
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "trendi.settings")
django.setup()
from user.models import *
from product.models import *
from order.models import *
from review.models import *
from favor.models import *
def insert_category():
CSV_PATH_1 = "csv/1_categories.csv"
with open(CSV_PATH_1, newline='', encoding='utf-8-sig') as csv_file:
data_reader = csv.reader(csv_file)
next(data_reader, None)
for row in data_reader:
name = row[1]
Category.objects.create(
name=name
)
print("=================================================================================")
print("categories 데이터가 정상적으로 추가되었습니다.")
#...
#...
#...
#...
def insert_reviews():
content = [
"만족합니다. 별 다섯개 드립니다. 짱짱!!",
"가격 대비 입기 좋아요. 그런데 한 번 빨면 옷감이 조금 상해요..ㅠㅠ",
"넘 마음에들고 이뻐요..! 슬림이라서 더 얇아보이는 효과가 있기도하고 ??",
"재질도 부드럽고 핏도 예쁘고 데일리로 입기 좋아요!ㅎㅎ",
"만족하지만 배송이 조금 느렸습니다...",
"완전히 핏되진 않은데 이가격에 두개면 개이득입니다.. ! ^^",
"생각한거만큼 안예뻤어요 ㅎㅎㅎ 역시 믿고보는 트랜디입니다:) 마지막엔 품질이 다른거보다 살짝쿵살짝 없네 했는데 진짜진짜 너무 이뽀!! 파세요",
"까끌까끌한 필링이여서 싫어요 종종 안을 것 같아요..",
"리뷰가 적은 이유를 알겠어요! 다 안 예쁘다 해서 품질고민을 오래하다 초록색 샀는데 너무 싫어요!!!!",
"같이 입어도 색이 따뜻해보여서 좋아요 !! 상의는 항상 밑이 어정쩡해서 행복한데 상의는 딱 여리여자해서 치마나 치킨마요 다 잘어울리는 "
"상의이에요! 다른 색상도 너무 사고싶당...",
"지하상가에서 산거같아도 입으면 진짜 조낸 이쁘고 남들이 다 탐나할거같은 그런그런그런 너무너무너무 이뻐용 히히",
"진짜 이런 상품이 있다니 삼만구천팔백원에 팔아도 될듯 >_+<",
"단추 끼우는 구멍이 너무 작아ㅕ ㅠ공장에서 공정이 잘 안된듯,. 다시 공정하게 해주세여ㅑ",
"완전 이뻐요~~ 인스타 들어가서 영상보시면 더 이뽀용 >_< 털 빠짐 제로!!",
]
for row in range(1, 10000):
product = Product.objects.get(id=random.randint(1, 253))
review = Review(
content = content[random.randint(0, len(content)-1)],
star = random.randint(2, 5),
user = User.objects.get(id=random.randint(1, 50)),
product = product
)
review.save()
print("=================================================================================")
print("reviews 데이터가 정상적으로 추가되었습니다.")
def insert_basic_order():
# 주문 기본 데이터
# 유저 1에서 10이 구매하는 새로운 1개의 오더가 만들어짐
ordered = Order.objects.create(
order_number=str(1000),
delivery_fee = 0,
user=User.objects.get(id=random.randint(1, 10)),
orderstatus=OrderStatus.objects.get(id=random.randint(1, 5)),
)
order_list = OrderList(
quantity=random.randint(1, 2),
order=ordered,
product=Product.objects.get(id=random.randint(1, 10)),
size = Size.objects.get(id=random.randint(1, len(Size.objects.all()))),
color = Color.objects.get(id=random.randint(1, len(Color.objects.all())))
)
order_list.save()
def random_date(start, end):
delta = end - start
int_delta = (delta.days * 24 * 60 * 60) + delta.seconds
random_second = random.randrange(int_delta)
return start + timedelta(seconds=random_second)
def insert_order_lists():
print("insert_order_lists=======================================")
d1 = datetime.strptime('11/13/2020 10:30 AM', '%m/%d/%Y %I:%M %p')
d2 = datetime.strptime('11/26/2020 04:50 AM', '%m/%d/%Y %I:%M %p')
for i in range(1, 10000):
# order_id +1 시키기
new_order_id = Order.objects.all().aggregate(Max('id'))['id__max'] + 1
# order_number +1 시키기 (현재 str 값)
order_number = Order.objects.all().aggregate(Max('order_number'))['order_number__max']
new_order_number = str(int(order_number) + 1)
# 2020/10/20 ~ 2020/11/21 까지의 랜덤 날짜
ordered_date = random_date(d1, d2)
ordered = Order.objects.create(
order_number = new_order_number,
user = User.objects.get(id=random.randint(1, 10)),
orderstatus = OrderStatus.objects.get(id=random.randint(1, 5)),
)
product_ids = []
for i in range(0, 3):
product_ids.append((random.randint(1, 253)))
product_ids_set = list(set(product_ids))
for i in range(len(product_ids_set)):
OrderList.objects.create(
quantity = random.randint(1, 3),
order = ordered,
product = Product.objects.get(id=product_ids_set[i]),
)
Order.objects.filter(order_number=new_order_number).update(updated_at=ordered_date)
order = Order.objects.get(order_number=new_order_number)
OrderList.objects.filter(order=order).update(updated_at=ordered_date)
print("=================================================================================")
print("order 데이터 10000개가 정상적으로 추가되었습니다.")
print("order_list 10000개가 데이터가 정상적으로 추가되었습니다.")
print("만들어진 데이터는 order_status 1~5 까지의 데이터 입니다.")
print("=================================================================================")
delete_data.py# print("=================전체 데이터 삭제 시작=================")
# def delete_all_data():
# User.objects.all().delete()
# Seller.objects.all().delete()
# Color.objects.all().delete()
# Size.objects.all().delete()
# Sale.objects.all().delete()
# Delivery.objects.all().delete()
# OrderStatus.objects.all().delete()
# Order.objects.all().delete()
# OrderList.objects.all().delete()
# Destination.objects.all().delete()
# Review.objects.all().delete()
# ProductDetailImage.objects.all().delete()
# SubCategory.objects.all().delete()
# Category.objects.all().delete()
# Product.objects.all().delete()
# print("=================전체 데이터 삭제 완료=================")
#
# # alter table users auto_increment = 1;
# # alter table sellers auto_increment = 1;
# # alter table colors auto_increment = 1;
# # alter table sizes auto_increment = 1;
# # alter table sale_ratios auto_increment = 1;
# # alter table deliveries auto_increment = 1;
# # alter table status auto_increment = 1;
# # alter table orders auto_increment = 1;
# # alter table order_lists auto_increment = 1;
# # alter table destinations auto_increment = 1;
# # alter table reviews auto_increment = 1;
# # alter table product_detail_urls auto_increment = 1;
# # alter table sub_categories auto_increment = 1;
# # alter table categories auto_increment = 1;
# # alter table products auto_increment = 1;
最初は確かにcsvの仕事をしたことがありますね.ファイルI/Oを勉強した以上、もったいないことではありません.その後DDL
truncate
が用いられ、初期化シーケンスを必要とせずに自動的に1から入ることができるようになった.3.商品リストの配布(フィルタリング、注文)
まず要求事項を整理した.
1.ホームページボタンをクリックした場合(全商品基準)
日间の贩売量の顺番によって、ただ1日の出荷の商品だけを出して、その中の割引の商品の、分类の何回の商品のリスト
3.毎日配送ボタンをクリックした場合(全商品基準)
毎日配送される商品を人気順に商品リストを配布します.
△人気順は販売量を基準にしています.人気をどう決めるかという政策を立てていないので、勝手に決めました.
または、
최신 순
で商品リストを送信します.または、
가격 순
で商品リストを送信します.以上の規格に加えて
세일 중
の商品を濾過することができる.上記の規格を加えると、商品の
카테고리
を濾過することができます.人気順に1日配送された商品を抽出し、価格順にソートして割引商品のみを表示し、カテゴリNの商品のみを表示します.
4.ショッピングモール/スーパーボタンをクリックした場合(全商品中)
brandi pickがtrueの商品リストを表示します.
上記仕様と異なり、
카테고리 별
、서브 카테고리별
の商品を表示することができる.カテゴリまたはサブカテゴリ別に商品を表示する場合は、次の条件を追加できます.
판매량순 / 최신 순 / 리뷰 많은 순 / 가격 순
さらに
하루 배송 / 세일 중
人分の商品を濾過できる.カテゴリ番号2、コメントの多い順、1日配送、割引商品を表示します.
以上の条件はすべてFrontend側からGETメソッドの
Query String
値に遷移する.1つのビューを使用してAPIを実装することは最も困難であり、
Q
オブジェクトを使用してほとんどの困難を解決した.コードは以下の通りです.
def get(self, request):
try:
search = request.GET.get('search')
is_pick = request.GET.get('trendi-pick')
ranking_by = request.GET.get('ranking')
is_sale = request.GET.get('sale')
is_delivery = request.GET.get('delivery')
category = request.GET.get('category')
sub_category = request.GET.get('sub-category')
ordering = request.GET.get('ordering')
products = Product.objects.\
filter(orderlist__order__orderstatus_id=5).\
select_related(
'seller',
'delivery',
'sale',
'category',
'sub_category'
).\
prefetch_related(
'orderlist_set',
'review_set'
).\
annotate(sum=Sum('orderlist__quantity')).\
annotate(review_count=Subquery(
Review.objects.filter(product=OuterRef('pk')).values('product').
annotate(count=Count('pk')).values('count'))
)
q = Q()
if is_pick:
q.add(Q(trendi_pick=is_pick), Q.AND)
if is_sale:
q.add(Q(sale_id__gt=is_sale), Q.AND)
if ranking_by:
rank_filter = {'day' : 1, 'week' : 7, 'month' : 30}
date = datetime.today() - timedelta(days=rank_filter[ranking_by])
q.add(Q(orderlist__updated_at__gt=date), Q.AND)
if category:
q.add(Q(category=category), Q.AND)
if sub_category:
q.add(Q(sub_category=sub_category), Q.AND)
if is_delivery:
q.add(Q(delivery__delivery_type=is_delivery), Q.AND)
sort_type = {
'latest' : '-updated_at',
'review' : '-review_count',
'l-price' : 'price',
'h-price' : '-price',
None : '-sum'
}
if not ordering:
products = products.order_by(sort_type[ordering])
if ordering in sort_type:
products = products.order_by(sort_type[ordering])
if search:
products = Product.objects.select_related(
'seller', 'delivery', 'sale',
)
q &= Q(title__icontains = search) |\
Q(category__name__icontains = search) |\
Q(sub_category__name__icontains = search) |\
Q(seller__name__icontains = search)
product_list = [
{
'is_pick' : product.trendi_pick,
'image_url' : product.thumb_image_url,
'seller_name' : product.seller.name,
'title' : product.title,
'delivery' : product.delivery.delivery_type == 1,
'sale' : convert_sale(product.sale.sale_ratio),
'discounted_price': get_discounted_price(
product.price,
product.sale.sale_ratio
),
'price' : product.price,
'updated_date' : product.updated_at,
'product_pk' : product.pk,
} for product in products.filter(q)
]
number_of_products = products.filter(q).count()
if not product_list:
return JsonResponse({"message": "NO_RESULT"}, status=400)
return JsonResponse(
{
"number_of_products": number_of_products,
"product_list" : product_list,
}, status=200
)
except TypeError:
return JsonResponse({"message": "TYPE_ERROR"}, status=400)
except KeyError:
return JsonResponse({"message": "KEY_ERROR"}, status=400)
except Product.DoesNotExist:
return JsonResponse({"message": "NOT_EXIST_PRODUCT"}, status=400)
**raw SQLを使用する場合、mapperはほとんど使用されます.ORMは確かに便利ですこの部分を実装する際にサブクエリを使用して審査数を取得するのは、実際にはよくありません.
*データがロードされるほどSubQueryのパフォーマンスが低下するためです.groupbyやdistictを使うのは夢でも考えられません.もっと遅いです.
解決策は?別のテーブルを作ってください.
でもそれじゃもっと负けちゃう気がして….
ロック処理とか...初期に...待ってとにかく...うん.言葉では説明できない...Databaseを勉強している間によく勉強できなかった.急にそう思った.何でも言叶で说明できるのが本当の知识だ.
ポスト
ターゲットのすべての機能を達成できませんでした.
git 린이 로써
Gitを作っている時に多くの人に聞いたことがあるようです残念なことに、Agile-Scrumはうまく応用されていますか?この点.
うん.適用されないようです.何でも、初めては疎遠でした.
Reference
この問題について(Wecode-最初のプロジェクト後期-Trendi), 我々は、より多くの情報をここで見つけました https://velog.io/@pm1100tm/wecode-1차-프로젝트-후기テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol