■ JITHub 개발일지 50일차
□ TIL(Today I Learned) ::
Django Rest Framework 코드 리뷰
- 유저 정보 유효성 검사(validation)
- UserSerializer에서 비밀번호를 재확인하기 위해서 repassword라는 변수를 생성했다.
- 임의의 인자를 추가로 받기 위해서 DRF에서 제공하는 extra_kwargs를 통해 error_messages를 지정해주었다. 항목은 required, invaild, blank를 지정했다.
---------------------------------------------------------------------------------------
class Meta:
...
extra_kwargs = {
# write_only : 해당 필드를 쓰기 전용으로 만들어 준다.
# 쓰기 전용으로 설정 된 필드는 직렬화 된 데이터에서 보여지지 않는다.
'password': {'write_only': True}, # default : False
'email': {
# error_messages : 에러 메세지를 자유롭게 설정 할 수 있다.
'error_messages': {
# required : 값이 입력되지 않았을 때 보여지는 메세지
'required': '이메일을 입력해주세요.',
# invalid : 값의 포맷이 맞지 않을 때 보여지는 메세지
'invalid': '알맞은 형식의 이메일을 입력해주세요.'
},
# required : validator에서 해당 값의 필요 여부를 판단한다.
'required': False # default : True
},
}
1) required : 비어있을 경우 나타내는 에러메시지
2) invalid : 값의 포맷이 맞지 않을 경우 나타내는 에러메시지
3) blank : ???
class UserSerializer(serializers.ModelSerializer):
repassword = serializers.CharField(error_messages={'required':'비밀번호를 입력해주세요', 'blank':'비밀번호를 입력해주세요.'})
class Meta:
model = User
fields = ('email', 'nickname', 'password', 'repassword','profile_image',)
# 각 필드에 해당하는 다양한 옵션 지정
extra_kwargs = {'email':{
'error_messages':{
'required':'이메일을 입력해주세요',
'invalid' : '알맞은 형식의 이메일을 입력해주세요',
'blank' : '이메일을 입력해주세요',
}},
...
- class Meta에 대한 설정을 완료하고, validate를 지정해주어야 한다.
- 유효성검사 케이스는 닉네임, 패스워드이다. data.get을 통해 유효성 검사에 필요한 각각의 값들을 가져온다.
- 아래 함수들을 validators.py에 생성함을 통해 유효성검사에 활용할 수 있다.
1) contains_special_character(<str>) : 특수문자가 포함되어있는지 확인
2) contains_uppercase_letter(<str>) : 대문자가 포함되어있는지 확인
3) contains_lowercase_letter(<str>) : 소문자가 포함되어있는지 확인
4) contains_number(<str>) : 숫자가 포함되어있는지 확인
import string
#특수문자 유효성검사
def contains_special_character(value):
for char in value:
if char in string.punctuation:
return True
return False
#영어 대문자 유효성 검사
def contains_uppercase_letter(value):
for char in value:
if char.isupper():
return True
return False
#영어 소문자 유효성 검사
def contains_lowercase_letter(value):
for char in value:
if char.islower():
return True
return False
#숫자 유효성 검사
def contains_number(value):
for char in value:
if char.isdigit():
return True
return False
- 유효성 검사에 맞지 않을 경우 serializers.ValidationError(detail={"<이름>":"<오류내용>"})을 사용하여 에러 메시지를 전달한다.
def validate(self, data):
nickname = data.get('nickname')
password = data.get('password')
repassword = data.get('repassword')
# nickname 유효성 검사
if contains_special_character(nickname) or len(nickname)<3:
raise serializers.ValidationError(detail={
"nickname":"닉네임은 2자 이하 또는 특수문자를 포함할 수 없습니다."
})
# 비밀번호 유효성 검사
if password:
# 비밀번호 일치여부
if password != repassword:
raise serializers.ValidationError(detail={
"password":"비밀번호가 일치하지 않습니다."
})
# 비밀번호 유효성
if (len(password) < 8 or len(password) > 17
or not contains_uppercase_letter(password)
or not contains_lowercase_letter(password)
or not contains_number(password)
or not contains_special_character(password)):
raise serializers.ValidationError(detail={
"password":"비밀번호는 8자 이상 16자 이하의 영문 대/소문자, 숫자, 특수문자 조합이어야 합니다."
})
return data
- UserSerializer 중 회원 가입 기능
view에서는 request.data를 가져와서 serializer에서 validated_data로 받아온다.
받아온 데이터에서 email과 nickname을 각각 추출하는데 이 때 dict형식으로 가져오므로 key타입으로 가져와서 각각 변수에 지정하였다.
지정된 변수는 User 모델에 맞게 입력해 넣고, password는 set_password를 통해 해싱처리한 후 마지막에 save()를 통해 저장한다.
# 회원 가입
def create(self, validated_data):
# request.data를 통해 받아온 데이터(validated_data) email과 nickname을 각 변수에 넣고 User모델에 넣어 패스워드를 해싱처리 후 user에 저장
email = validated_data['email']
nickname = validated_data['nickname']
user = User(
email=email,
nickname=nickname
)
user.set_password(validated_data['password']) # set_password : 패스워드 해싱처리
user.save()
return user
- 회원 정보 수정시에는 instance가 추가된다. instance는 view에서 이미 user = get_object_or_404(User, id=request.user.id)를 통해 가져온 유저정보이며, validated_data는 위의 회원가입과 동일하게 request.data이다.
이는 정보수정할 때에 기존의 user정보를 가져와서 입혀넣어야 하기 때문이며, view에서는 partial = True를 통해 기존의 데이터에 수정사항이 없을 경우 기존 데이터를 입혀넣을 수 있도록 한다.
serializer = UserSerializer(user, data=request.data, partial=True)
- 회원가입과 동일하게 기존의 instance의 각 항목에 validated_data의 각 항목을 넣어준다. 이 때 .get을 사용하여 항목들을 가져왔는데, 아래 drf docs를 참고하였다.
# 수정
def update(self, instance, validated_data): # instances는 현재 user를, validated_data는 request.data를 받아온다.
# 아래 처럼 instance에 request를 통해 가져온 데이터를 넣는다.
instance.nickname = validated_data.get("nickname", instance.nickname)
instance.profile_image = validated_data.get('profile_image', instance.profile_image)
instance.save()
return instance
- 백엔드에서 모두 셋팅을 해주었다고 생각했지만 반응이 없어 콘솔 창을 확인해본 결과, CORS 에러가 있었다. settings.py에서 아래 사항을 모두 확인해주어야 한다. 마지막 코드인 CORS_ALLOW_ALL_ORIGINS = True를 누락해서 계속 에러가 발생하고 있었던 것이다.
INSTALLED_APPS = [
...
'corsheaders',
...
]
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
CORS_ALLOW_ALL_ORIGINS = True
- 이제 Song app에 대한 세팅을 해준다.
- 모델과 Serializer를 정의해주는데, song의 기능 중 '좋아요'가 있다. 좋아요는 User모델과 manytomany로 엮고, 좋아요 숫자를 count해야 한다. 아래와 같이 필요한 모델을 셋팅하는데 맨 아래줄 처럼 likes를 정의한다.
class Song(modesl.Model):
year = models.CharField(max_length=70, blank=True)
rank = models.CharField(max_length=70, blank=True)
title = models.CharField(max_length=70, blank=True)
genre = models.CharField(max_length=70, blank=True)
singer = models.CharField(max_length=70, blank=True)
type = models.CharField(max_length=70, blank=True)
lyrics = models.TextField(blank=True)
likes = models.IntegerField(blank=True)
image = models.TextField(blank=True)
genre_no = models.IntegerField(blank=True)
song_likes = models.ManyToManyField(User, related_name="like_song",blank=True)
- serializer를 셋팅해야 하는데 위의 특성을 위해 serializermethodfield를 사용한다.
class SongSerializer(serializers.ModelSerializer):
class Meta:
model = Song
fields = "__all__"
song_likes = serializers.StringRelatedField(many=True)
song_likes_count = serializers.SerializerMethodField()
...
def get_song_likes_count(self, obj) :
return obj.song_likes.count()
*SerializerMethodField는 아래의 케이스에 사용한다.
- 모델에 없는 필드이지만 JSON에 특정 필드를 추가하여 전달할 경우
- 모델에 정의된 필드 값을 변경해서 JSON으로 전달할 경우
위에서 정의한 SerializerMethodField는 클래스 아래에 get_song_likes_count와 같이 함수로 정의해준다.
'DEV > Web 개발' 카테고리의 다른 글
Web 개발 :: 딥러닝 프로젝트 주제 선정 _TIL55 (0) | 2022.11.23 |
---|---|
Web 개발 :: DRF 리뷰, 파이썬 and와 &의 차이 _TIL#51 (0) | 2022.11.16 |
Web 개발 :: DRF 리뷰 _TIL#49 (0) | 2022.11.14 |
Web 개발 :: AWS 인스턴스에 도커(Docker) 셋팅 _TIL#48 (1) | 2022.11.11 |
Web 개발 :: Django DRF Test Code 활용하기 (1) | 2022.11.09 |
댓글