스파르타코딩클럽
내일배움캠프 AI 웹개발자양성과정 2회차
DRF 강의 6일차 과제
0. 과제내용
- product 앱의 product 테이블 구성을 <작성자, 썸네일, 상품 설명, 등록일자, 노출 종료 일자, 가격, 수정 일자, 활성화 여부>로 변경해주세요
- django serializer를 사용해 validate / create / update 하는 기능을 구현해주세요
- custom validation 기능을 사용해 노출 종료 일자가 현재보다 더 이전 시점이라면 상품을 등록할 수 없도록 해주세요
- custom creator 기능을 사용해 상품 설명의 마지막에 "<등록 일자>에 등록된 상품입니다." 라는 문구를 추가해주세요
- custom update 기능을 사용해 상품이 update 됐을 때 상품 설명의 가장 첫줄에 "<수정 일자>에 수정되었습니다." 라는 문구를 추가해주세요
- product 앱에서 <작성자, 상품, 내용, 평점, 작성일>을 담고 있는 review 테이블을 만들어주세요
- review 테이블을 관리자 페이지에서 자유롭게 추가/수정 할 수 있도록 설정해주세요
- 현재 날짜를 기준으로, 노출 종료 날짜가 지나지 않았고 활성화 여부가 True이거나 로그인 한 사용자가 등록 한 상품들의 정보를 serializer를 사용해 리턴해주세요
- 5번 상품 정보를 리턴 할 때 상품에 달린 review와 평균 점수를 함께 리턴해주세요
- 평균 점수는 (리뷰 평점의 합/리뷰 갯수)로 구해주세요
- 작성 된 리뷰는 모두 return하는 것이 아닌, 가장 최근 리뷰 1개만 리턴해주세요
- 로그인 하지 않은 사용자는 상품 조회만 가능하고, 회원가입 이후 3일 이상 지난 사용자만 상품을 등록 할 수 있도록 권한을 설정해주세요
1. product 앱의 product 테이블 구성을 <작성자, 썸네일, 상품 설명, 등록일자, 노출 종료 일자, 가격, 수정 일자, 활성화 여부>로 변경해주세요
class Product(models.Model):
author = models.ForeignKey(UserModel, verbose_name="작성자", on_delete=models.CASCADE)
title = models.CharField('상품 명', max_length=30)
content = models.TextField('상품 설명')
thumbnail = models.ImageField('썸네일', upload_to='product/img', height_field=None, width_field=None, max_length=100)
price = models.IntegerField('가격', default=0)
update_at = models.DateTimeField('수정 일자', auto_now=True)
created_at = models.DateTimeField('등록 일자', auto_now_add=True)
show_end_at = models.DateTimeField('노출 종료 일자', default="2022-06-25 00:00:00")
is_active = models.BooleanField('활성화 여부', default=True)
def __str__(self):
return f'Product : {self.title} - {self.author}'
2. django serializer를 사용해 validate / create / update 하는 기능을 구현해주세요
- custom validation 기능을 사용해 노출 종료 일자가 현재보다 더 이전 시점이라면 상품을 등록할 수 없도록 해주세요
- custom creator 기능을 사용해 상품 설명의 마지막에 "<등록 일자>에 등록된 상품입니다." 라는 문구를 추가해주세요
- custom update 기능을 사용해 상품이 update 됐을 때 상품 설명의 가장 첫줄에 "<수정 일자>에 수정되었습니다." 라는 문구를 추가해주세요
class ProductSerializer(serializers.ModelSerializer):
author = serializers.SerializerMethodField()
def get_author(self, obj):
author_name = obj.author.username
return author_name
class Meta:
model = Product
fields = ["title", "author", "content", "created_at",
"show_end_at", 'update_at', 'price', 'is_active']
def validate(self, data):
if 'babo' in data.get('title'):
raise serializers.ValidationError(
detail={"error": "부적절한 단어를 사용할 수 없습니다."},
)
now = timezone.now()
if data.get('show_end_at') and now > data.get('show_end_at'):
raise serializers.ValidationError(
detail={"error": "노출 종료 일자는 현재보다 과거일 수 없습니다."},
)
return data
def create(self, validated_data):
author = self.context.get('request').user
files = self.context.get('request').FILES
thumbnail = files.get('thumbnail')
product = Product(author=author, thumbnail=thumbnail,
**validated_data)
product.save()
created_at = product.created_at
end_msg = f'{created_at.strftime("%Y/%m/%d %H:%M:%S")} 에 등록된 상품입니다. '
product.content = product.content + '\n' + end_msg
product.save()
return product
def update(self, instance, validated_data):
for key, value in validated_data.items():
setattr(instance, key, value)
instance.save()
updated_at = instance.update_at
start_msg = f'{updated_at.strftime("%Y/%m/%d %H:%M:%S")} 에 수정되었습니다. '
instance.content = start_msg + '\n' + instance.content
instance.is_active = True
instance.save()
return instance
3. product 앱에서 <작성자, 상품, 내용, 평점, 작성일>을 담고 있는 review 테이블을 만들어주세요
class Review(models.Model):
author = models.ForeignKey(UserModel, verbose_name="작성자", on_delete=models.CASCADE)
product = models.ForeignKey(Product, verbose_name="상품", on_delete=models.CASCADE)
content = models.TextField('내용', default="")
rating = models.IntegerField('평점', default=0)
created_at = models.DateTimeField('작성일', auto_now_add=True)
def __str__(self):
return f'Review : {self.product.title} - {self.author.username} ({self.rating})'
4. review 테이블을 관리자 페이지에서 자유롭게 추가/수정 할 수 있도록 설정해주세요
class ProductAdmin(admin.ModelAdmin):
...
admin.site.register(Product, ProductAdmin)
admin.site.register(Review)
5. 현재 날짜를 기준으로, 노출 종료 날짜가 지나지 않았고 활성화 여부가 True이거나 로그인 한 사용자가 등록 한 상품들의 정보를 serializer를 사용해 리턴해주세요
class ProductDetailView(APIView):
permission_classes = [IsAuthenticatedorRegistedMoreThanThreeDaysUser]
def get(self, request):
user = request.user
show_now_at = timezone.now()
query = Q(author=user) | ((Q(show_end_at__gte=show_now_at)) & Q(is_active=True))
return Response(ProductDetailSerializer(products, many=True).data)
6. 5번 상품 정보를 리턴 할 때 상품에 달린 review와 평균 점수를 함께 리턴해주세요
- 평균 점수는 (리뷰 평점의 합/리뷰 갯수)로 구해주세요
- 작성 된 리뷰는 모두 return하는 것이 아닌, 가장 최근 리뷰 1개만 리턴해주세요
class ReivewSerializer(serializers.ModelSerializer):
author = serializers.SerializerMethodField()
def get_author(self, obj):
author_name = obj.author.username
return author_name
class Meta:
model = Review
fields = ["author", "content", "rating", "created_at",]
class ProductDetailSerializer(serializers.ModelSerializer):
author = serializers.SerializerMethodField()
avg_rating = serializers.SerializerMethodField()
review = serializers.SerializerMethodField()
def get_author(self, obj):
author_name = obj.author.username
return author_name
def get_review(self, obj):
reviews = Review.objects.filter(product=obj).order_by("-created_at")
if 0 < len(reviews):
return ReivewSerializer(reviews[0]).data
else:
return None
def get_avg_rating(self, obj):
reviews = Review.objects.filter(product=obj)
if 0 < len(reviews):
total_rating = 0
for review in reviews:
total_rating += review.rating
avg_rating = total_rating / len(reviews)
return avg_rating
else:
return None
class Meta:
model = Product
fields = ["title", "author", "content", "created_at",
"show_end_at", 'update_at', 'price', 'is_active',
"avg_rating", 'review']
7. 로그인 하지 않은 사용자는 상품 조회만 가능하고, 회원가입 이후 3일 이상 지난 사용자만 상품을 등록 할 수 있도록 권한을 설정해주세요
class IsAuthenticatedorRegistedMoreThanThreeDaysUser(BasePermission):
"""
get : 비로그인 사용자 가능
post, put, delete: 로그인 and 가입일 기준 3일 이상 지난 사용자만 가능
"""
message = '관리자 또는 가입 후 3일 이상 지난 사용자만 게시글을 작성하실 수 있습니다.'
SAFE_METHODS = ('GET', )
def has_permission(self, request, view):
user = request.user
if user.is_authenticated and user.is_admin:
return True
if user.is_authenticated and request.user.join_date < (timezone.now() - timedelta(days=3)):
return True
if request.method in self.SAFE_METHODS:
return True
return False
Github
https://github.com/kimphysicsman/nbcamp-drf-2