MLS - My Little Shoes (나만의 신발 스타일 만들기)
2022.06.28-07.06
김동우 : 회원가입/로그인 기능 / Generative model 사용
김진수 : 추천 스타일 페이지
박진우 : 이미지 업로드 + 결과 페이지 (결과 페이지에서 저장누르면 히스토리에 저장됨)
최민기 : 히스토리(게시판) 페이지 (+ 좋아요 + 댓글 + 즐겨찾기)
https://github.com/crowsonkb/style-transfer-pytorch
사용자로부터 image_one (content.png)와 image_two (style.png)를 받아서
style_transfer 명령어를 통해 image_result (out.png)를 생성
// upload.js
// 이미지 업로드
async function imageUpload() {
const formData = new FormData();
image_one = document.getElementById("a-upload-file").files[0];
image_two = document.getElementById("b-upload-file").files[0];
formData.append("image_one", image_one);
formData.append("image_two", image_two);
$.ajax({
type: "POST",
url: "http://127.0.0.1:8000/upload/",
data: formData,
cache: false,
contentType: false,
processData: false,
success: function (response) {
document
.getElementById("result-file")
.setAttribute("src", backend_url + "/out.png");
},
error: function (error) {
alert(error.responseText);
},
});
}
# upload/views.py
# 이미지 등록 페이지
class ImageUploadView(APIView):
# 새로운 이미지 만들기
# 이미지 두장 받아서 새로운 스타일의 이미지 만들어서 반환
def post(self, request):
content = request.FILES.get("image_one", "")
style = request.FILES.get("image_two", "")
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
content_path = BASE_DIR + '\content.png'
style_path = BASE_DIR + '\style.png'
with open(content_path, 'wb+') as destination:
for chunk in content.chunks():
destination.write(chunk)
with open(style_path, 'wb+') as destination:
for chunk in style.chunks():
destination.write(chunk)
os.system('style_transfer content.png style.png')
output_path = 'out.png'
return Response({'output_path': output_path}, status=status.HTTP_200_OK)
content.png, style.png, out.png를 ImageField로 넣은 ImageModel 저장 후 HistoryModel 저장
// upload.js
// 결과 저장
async function image_save() {
user = localStorage.getItem("user");
const data = {
user: user,
};
$.ajax({
type: "POST",
url: "http://127.0.0.1:8000/upload/result/",
data: JSON.stringify(data),
success: function (response) {
alert(response["message"]);
console.log(response["message"]);
location.reload();
},
error: function (error) {
alert(error.responseText);
console.log(error.responseText);
},
});
}
# upload/views.py
# 이미지 결과 페이지
class ImageResultView(APIView):
# 저장하기 버튼 클릭 : 이미지 저장
def post(self, request):
data = json.loads(request.body)
user_id = data['user']
image_object = ImageModel()
image_one = Image.open('content.png')
image_file = BytesIO()
image_one.save(image_file, image_one.format)
image_object.image_one.save('content.png',
InMemoryUploadedFile(
image_file,
None, 'content.png',
'image/png',
image_one.size,
None),
save=False)
image_one.close()
image_two = Image.open('style.png')
image_file = BytesIO()
image_two.save(image_file, image_two.format)
image_object.image_two.save('style.png',
InMemoryUploadedFile(
image_file,
None, 'style.png',
'image/png',
image_two.size,
None),
save=False)
image_two.close()
image_result = Image.open('out.png')
image_file = BytesIO()
image_result.save(image_file, image_result.format)
image_object.image_result.save('out.png',
InMemoryUploadedFile(
image_file,
None, 'out.png',
'image/png',
image_result.size,
None),
save=False)
image_result.close()
image_object.save()
history_data = {
'exposure_start': "2022-06-30 00:00:00",
'exposure_end': "2023-06-30 00:00:00",
}
user = UserModel.objects.get(id=user_id)
history_serializer = HistorySerializer(data=history_data)
if history_serializer.is_valid():
history_serializer.save(user=user, image=image_object)
return Response({'message': '저장 완료!'},status=status.HTTP_200_OK)
class UserManager(BaseUserManager):
def create_user(self, email, username, password=None):
if not username:
raise ValueError('Users must have an username')
user = self.model(
username=username,
email=email,
)
user.set_password(password)
user.save(using=self._db)
return user
# python manage.py createsuperuser 사용 시 해당 함수가 사용됨
def create_superuser(self, email, username, password=None):
user = self.create_user(
email=email,
username=username,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
username = models.CharField("사용자 아이디", max_length=12, unique=True)
email = models.EmailField("이메일", max_length=100, unique=True)
password = models.CharField("비밀번호", max_length=128)
fullname = models.CharField('이름', max_length=20)
join_date = models.DateTimeField('생성시각', auto_now_add=True)
def __str__(self):
return f"{self.username}"
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserManager()
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
@property
def is_staff(self):
return self.is_admin
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
intro = models.TextField()
age = models.IntegerField()
def __str__(self):
return f"{self.user.username}'s profile"
# user/serializers.py
import re
from rest_framework import serializers
from .models import User, UserProfile
SPECIAL_CHAR = ['`', '~', '!', '@', '#', '%', '^', '&', '*', '(', ')',
',', '.', '/', '<', '>', '?', '[', ']', '{', '}']
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ['age', 'intro']
class UserSerializer(serializers.ModelSerializer):
userprofile = UserProfileSerializer()
brand = serializers.SerializerMethodField()
def get_brand(self, obj):
return "브랜드가 들어갈 자리"
class Meta:
model = User
fields = ["username", "password", "fullname", "email", "userprofile", "brand"]
extra_kwargs = {
'password': {'write_only': True}
}
def validate(self, data):
if self.instance is None or data.get('password'):
password = data.get('password', "")
if len(password) < 8 or len(password) > 14:
raise serializers.ValidationError(
detail={"error": "비밀번호는 8글자 이상, 14글자 이하이어야합니다."},
)
if not any(char in SPECIAL_CHAR for char in password):
raise serializers.ValidationError(
detail={"error": "특수문자가 1개이상 포함되어야합니다."},
)
if re.search('[0-9]+', password) is None:
raise serializers.ValidationError(
detail={"error": "숫자가 1개이상 포함되어야합니다."},
)
if (re.search('[a-z]+', password) is None) and (re.search('[A-Z]+', password) is None):
raise serializers.ValidationError(
detail={"error": "영문 대문자 또는 소문자가 1개이상 포함되어야합니다."},
)
return data
def create(self, validated_data):
userprofile = validated_data.pop('userprofile')
password = validated_data.pop('password')
user = User.objects.create(**validated_data)
user.set_password(password)
user.save()
userprofile = UserProfile.objects.create(user=user, **userprofile)
return validated_data
# instance : 수정할 object
# validated_data : 수정할 내용
def update(self, instance, validated_data):
# validated_data = {'username': 'dongwoo', 'email': 'esdx@daum.net', ...}
for key, value in validated_data.items():
if key == "password":
instance.set_password(value)
continue
if key == "userprofile":
for key_2, value_2 in value.items():
setattr(instance.userprofile, key_2, value_2)
continue
setattr(instance, key, value)
instance.save()
instance.userprofile.save()
return instance
# user/views.py
# 로그인/로그아웃 기능
class UserView(APIView):
permission_classes = [permissions.AllowAny]
# 로그인 기능
def post(self, request):
login_data = {
'email': request.data.get('email', ''),
'password': request.data.get('password', ''),
}
user = authenticate(request, **login_data)
if not user:
msg = '아이디 또는 패스워드를 확인해주세요.'
return Response({'message': msg}, status=status.HTTP_400_BAD_REQUEST)
login(request, user)
id = user.id
msg = '로그인 성공!'
return Response({'message': msg, 'id': id, 'fullname': user.fullname, 'email': user.email}, status=status.HTTP_200_OK)
# 로그아웃 기능
def delete(self, request):
logout(request)
msg = '로그아웃 성공!'
return Response({'message': msg}, status=status.HTTP_200_OK)
# 회원 정보 기능
class UserInfoView(APIView):
# 회원 정보 조회
def get(self, request):
user = request.user
if not user.is_authenticated:
msg = '로그인을 해주세요'
return Response({'message': msg}, status=status.HTTP_400_BAD_REQUEST)
return Response(UserSerializer(user).data, status=status.HTTP_200_OK)
# 회원가입
def post(self, request):
signup_data = json.loads(request.body)
user_serializer = UserSerializer(data=signup_data)
if user_serializer.is_valid(): # validation
user_serializer.save() # create
return Response({'message': '저장 완료!'}, status=status.HTTP_200_OK)
return Response(user_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# 회원수정
def put(self, request):
user = request.user
confirm_data = {
'email': user.email,
'password': request.data.get('password_old', ''),
}
user = authenticate(request, **confirm_data)
if not user:
return Response({'message': '비밀번호를 확인해주세요.'}, status=status.HTTP_400_BAD_REQUEST)
if request.data.get('password_new'):
request.data['password'] = request.data.get('password_new')
user_serializer = UserSerializer(user, data=request.data, partial=True)
if user_serializer.is_valid(): # validation
user_serializer.save() # update
return Response({'message': '저장 완료!'}, status=status.HTTP_200_OK)
return Response(user_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# 회원탈퇴
def delete(self, request):
confirm_word = request.data.get('confirm_word', '')
if confirm_word != '확인':
return Response({'error': '회원 탈퇴 확인 메시지를 정확히 입력해주세요'},
status=status.HTTP_400_BAD_REQUEST)
email = request.user.email
password = request.data.get('password', '')
confirm_data = {
'email': email,
'password': password,
}
user = authenticate(request, **confirm_data)
if not user:
return Response({'error': '비밀번호를 확인해주세요.'}, status=status.HTTP_400_BAD_REQUEST)
logout(request)
user.delete()
return Response({'message': '삭제 성공!'}, status=status.HTTP_200_OK)
# 회원 히스토리 기능
class UserHistoryView(APIView):
permission_classes = [permissions.IsAuthenticated]
# 회원 히스토리 조회 기능
def get(self, request):
histories = History.objects.filter(user=request.user)
history_ids = [history.id for history in histories]
return Response(history_ids, status=status.HTTP_200_OK)
# 회원 히스토리 좋아요 기능
class UserHistoryLikeView(APIView):
permission_classes = [permissions.IsAuthenticated]
# 좋아요누른 히스토리 조회 기능
def get(self, request):
likes = Like.objects.filter(user=request.user)
histories = [like.history for like in likes]
history_ids = [history.id for history in histories]
return Response(history_ids, status=status.HTTP_200_OK)
https://www.notion.so/kimphysicsman/MLS-My-Little-Shoes-2d7eafdb6a514ae7a569f11cc04411e1
https://github.com/nbcamp-AI-2-fantastic4/mylittleshoes_backend
https://github.com/nbcamp-AI-2-fantastic4/mylittleshoes_frontend