1st Project [ wesop! ] -2 CODE Review - My code

이번 프로젝트에서는 팀원 한분과 함께 product api를 맡아서 하게되었다. 다행이 백엔드 팀원분들이 모두 열의가 넘치는 분들이라 기능은 잘 구현했지만 서로 쓴 코드를 공유할 수 있는 시간이 없어서 너무 아쉬웠다... 우리 핫산 님과 11시 요정은 저의 첫 러닝 메이트 였습니다...😌

Models.py

from itertools import product
from django.db import models

from users.models import User
from cores.timestamp import TimeStamp

# Create your models here.
class Product(TimeStamp):
    name        = models.CharField(max_length=45)
    price       = models.DecimalField(decimal_places=2, max_digits=10)
    size        = models.CharField(max_length=45)
    description = models.TextField(max_length=1000)
    category    = models.ForeignKey('Category', on_delete=models.CASCADE)
    howtouse    = models.JSONField()
    feelings    = models.ManyToManyField('Feeling', through="ProductFeelings")
    badge       = models.CharField(max_length=15, null=True)
    skin_types  = models.ManyToManyField('SkinType', through='ProductSkintype')
    
    class Meta:
        db_table = 'products'

class Feeling(models.Model):
    name = models.CharField(max_length=30)
    
    class Meta:
        db_table = 'feelings'

class ProductFeelings(models.Model):
    product = models.ForeignKey('Product' , on_delete=models.CASCADE)
    feeling = models.ForeignKey('Feeling' , on_delete=models.CASCADE)

    class Meta:
        db_table = 'product_feelings'

class Category(models.Model):
    category_name    = models.CharField(max_length=45)
    main_description = models.CharField(max_length=1000, null=True)
    sub_description  = models.CharField(max_length=1000, null=True)
    
    class Meta:
        db_table = 'categories'

class Ingredient(models.Model):
    name = models.CharField(max_length=300)

    class Meta:
        db_table = 'ingredients'

class ProductIngredient(models.Model):
    product    = models.ForeignKey('Product', on_delete=models.CASCADE)
    ingredient = models.ForeignKey('Ingredient', on_delete=models.CASCADE)
    major      = models.BooleanField(default=False)

    class Meta:
        db_table = 'product_ingredients'

class ProductImage(models.Model):
    url       = models.CharField(max_length=2000)
    product   = models.ForeignKey('Product', on_delete=models.CASCADE)

    class Meta:
        db_table = 'product_imgaes'
    
class SkinType(models.Model):
    name = models.CharField(max_length=45)

    class Meta:
        db_table = 'skin_types'

class ProductSkintype(models.Model):
    product   = models.ForeignKey('Product', on_delete=models.CASCADE)
    skin_type = models.ForeignKey('SkinType', on_delete=models.CASCADE)

    class Meta:
        db_table = 'product_skintypes'


class Review(TimeStamp):
    user    = models.ForeignKey(User, on_delete=models.CASCADE, related_name='users')
    product = models.ForeignKey('Product' , on_delete=models.CASCADE, related_name='products')
    content = models.CharField(max_length=400)

    class Meta:
        db_table = 'reviews'

class ReviewImage(models.Model):
    review    = models.ForeignKey('Review' , on_delete=models.CASCADE , related_name='reviews')
    image_url = models.CharField(max_length=300)

    class Meta:
        db_table = 'review_images'

안써봤던 JSONField를 사용해보았다. 장고의 필드 내용들은 한번 쭈욱 정리가 필요하니 나중에 한번 다 정리를 해보려고한다.

product_detail_views

class ProductDetailView(View):
    def get(self, request, product_id):
        try: 
            product = Product.objects.get(id = product_id)
            main_ingredients = Ingredient.objects.filter(productingredient__product_id = product.id, productingredient__major = True)
            skin_type        = SkinType.objects.filter(productskintype__product_id = product_id)
            feelings         = ProductFeelings.objects.filter(product = product_id)
            product_detail = {
                'id'                : product.id,
                'name'              : product.name,
                'price'             : product.price,
                'size'              : product.size,
                'category'          : product.category.category_name,
                'description'       : product.description,
                'feeling'           : [feeling.feeling.name for feeling in feelings],
                'product_imges'     : [image.url for image in product.productimage_set.all()],
                'main_ingredients'  : [ingredient.name for ingredient in main_ingredients],
                'ingredients'       : [ingredient.name for ingredient in Ingredient.objects.filter(productingredient__product = product_id)],
                'skin_type'         : [type.name for type in skin_type]
            }
            howtouse = product.howtouse
              
            return JsonResponse({'result' : [ product_detail , howtouse ] } , status = 200)
        except KeyError:
            return JsonResponse({'message' : 'KEY_ERROR'} , status = 404)
        except Product.DoesNotExist:
            return JsonResponse({'message' : 'PRODUCT_NAME_ERROR'} , status = 404)

Mysql을 알아야 ERD를 잘 알고 ERD에 대한 이해도가 높아야 좋은 api를 만들 수 있다... 누군가 시작점에서 내 블로그를 읽는 다면 무조건 기본을 하나하나 다져가며 공부하길 바란다.

product_recommend_view

class RecommendedView(View):
    def get(self, request, product_id):
        try:
            category_id   = Product.objects.get(id = product_id).category
            products      = Product.objects.filter(category = category_id).exclude(id=product_id)
            
            recommend_list = [{
                'name'      : product.name,
                'image'     : [image.url for image in product.productimage_set.all()],
                'skintype'  : [types.skin_type.name for types in product.productskintype_set.all()]
                } for product in products]

            return JsonResponse({'result' : recommend_list }, status = 200)
        except KeyError:
            return JsonResponse({'message' : 'KEY_ERROR'} , status = 401)
        except Product.DoesNotExist:
            return JsonResponse({'message' : 'PRODUCT_DOES_EXIST'} , status = 401)

product_review_view

class ProductReviewView(View):
    def get(self, request):
        try:
            data = json.loads(request.body)
            reviews =Review.objects.filter(product_id = data['product_id'])
            
            if not reviews.exists():
                return JsonResponse({'message' : 'PRODUCT_REVIEW_DOES_NOT_EXIST'} , status = 404)
            
            result = [{
                    'review_id' : review.id,
                    'user'      : review.user.email,
                    'content'   : review.content
                } for review in reviews]
            return JsonResponse({'message' : result} , status =200)
        except KeyError:
            return JsonResponse({'message': 'KEY_ERROR'} , status = 400)
        except Product.DoesNotExist:
            return JsonResponse({'message' : 'PRODUCT_DOES_NOT_EXIST'} , status = 400)
    
    @author
    def post(self, request):
        try:
            data = json.loads(request.body)
            content     = data['content']
            user        = request.user
            product     = Product.objects.get(id = data['product_id'])
            
            Review.objects.create(  
                user    = user,
                product = product, 
                content = content
            )
             
            return JsonResponse({'message' : 'SUCCESS'} , status = 201) 
        except KeyError:
            return JsonResponse({'message': 'KEY_ERROR'} , status = 400)
        except Product.DoesNotExist:
            return JsonResponse({'message' : 'PRODUCT_DOES_NOT_EXIST'} , status = 404)
    
    @author
    def delete(self, request, review_id):
        review      = Review.objects.filter(user = request.user , id = review_id)
            
        if not review.exists():
            return JsonResponse({'message' : 'UNAUTHORIZED_REQUEST'} , status = 404)
        
        review.delete()
            
        return JsonResponse({'message' : 'SUCCESS'} , status = 200)

게시판 기능 api 는 프로젝트의 사정상 빛을 볼 순 없었다. 뭔가 아쉬운 마음이 들기도 했지만 내 api가 빛을 볼 수 있도록 자신에 시간에서 최선을 다한 내 프론트 짝궁 Mr.MD의 모습을 봤기 때문에 기쁜 마음으로 다음 기약했다.

product_list_view (필터 기능 구현)

class ProductListView(View):
    def get(self, request):
        category_id   = request.GET.get('category_id', None)
        offset        = int(request.GET.get('offset', 0))
        limit         = int(request.GET.get('limit', 100))
        ingredient_id = request.GET.getlist('ingredient_id', None)
        skintype_id   = request.GET.getlist('skintype_id', None)
        scent         = request.GET.get('scent', None)
        feeling_id    = request.GET.get('feeling_id', None)
        search        = request.GET.get('search', None)
        
        q = Q()

        if search:
            q &= Q(name__icontains=search)

        if category_id:
            q &= Q(category__id=category_id)

        if scent:
            q &= Q(howtouse__scent__contains=scent)
        
        if ingredient_id:
            q &= Q(productingredient__ingredient__id__in=ingredient_id)
        
        if skintype_id:
            q &= Q(productskintype__skin_type__id__in=skintype_id)
        
        if feeling_id:
            q &= Q(productfeelings__feeling__id__in=feeling_id)

        products = Product.objects.filter(q)[offset:offset+limit]

        result = [{
            'id'         : product.id,
            'badge'      : product.badge,
            'productName': product.name,
            'size'       : product.size,
            'price'      : product.price,
            'feeling'    : [feeling.feeling.name for feeling in product.productfeelings_set.all()],
            'ingredient' : [item.ingredient.name for item in product.productingredient_set.all()],
            'skin_type'  : [productskintype.skin_type.name for productskintype in product.productskintype_set.all()],
            'url'        : [img.url for img in product.productimage_set.all()],
            'howtouse'   : product.howtouse,
            'category'   : {
                'categoryId'            : product.category.id,
                'categoryName'          : product.category.category_name,
                'categoryDescription'   : product.category.main_description,
                'categorySubDescription': product.category.sub_description
            }
        } for product in products]
        return JsonResponse({'result':result}, status=200)

이 api는 다른 백엔드 분과 요이땅! 하고 만든 뒤 서로껄 합치면서 작성한 api이다. 다 만든 뒤 합치면서 더 성능이 좋은 api가 만들어 진것 같아 기분이 좋았다.

좋은 웹페이지 즐겨찾기