-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* #8 feat: products create * test product create api * products/create/ 재수정 * remove migrations/0002 file * product/migrations 재추가 * #8 style : 주석 정리
- Loading branch information
Showing
7 changed files
with
256 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -156,4 +156,7 @@ cython_debug/ | |
secrets.json | ||
media | ||
.env | ||
.Dockerfile.swp | ||
.Dockerfile.swp | ||
products/photo/ | ||
users/profile/ | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
products/migrations/0002_alter_productkeyword_product_and_more.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Generated by Django 4.0 on 2024-01-03 12:43 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('products', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='productkeyword', | ||
name='product', | ||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='keywords', to='products.product'), | ||
), | ||
migrations.AlterField( | ||
model_name='productphoto', | ||
name='product', | ||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_photos', to='products.product'), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import io | ||
import time | ||
import uuid | ||
|
||
|
||
from django.shortcuts import get_object_or_404 | ||
from django.db import transaction | ||
from django.core.files.images import ImageFile | ||
from django.core.files.uploadedfile import InMemoryUploadedFile | ||
from django.conf import settings | ||
|
||
from products.models import Category, Product, ProductKeyword, ProductPhoto | ||
from users.models import User | ||
#from core.exceptions import ApplicationError | ||
|
||
class ProductCoordinatorService: | ||
def __init__(self, user:User): | ||
self.user=user | ||
#pass | ||
@transaction.atomic | ||
def create(self, category: str, name : str,keywords : list[str],basic_price : str,option : str, | ||
product_photos : list[str],info : str,notice : str,period : str,transaction_direct : bool, | ||
transaction_package : bool,refund : str, | ||
) -> Product: | ||
product_service=ProductService() | ||
|
||
product= product_service.create( | ||
reformer=self.user, | ||
category=category, | ||
name=name, | ||
basic_price=basic_price, | ||
option=option, | ||
info=info, | ||
notice=notice, | ||
period=period, | ||
transaction_direct=transaction_direct, | ||
transaction_package=transaction_package, | ||
refund=refund, | ||
) | ||
|
||
if product is not None: | ||
ProductPhotoService.process_photos(product=product, product_photos=product_photos ) | ||
ProductKeywordService.process_keywords(product=product, keywords=keywords) | ||
return product | ||
else: | ||
raise ValueError('Product 생성 실패') | ||
|
||
class ProductService: | ||
def __init__(self): | ||
pass | ||
|
||
@staticmethod | ||
def create(category: str,name : str,basic_price : str,option : str,info : str,notice : str, | ||
period : str,transaction_direct : bool,transaction_package : bool,refund : str, reformer : User): | ||
category = get_object_or_404(Category, id=category) | ||
|
||
product = Product( | ||
name = name, | ||
category = category, | ||
basic_price = basic_price, | ||
option = option, | ||
info = info, | ||
notice = notice, | ||
period = period, | ||
transaction_direct = transaction_direct, | ||
transaction_package = transaction_package, | ||
refund = refund, | ||
reformer = reformer, | ||
) | ||
|
||
product.full_clean() | ||
product.save() | ||
|
||
return product | ||
|
||
|
||
class ProductPhotoService: | ||
def __init__(self): | ||
pass | ||
|
||
@staticmethod | ||
def create(image:InMemoryUploadedFile, product:Product): | ||
ext = image.name.split(".")[-1] | ||
file_path = '{}.{}'.format(str(time.time())+str(uuid.uuid4().hex),ext) | ||
image = ImageFile(io.BytesIO(image.read()),name=file_path) | ||
product_photo = ProductPhoto(image=image, product=product) | ||
|
||
product_photo.full_clean() | ||
product_photo.save() | ||
|
||
return settings.MEDIA_URL + product_photo.image.name | ||
|
||
@staticmethod | ||
def process_photos(product: Product, product_photos: list[str]): | ||
for product_photo in product_photos: | ||
op, photo_url = product_photo.split(',') | ||
|
||
photo_path = photo_url.replace(settings.MEDIA_ROOT,'').lstrip('/') | ||
|
||
try: | ||
product_photo, created = ProductPhoto.objects.get_or_create(image=photo_path) | ||
except Exception as e: | ||
print(f"Error in process_photos: {e}") | ||
product_photo = None | ||
|
||
if op == 'add': | ||
product_photo.product = product | ||
product_photo.full_clean() | ||
product_photo.save() | ||
elif op == 'remove': | ||
product_photo.delete() | ||
|
||
|
||
|
||
class ProductKeywordService: | ||
def __init__(self): | ||
pass | ||
|
||
@staticmethod | ||
def process_keywords(product: Product, keywords: list[str]): | ||
print(keywords) | ||
for keyword_str in keywords: | ||
keyword_list= keyword_str.split(',') | ||
print(keyword_list) | ||
|
||
for k in keyword_list: | ||
ex_keyword = ProductKeyword.objects.filter(product=product,name=k) | ||
|
||
if not ex_keyword.exists(): | ||
new = ProductKeyword(product=product,name=k) | ||
new.full_clean() | ||
new.save() | ||
print(f"Keyword : '{k}' 추가") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from django.urls import path | ||
from .views import * | ||
|
||
app_name = "products" | ||
|
||
urlpatterns = [ | ||
path('create/',ProductCreateApi.as_view(),name='product_create'), | ||
path('photos/create/',ProductPhotoCreateApi.as_view(), name='product_photo_create'), | ||
|
||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,83 @@ | ||
from django.shortcuts import render | ||
|
||
# Create your views here. | ||
from rest_framework.views import APIView | ||
from rest_framework.permissions import AllowAny, IsAuthenticated | ||
from rest_framework import serializers, status | ||
from rest_framework.response import Response | ||
|
||
from users.models import User | ||
|
||
from .models import Category, Product | ||
from .services import ProductCoordinatorService, ProductPhotoService, ProductKeywordService, ProductService | ||
|
||
|
||
class ProductCreateApi(APIView): | ||
permission_classes=(AllowAny,) | ||
|
||
class ProductCreateInputSerializer(serializers.Serializer): | ||
name = serializers.CharField() | ||
category = serializers.CharField() | ||
keywords = serializers.ListField(required=False) | ||
basic_price = serializers.CharField() | ||
option = serializers.CharField() | ||
product_photos = serializers.ListField(required=False) | ||
info = serializers.CharField() | ||
notice = serializers.CharField() | ||
period = serializers.CharField() | ||
transaction_direct = serializers.BooleanField() | ||
transaction_package = serializers.BooleanField() | ||
refund = serializers.CharField() | ||
|
||
def post(self,request): | ||
serializers = self.ProductCreateInputSerializer(data=request.data) | ||
serializers.is_valid(raise_exception=True) | ||
data = serializers.validated_data | ||
|
||
service=ProductCoordinatorService(user=request.user) | ||
|
||
product = service.create( | ||
name=data.get('name'), | ||
category=data.get('category'), | ||
keywords=data.get('keywords', []), | ||
basic_price=data.get('basic_price'), | ||
option=data.get('option'), | ||
product_photos=data.get('product_photos', []), | ||
info=data.get('info'), | ||
notice=data.get('notice'), | ||
period=data.get('period'), | ||
transaction_direct=data.get('transaction_direct'), | ||
transaction_package=data.get('transaction_package'), | ||
refund=data.get('refund'), | ||
) | ||
|
||
if product is not None: | ||
return Response({ | ||
'status' : 'success', | ||
'data' : {'id': product.id}, | ||
},status=status.HTTP_201_CREATED) | ||
else: | ||
return Response({ | ||
'status':'failure','message':'Product생성 실패', | ||
},status=status.HTTP_500_INTERNAL_SERVER_ERROR) | ||
|
||
|
||
class ProductPhotoCreateApi(APIView): | ||
permission_classes=(AllowAny,) | ||
|
||
class ProductPhotoCreateInputSerializer(serializers.Serializer): | ||
image = serializers.ImageField() | ||
|
||
def post(self, request): | ||
serializers = self.ProductPhotoCreateInputSerializer(data=request.data) | ||
serializers.is_valid(raise_exception=True) | ||
data = serializers.validated_data | ||
|
||
product_photo_url = ProductPhotoService.create( | ||
image=data.get('image'), | ||
) | ||
|
||
return Response({ | ||
'status':'success', | ||
'data':{'location': product_photo_url}, | ||
}, status = status.HTTP_201_CREATED) | ||
|