Skip to content

Commit

Permalink
#8 feat: products create (#10)
Browse files Browse the repository at this point in the history
* #8 feat: products create

* test product create api

* products/create/ 재수정

* remove migrations/0002 file

* product/migrations 재추가

* #8 style : 주석 정리
  • Loading branch information
0321minji authored Jan 10, 2024
1 parent 7616226 commit 925ab8a
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 5 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,7 @@ cython_debug/
secrets.json
media
.env
.Dockerfile.swp
.Dockerfile.swp
products/photo/
users/profile/
.idea
1 change: 1 addition & 0 deletions UpcyProject/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@
urlpatterns = [
path('admin/', admin.site.urls),
path('users/', include('users.urls')),
path('products/',include('products.urls')),
]
24 changes: 24 additions & 0 deletions products/migrations/0002_alter_productkeyword_product_and_more.py
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'),
),
]
6 changes: 3 additions & 3 deletions products/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ class Product(TimeStampedModel):
class ProductKeyword(models.Model):
name = models.CharField(max_length=100)
product = models.ForeignKey(
'Product', related_name='keywords', on_delete=models.CASCADE, null=False, blank=False)
'Product', related_name='keywords', on_delete=models.CASCADE, null=True, blank=True)


#Product_Photo 모델 만들기
def get_product_photo_upload_path(instance, filename):
return 'product/photo/{}'.format(filename)
return 'products/photo/{}'.format(filename)

class ProductPhoto(models.Model):
image = models.ImageField(
upload_to=get_product_photo_upload_path, default='product_photo.png')
product = models.ForeignKey(
'Product', related_name='product_photos', on_delete=models.CASCADE, null=False, blank=False)
'Product', related_name='product_photos', on_delete=models.CASCADE, null=True, blank=True)
133 changes: 133 additions & 0 deletions products/services.py
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}' 추가")
10 changes: 10 additions & 0 deletions products/urls.py
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'),

]
82 changes: 81 additions & 1 deletion products/views.py
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)

0 comments on commit 925ab8a

Please sign in to comment.