김성우 (9월 23일 토)
- 1차공부, 14:18-17:22(3h04m)
- 운동, 17:30-21:00
- 2차공부, 22:30-01:35 (3h05m)
-총 공부 시간 : 6h 09m**
오늘은 User API, Product API, Profile API, Product Inquery API 관련해서
Postman 을 통해서 테스트 마쳤고
하면서 Serializers.py 에 잘못된 필드나 로직, 에러처리가 있어서 수정했습니다.
사실 오늘 한 기능들에 대해서
프론트엔드도 연결해서, 결과물을 보고 만족하고 싶었는데
오늘 너무 띵가띵가 놀았더니 다 하지 못하고 자야하네요 ... 너무 아쉽습니다
API 명세서에도 아직, url 이나 Response 를 적지못해서
내일 할게 더 많을 것 같습니다 ㅎㅎ 괜찮아요~ 천천히라도 꾸준히 하면 되죠
다음은 오늘 중점적으로 작업했던 친구들입니다
유저모델
# 최초 작성일 :23년6월7일
# 업데이트 일자 : 23년 9월 23일
class User(AbstractUser):
# 메타 클래스는, DB 정보들에 대한 정보를 입력하는 곳
class Meta:
db_table = "user" # DB 테이블 이름을 user 로 설정해줌
email = models.EmailField(verbose_name='이메일', max_length=255, unique=True)
# Email , account 는 unique 해야 한다.
nickname = models.CharField("별명", null=False, blank=False, max_length=50)
username = models.CharField("이름", null=True, blank=True, max_length=50)
phoneNumberRegex = RegexValidator(regex = r'^01([0|1|6|7|8|9]?)-?([0-9]{3,4})-?([0-9]{4})$')
phone = models.CharField("휴대폰번호", validators = [phoneNumberRegex], max_length = 11, unique = True, blank=True, null=True)
# 핸드폰 번호 전용 필드가 있지만, CharField를 통해 RegexValidator를 사용하면 휴대폰번호 형식을 입력받을 수 있습니다.
LOGIN_TYPE = [
("normal", "일반"),
("google", "구글"),
("kakao", "카카오"),
("naver", "네이버"),
]
login_type = models.CharField(
"로그인 타입", max_length=10, choices=LOGIN_TYPE, default="normal"
)
joined_at = models.DateField("계정 생성일", auto_now_add=True)
is_active = models.BooleanField("활성화 여부", default=True)
is_staff = models.BooleanField("스태프 여부", default=False)
is_admin = models.BooleanField("관리자 여부", default=False)
is_phonecertify = models.BooleanField("폰번호 인증 여부", default=False)
objects = UserManager()
USERNAME_FIELD = 'email' # 회원가입시, 계정이름으로 가입하기 때문에, Unique=True 로 해주어야 하는 필드
REQUIRED_FIELDS = ['nickname',]
def __str__(self):
return str(self.email)
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
상품 모델
# 수정 날짜 : 23년 9월 22일 금요일
# ---------------- 상품 모델 시작 ----------------
class Product(models.Model):
class Meta:
db_table = "product"
PRODUCT_TYPE = [
("상의", "상의"),
("하의", "하의"),
("신발", "신발"),
("etc", "기타등등"),
]
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="products")
product_type = models.CharField(verbose_name="상품 종류", choices=PRODUCT_TYPE, null=False, max_length=30,blank=False)
product_name = models.CharField(verbose_name="상품 이름", null=False, max_length=100, unique=True)
price = models.PositiveIntegerField(verbose_name="상품 가격", null=False, blank=False)
info = models.TextField(verbose_name="상품 정보")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="생성 시간")
updated_at = models.DateTimeField(auto_now=True, verbose_name="수정 시간")
likes = models.ManyToManyField(User, verbose_name="좋아하는 상품", blank=True, related_name="liked_products")
manufacturer = models.CharField(verbose_name="제조사", max_length=30)
product_img = models.ImageField(upload_to=rename_imagefile_to_uuid, verbose_name="상품 이미지", blank=True, null=True)
# ---------------- 좋아요 갯수 ------------------
def count_likes(self):
return self.likes.count()
def __str__(self):
return str(self.product_name)
# ---------------- 상품 모델 끝 ----------------
상품 옵션 모델
# ---------------- 상품 옵션 시작 ----------------
# 수정 날짜 : 23년 9월 22일 금요일
class ProductSize(models.Model):
class Meta:
db_table = "product_size"
size_value = models.CharField("사이즈 값", max_length=10, unique=True)
def __str__(self):
return str(self.size_value)
class ProductColor(models.Model):
class Meta:
db_table = "product_color"
color = models.CharField("색상", max_length=50, unique=True)
def __str__(self):
return str(self.color)
class ProductGenderType(models.Model):
class Meta:
db_table = "product_gender_type"
GENDER_TYPE = [
("MEN", "MEN"),
("WOMEN", "WOMEN"),
("UNISEX", "UNISEX"),
]
gender_type = models.CharField("성별구분", choices=GENDER_TYPE, max_length=50,default="UNISEX")
def __str__(self):
return str(self.gender_type)
class ProductCategory(models.Model):
class Meta:
db_table = "product_category"
PRODUCT_CATEGORY = [
("TOP", "TOP"),
("BOTTOM", "BOTTOM"),
("SHOES", "SHOES"),
("ETC", "ETC"),
]
product_category = models.CharField("상품 종류", choices=PRODUCT_CATEGORY, max_length=50, default="기타등등")
def __str__(self):
return str(self.product_category)
# 수정 날짜 : 23년 9월 22일 금요일
class ProductInventoryManagement(models.Model):
class Meta:
db_table = "product_inventory"
product = models.ForeignKey(Product, on_delete=models.CASCADE)
size = models.ForeignKey(ProductSize, on_delete=models.CASCADE)
color = models.ForeignKey(ProductColor, on_delete=models.CASCADE)
product_category = models.ForeignKey(ProductCategory, on_delete=models.CASCADE)
possible_gender_type = models.ForeignKey(ProductGenderType, on_delete=models.CASCADE)
stockquantity = models.PositiveIntegerField("재고수량", default= 0)
def __str__(self):
return str('{self.product}의 재고 현황')
def decrease_stockquantity(self, stockquantity):
if self.stockquantity >= stockquantity:
self.stockquantity -= stockquantity
self.save()
return True
return False
# ---------------- 상품 옵션 끝 ----------------
상품 문의 모델
# ---------------- 상품 문의 글 시작 ----------------
# 수정 날짜 : 23년 9월 23일 토요일
# 구매하든, 안하든 사용자에게 1개이상으로 마음대로 상품에 대한 문의를 할 수 있다.
# 수정 내용 : 문의는 Question 보단 Inquery가 맞다.
class ProductInquery(models.Model):
class Meta:
db_table = "product_inquery"
INQUERY_TYPE = [
("상품", "상품"),
("배송", "배송"),
("환불/취소", "환불/취소"),
("교환/반품", "교환/반품"),
("기타등등", "기타등등"),
]
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="inquiries")
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="inquiries")
inquery_type = models.CharField("상품 문의 유형", choices=INQUERY_TYPE, max_length=50, default="기타등등")
inquery_title = models.CharField("상품 문의 제목", null=False, max_length=50)
inquery_content = models.TextField(verbose_name="문의 내용")
created_at = models.DateTimeField("작성 날짜", auto_now_add=True)
is_secret = models.BooleanField("비밀글 여부", default=False)
is_responded = models.BooleanField("관리자 답변 여부", default=False)
def __str__(self):
return str(self.inquery_title)
# ---------------- 상품 문의 글 끝 ----------------
views.py 에서 전체적으로 개선하고자 하는 목표가 생겼는데
APIView 를 ViewSet 을 이용해서 바꾸려는 것입니다
ViewSet 을 이용해서 코드를 작성하면,
하나의 클래스에 5개 6개, 그 이상의 함수들을 마음대로 작성할 수 있기 때문에
지금처럼, ArticleView 따로, ArticleDetailView 따로 작성할 필요가 없고
내 마음대로 함수의 이름을 지정할 수 있기 때문에
가독성이 좋아진다고 합니다.
추후 변경해보고, 블로그에 기록하겠습니다 .
오늘 중점적으로 다뤘던 함수는
User 관련 함수입니다. 그중에서도 프로필 함수
# 내용 : 프로필 상세보기, 프로필 수정, 회원 탈퇴
# 업데이트 일자 :23년6월7일
class ProfileView(APIView):
permission_classes = [IsAuthenticatedOrReadOnly]
# 이 함수를 실행하면, get_object_or_404를 실행한다.
def get_object(self, user_id):
return get_object_or_404(User, id=user_id)
# 회원 정보 프로필은, 쇼핑몰이기 때문에 자기 자신의 프로필만 볼 수 있도록 해줄 것임
def get(self, request, user_id):
user = self.get_object(user_id)
serializer = UserProfileSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)
# 프로필 수정, 권한이 있어야함.
def patch(self, request, user_id):
user = self.get_object(user_id)
if user == request.user:
serializer = UserUpdateSerializer(
user, data=request.data, partial=True)
serializer.is_valid(raise_exception=True) # 유효성 검사 및 오류 발생
serializer.save()
return Response({"message": "프로필 수정이 완료되었습니다!"}, status=status.HTTP_200_OK)
else:
return Response({"message": "권한이 없습니다. 내 프로필만 수정 가능해요."}, status=status.HTTP_403_FORBIDDEN)
# 이미지 업로드, 교체 가능, 삭제는 없음.
# 회원 탈퇴 (비밀번호 받아서)
def delete(self, request, user_id):
user = self.get_object(user_id)
datas = request.data.copy() # request.data → request.data.copy() 변경
# request.data는 Django의 QueryDict 객체로서 변경이 불가능하여 복사하여 수정한 후 전달하는 방법을 이용!
datas["is_active"] = False
# 회원 탈퇴 시, 계정을 비활성화 하는 것으로 설정.
serializer = UserDeleteSerializer(user, data=datas)
if user.check_password(request.data.get("password")):
serializer.is_valid(raise_exception=True) # 유효성 검사 및 오류 발생
serializer.save()
return Response({"message": "계정이 비활성화 되었습니다"}, status=status.HTTP_204_NO_CONTENT)
else:
raise ValidationError({"message": "비밀번호가 다릅니다"})
# 계정 재활성화
class ActivateView(APIView):
permission_classes = [AllowAny]
# 이 함수를 실행하면, get_object_or_404를 실행한다.
def get_object(self, user_id):
return get_object_or_404(User, id=user_id)
def post(self, request, user_id):
user = self.get_object(user_id)
if user.is_active:
return Response({"message": "이미 활성화된 계정입니다."}, status=status.HTTP_400_BAD_REQUEST)
user.is_active = True
user.save()
return Response({"message": "계정이 재활성화되었습니다."}, status=status.HTTP_200_OK)
입니다...
다음 번에는 , 유지 보수가 쉽게 전체적인 코드를 개선하고
이후의 백엔드 코드들도 ViewSet 을 이용해서 작업해보겠습니다.
오늘 TIL 끝!
'부트캠프TIL, WIL > AI웹개발(스파르타코딩클럽)' 카테고리의 다른 글
[2023.10.23] 쇼핑몰 웹개발 7일차~23일차 (3D 모델링공부가 가미된..) (5) | 2023.10.23 |
---|---|
[2023.10.04수 TIL] 쇼핑몰 웹개발 최종플젝 3~6일차 (2) | 2023.10.05 |
[23.09.22TIL] 쇼핑몰 웹개발 최종 프로젝트 S.A.(ERD, API명세, Wire Frame초기안) (0) | 2023.09.23 |
[TIL] 2023년 9월 21일 목요일 (0) | 2023.09.22 |
[TIL] 2023년 9월 20일 수요일 (0) | 2023.09.20 |