실습
blog / models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
head_image = models.ImageField(
upload_to='blog/images/%Y/%m/%d/', blank=True)
file_upload = models.FileField(
upload_to='blog/files/%Y/%m/%d/', blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
def __str__(self):
return self.title
makemigrations, migrate
pip install djangorestframework
pip install django-cors-headers
pip install django-rest-auth
settings.py에 추가
authtoken 추가한 후, migrate 꼭 해야함
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# django lib app
'rest_framework',
'rest_framework.authtoken', # migrate를 해서 token을 생성하게 해야 합니다! 그 다음 가입부터 token생성!
'rest_auth',
'corsheaders',
# custom app
'blog',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', #최상단 추가
]
CORS_ORIGIN_ALLOW_ALL=True
CORS_ALLOW_CREDENTIALS=True
blog / serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
blog / views.py
from django.shortcuts import render
from .models import Post
from rest_framework.views import APIView
from .serializers import PostSerializer
from rest_framework.response import Response
class PostListAPIView(APIView):
def get(self, request): # get요청일때
post_list = Post.objects.all()
serializer = PostSerializer(post_list, many=True)
return Response(serializer.data)
def post(self, request):
serializer = PostSerializer(data=request.data) # request의 데이터를 전달
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
postlist = PostListAPIView.as_view()
127.0.0.1:8000/blog 가 정상적으로 나오는지 확인
인증된 사용자만 보게하려면 permission_classes를 이용한다
blog / views.py
from django.shortcuts import render
from .models import Post
from rest_framework.views import APIView
from .serializers import PostSerializer
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
class PostListAPIView(APIView):
permission_classes = [IsAuthenticated] # 이 API뷰에 대한 접근 권한 설정. 로그인된 사용자만 접근가능
def get(self, request): # get요청일때
post_list = Post.objects.all()
serializer = PostSerializer(post_list, many=True)
return Response(serializer.data)
def post(self, request):
serializer = PostSerializer(data=request.data) # request의 데이터를 전달
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
postlist = PostListAPIView.as_view()
회원가입, 로그인 등 만들어보기
settings.py에 추가
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
}
blog / serializers.py 수정
from rest_framework import serializers
from .models import Post
from rest_framework.authtoken.models import Token # 토큰 모델 Token.objects.get()이런식으로 토큰 확인 가능
from rest_framework.validators import UniqueValidator # 중복 검사(회원 가입할 때 동일한 아이디가 있는지 검사 등)
from django.contrib.auth.password_validation import validate_password # 비밀번호 유효성 검사
from django.contrib.auth.models import User # User 모델(기본 User모델 사용시 사용자명, 비밀번호, 이메일 필드만 사용 가능 => 상속받아 커스터마이징 가능)
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
class RegisterSerializer(serializers.ModelSerializer):
username = serializers.CharField(
required = True,
validator = [UniqueValidator(queryset=User.objects.all())] # 중복 검사
)
email = serializers.EmailField(
required = True,
validators = [UniqueValidator(queryset=User.objects.all())] # 중복 검사
)
password = serializers.CharField(
write_only = True, # 쓰기 전용
required = True,
validators = [validate_password] # 비밀번호 유효성 검사
)
password2 = serializers.CharField( # 비밀번호 확인 필드
write_only = True,
required = True
)
class Meta:
model = User
fields = '__all__'
def validate(self, attrs): # 직렬화되기전 데이터 검사. attrs : Serializer에 제출된 데이터
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({'password': '비밀번호가 일치하지 않습니다.'})
return attrs
def create(self, validated_data): # 직렬화된 데이터를 기반으로 새로운 객체를 생성. validated_data : 유효성 검사를 마친 데이터
user = User.objects.create(
username = validated_data['username'],
email = validated_data['email']
)
user.set_password(validated_data['password'])
user.save()
Token.objects.create(user=user)
return user
blog / views.py
RegisterView 추가
from django.shortcuts import render
from .models import Post
from rest_framework.views import APIView
from .serializers import PostSerializer, RegisterSerializer
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import CreateAPIView
from django.contrib.auth.models import User
class PostListAPIView(APIView):
permission_classes = [IsAuthenticated] # 이 API뷰에 대한 접근 권한 설정. 로그인된 사용자만 접근가능
def get(self, request): # get요청일때
post_list = Post.objects.all()
serializer = PostSerializer(post_list, many=True)
return Response(serializer.data)
def post(self, request):
serializer = PostSerializer(data=request.data) # request의 데이터를 전달
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
postlist = PostListAPIView.as_view()
class RegisterView(CreateAPIView): # post요청을 받아서 새로운 객체를 생성
queryset = User.objects.all()
serializer_class = RegisterSerializer
userregister = RegisterView.as_view()
blog / urls.py에 urlpatterns추가한 후,
Django와 구분되는 다른 폴더에 html파일 만들기 (다른 서버)
register.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>register</title>
</head>
<body>
<form action="http://127.0.0.1:8000/blog/register/" method="post">
유저이름 : <input type="text" name="username"><br>
이메일 : <input type="text" name="email"><br>
패스워드 : <input type="password" name="password"><br>
패스워드2 : <input type="password" name="password2"><br>
<input type="submit" value="회원가입">
</form>
</body>
</html>
Live Server로 켜서 회원가입되는지 확인, admin으로 접속해서 DB에 잘 들어갔는지 확인
로그인 만들기
blog / serializers.py
LoginSerializer 추가
from rest_framework import serializers
from .models import Post
from rest_framework.authtoken.models import Token # 토큰 모델 Token.objects.get()이런식으로 토큰 확인 가능
from rest_framework.validators import UniqueValidator # 중복 검사(회원 가입할 때 동일한 아이디가 있는지 검사 등)
from django.contrib.auth.password_validation import validate_password # 비밀번호 유효성 검사
from django.contrib.auth.models import User # User 모델(기본 User모델 사용시 사용자명, 비밀번호, 이메일 필드만 사용 가능 => 상속받아 커스터마이징 가능)
from django.contrib.auth import authenticate
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
class RegisterSerializer(serializers.ModelSerializer):
username = serializers.CharField(
required = True,
validators = [UniqueValidator(queryset=User.objects.all())] # 중복 검사
)
email = serializers.EmailField(
required = True,
validators = [UniqueValidator(queryset=User.objects.all())] # 중복 검사
)
password = serializers.CharField(
write_only = True, # 쓰기 전용
required = True,
validators = [validate_password] # 비밀번호 유효성 검사
)
password2 = serializers.CharField( # 비밀번호 확인 필드
write_only = True,
required = True
)
class Meta:
model = User
fields = '__all__'
def validate(self, attrs): # 직렬화되기전 데이터 검사. attrs : Serializer에 제출된 데이터
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({'password': '비밀번호가 일치하지 않습니다.'})
return attrs
def create(self, validated_data): # 직렬화된 데이터를 기반으로 새로운 객체를 생성. validated_data : 유효성 검사를 마친 데이터
user = User.objects.create(
username = validated_data['username'],
email = validated_data['email']
)
user.set_password(validated_data['password'])
user.save()
Token.objects.create(user=user)
return user
class LoginSerializer(serializers.ModelSerializer):
username = serializers.CharField(required=True)
password = serializers.CharField(write_only=True, required=True)
class Meta:
model = User
fields = ['username', 'password'] # 로그인 시 아이디와 비밀번호만 필요
def validate(self, data):
user = authenticate(**data) # 직렬화되기전 data로 사용자를 인증햇 인증에 성공한경우 사용자 객체를 반환.
if user: # 사용자 인증이 됐으면 사용자 토큰 반환
token = Token.objects.get(user=user)
return token
raise serializers.ValidationError("유효하지 않은 로그인입니다.")
blog / views.py
LoginView 추가
from django.shortcuts import render
from .models import Post
from rest_framework.views import APIView
from .serializers import PostSerializer, RegisterSerializer, LoginSerializer
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import CreateAPIView, GenericAPIView
from django.contrib.auth.models import User
class PostListAPIView(APIView):
permission_classes = [IsAuthenticated] # 이 API뷰에 대한 접근 권한 설정. 로그인된 사용자만 접근가능
def get(self, request): # get요청일때
post_list = Post.objects.all()
serializer = PostSerializer(post_list, many=True)
return Response(serializer.data)
def post(self, request):
serializer = PostSerializer(data=request.data) # request의 데이터를 전달
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
postlist = PostListAPIView.as_view()
class RegisterView(CreateAPIView): # post요청을 받아서 새로운 객체를 생성
queryset = User.objects.all()
serializer_class = RegisterSerializer
userregister = RegisterView.as_view()
class LoginView(GenericAPIView):
serializer_class = LoginSerializer
def post(self, request):
serializer = LoginSerializer(data=request.data)
if serializer.is_valid():
token = serializer.validated_data
return Response({'token': token.key})
return Response(serializer.errors)
userlogin = LoginView.as_view()
blog / urls.py에 패턴 추가,
다른 폴더에 login.html 파일 만들기
login.html
폼의 제출을 js로 가로채서 json형식으로 fetch를 이용해서 post보내기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>login</title>
</head>
<body>
<form action="http://127.0.0.1:8000/blog/login/" method="post">
유저이름 : <input type="text" name="username"><br>
패스워드 : <input type="password" name="password"><br>
<input id="login" type="submit" value="로그인">
</form>
<script>
const login = document.querySelector('#login');
login.addEventListener('click', (e) => {
e.preventDefault(); // submit의 기본동작을 막는다.
const username = document.querySelector('input[name="username"]').value;
const password = document.querySelector('input[name="password"]').value;
const data = {
username: username,
password: password
}
console.log(data)
// fetch를 이용해서 서버에 POST 요청을 보낸다.
fetch('http://127.0.0.1:8000/blog/login/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
console.log(data)
})
})
</script>
</body>
</html>
write.html
글쓰기 까지 구현.
로그인시 로컬스토리지에 유저토큰정보를 저장하고, 글쓸때는 로컬스토리지에서 유저토큰을 가져와서 같이 보낸다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>login</title>
</head>
<body>
<form action="http://127.0.0.1:8000/blog/login/" method="post">
유저이름 : <input type="text" name="username"><br>
패스워드 : <input type="password" name="password"><br>
<input id="login" type="submit" value="로그인">
</form>
<form action="" method="post">
title: <input type="text" name="title">
content: <input type="text" name="content">
<input id="write" type="submit" value="게시물작성">
</form>
<script>
const login = document.querySelector('#login');
const write = document.querySelector('#write');
login.addEventListener('click', (e) => {
e.preventDefault(); // submit의 기본동작을 막는다.
const username = document.querySelector('input[name="username"]').value;
const password = document.querySelector('input[name="password"]').value;
const data = {
username: username,
password: password
}
console.log(data)
// fetch를 이용해서 서버에 POST 요청을 보낸다.
fetch('http://127.0.0.1:8000/blog/login/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
console.log(data)
localStorage.setItem('token', data.token)
})
})
write.addEventListener('click', (e) => {
e.preventDefault(); // submit의 기본동작을 막는다.
const title = document.querySelector('input[name="title"]').value;
const content = document.querySelector('input[name="content"]').value;
const data = {
title: title,
content: content
}
console.log(data)
const token = localStorage.getItem('token')
if (token){
// fetch를 이용해서 서버에 POST 요청을 보낸다.
fetch('http://127.0.0.1:8000/blog/', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
console.log(data)
})
} else {
alert('로그인이 필요합니다.')
}
})
</script>
</body>
</html>
하지만 이렇게 하면 제대로 글이 올라가지 않는다.
인증을 세션기반이 아니라 토큰기반으로 바꿔줘야한다.
blog / views.py
from django.shortcuts import render
from .models import Post
from rest_framework.views import APIView
from .serializers import PostSerializer, RegisterSerializer, LoginSerializer
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import CreateAPIView, GenericAPIView
from django.contrib.auth.models import User
from rest_framework.authentication import TokenAuthentication
from rest_framework.authtoken.models import Token
class PostListAPIView(APIView):
# permission_classes = [IsAuthenticated] # 이 API뷰에 대한 접근 권한 설정. 로그인된 사용자만 접근가능
authentication_classes = [TokenAuthentication] # 토큰으로 확인
def get(self, request): # get요청일때
post_list = Post.objects.all()
serializer = PostSerializer(post_list, many=True)
return Response(serializer.data)
def post(self, request):
# request에 headers에 있는 Authorization: Bearer ${token}로 넘어온 토큰 확인하여 post 처리
print(request.headers)
print(request.headers['Authorization'])
print(request.headers['Authorization'].split(' ')[1])
token = request.headers.get('Authorization', None)
print(token)
if token:
print('토큰 존재!')
try:
token_key = token.split()[1]
# 유효한 토근인지 확인합니다. 아래 코드에서 token이 유효하지 않으면 애러 발생하면 except로 넘어갑니다.
token = Token.objects.get(key=token_key)
print('사용자:', token.user.username)
except:
print('토큰이 유효하지 않습니다.')
return Response({'error':'에러야!!'}, status=400)
serializer = PostSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
postlist = PostListAPIView.as_view()
class RegisterView(CreateAPIView): # post요청을 받아서 새로운 객체를 생성
queryset = User.objects.all()
serializer_class = RegisterSerializer
userregister = RegisterView.as_view()
class LoginView(GenericAPIView):
serializer_class = LoginSerializer
def post(self, request):
serializer = LoginSerializer(data=request.data)
if serializer.is_valid():
token = serializer.validated_data
return Response({'token': token.key})
return Response(serializer.errors)
userlogin = LoginView.as_view()
이런식으로 Django서버와 외부 FE 서버를 분리해서 DRF를 사용할 수 있다.
토큰 개념은 너무 어려우니까 나중에 다시 한번 보는걸로..
'Django > DRF' 카테고리의 다른 글
Django 프로젝트(3) - 📖 School Talks (2) | 2024.01.03 |
---|---|
Django 프로젝트(2) - 🎓 AI 지식인 서비스 (0) | 2023.12.02 |
DRF viewset을 이용한 serializers, authenticated 실습 (1) | 2023.11.20 |
JWT (JSON Web Token) (0) | 2023.11.17 |
DRF 와 마이크로서비스 튜토리얼 (1) | 2023.10.18 |