-
[Django] DRF 시작하기공부/Django 2020. 8. 5. 11:46
가상환경에 DRF설치
pip install djangorestframwork
app에 등록
INSTALLED\_APPS = [ 'rest\_framework', #rest installed app에 등록 ]
serializer.py
model을 입력할 수 있는, 직렬화하는 곳
from .models import Post from rest_framework import serializer class PostSerializer(serializer.ModelSerializer): class Meta: model = Post # fields = '__all__' fields = ['id', 'title', 'body']
Q.
meta class
가 뭐징?
[https://wikidocs.net/21056]views.py
from rest_framework import viewsets from .models imprt Post from .serializer iport PostSerializer #CBV class PostViewSet(viewsets.ModelViewSet): queryset = Post
urls.py
from django.urls import path, include from rest_framework,routers import DefaultRouter from . import views $django rest framwork -> router -> url router - DefaultRouter() router.register('post', views.PostViewSet) urlpatterns = [ path('', include(router.urls)), ]
router에 의해서 만들어진 url로 기본이 되는 페이지로 감!
민멘토님 say : 장고 내부코드가 얼마나 많은거겠어요!!😮pk값 부여
from .models import Post from rest_framework import serializer class PostSerializer(serializer.ModelSerializer): class Meta: model = Post # fields = '__all__' fields = ['id', 'title', 'body'] #id 값 추가
모델에 추가하지 않고 serializer에만 했는데 된다고????????????😲
options
id값을 사람들이 마음대로 수정한다면 문제가 생기겠죠?
'read_only' = true
from .models import Post from rest_framework import serializer class PostSerializer(serializer.ModelSerializer): class Meta: model = Post # fields = '__all__' fields = ['id', 'title', 'body'] read_only_fields = ('title',) #쉼표 안쓰면 문자열로 인식하니까 꼭 쓰기 #write_only_fields 도 있음.
ViewSet
View(CRUD)를 설계하는 쉽고 간단한 방법
코드 복잡도
APIView > Mixins > Generic CBV > ViewSet🙄 왜 복잡한 걸 알아야하죠?
👉 간단한 것들이 복잡한 것을 상속하고 있습니다!
복잡한 것을 알아야 커스터마이징과 오류해결에 도움이 됩니다!내부코드 보면서 하면 좋아용
https://github.com/encode/django-rest-frameworkClass-based Views
https://www.django-rest-framework.org/tutorial/3-class-based-views/ 참고
코드의 재사용성이 높아진다 -> 클래스의 상속
views.py
from rest_framework.views import APIView
APIView를 상속받은 모델을 사용할 것이기 때문에 view에서도 APIView를 import한다.from rest_framework.response import Response from rest_framework import status
어떤 status인지에 따라서 우리가 직접 response를 만들어서 보내준다.
모델 내부 구성원의 메소드 이름을 http method로 한다.
class 내가_정의한_모델(APIView): def<내가_필요로_하는_HTTP_Method>: 내가 어떻게 처리할 지 정의
Postlist에 get과 post만 필요 근데 내 입맛대로 put과 delete를 정의하겠다!
처리될 수 있게끔 설계 가능가능🚫APIView의 문제
어떤 모델이든 이마~안큼 긴 비슷한 논리의 view코드가 작성될 거임!
list, detail 등등 비슷비슷시간낭비, 에너지 낭비, 코드낭비
👉 mixins 등장: 상속 가능!Mixins
불필요한 코드를 줄이기위해 상속을 많이 함!
- 쿼리셋
- serializer
- http method
- 한 줄짜리 return문
from snippets.models import Post from snippets.serializers import PostSerializer from rest_framework import mixins from rest_framework import generics class PostList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): queryset = Post.objects.all() #쿼리셋 초기화 serializer_class = PostSerializer #serializer 초기화 def get(self, request, *args, **kwargs): #http method return self.list(request, *args, **kwargs) #한 줄짜리 return문 def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
가변인자?
받은 인자의 갯수에 무관한 인자 = 인자의 길이가 얼마든 다 받겠다
args: 넌 키월드 알규먼트
kwargs: 키워드 알규먼트def get(self, request, format=None): snippets = Snippet.objects.all() serializer = SnippetSerializer(snippets, many=True) return Response(serializer.data)
👇 이렇게 간단하게!!
def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs)
list
의 내부코드를 보면 위 코드와 상당히 유사❗🚫Mixins의 문제
mixins에서도 또 한번 더 간단하게 만들자
Generic CBV
- 쿼리셋
- serializer
from snippets.models import Post from snippets.serializers import PostSerializer from rest_framework import generics class PostList(generics.ListCreateAPIView): queryset = Snippet.objects.all() serializer_class = SnippetSerializer class PostDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Snippet.objects.all() serializer_class = SnippetSerializer
ListCreateAPIView
의 내부코드를 예상해봅시다
쿼리셋과 직렬화를 제외하고 다 사라진 것을 보니 http method로 정의하던 함수들까지 다 포함하고 있을 것 같아요!김사원님 say: 와 그러면 그냥 쿼리셋, 직렬화만 하면 crud가 다 되는거네..
실제 내부 코드는 mixins에서 주렁주렁 상속하는 코드와 상당히 유사❗
🚫Mixins의 문제
하나의 클래스에서 listView와 detailView를 구현하게 하자.
ViewSet
- list(), create(), retrieve(), update(), destroy(), partial_update() 등의 함수들을 묶어줌.
ReadOnlyModelyViewSet
- 이 친구도 GenericViewSet을 상속받고 있다.
- 읽기 기능만 하는거 만들고 싶으면 ㄱㄱ
ModelViewSet
from rest_framework.decorators import action from rest_framework.response import Response # 내가 임으로 view를 만들겠어 class SnippetViewSet(viewsets.ModelViewSet): queryset = Snippet.objects.all() serializer_class = SnippetSerializer permission_classes = [permissions.IsAuthenticatedOrReadOnly,IsOwnerOrReadOnly] #권한 설정
@action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer]) def highlight(self, request, *args, **kwargs): snippet = self.get_object() return Response(snippet.highlighted) def perform_create(self, serializer): serializer.save(owner=self.request.user)
action decorator : Custum API 만들기
뷰셋을 이용하면 간편하게 씨얼유디가 구현되는 거 인정 그런데 다른 Logic들을 구현하고 싶을 때는,,,??
####나만의 Custum API는???내가 임의로 view를 설계하기!
@action(method=['post'])
같은 식으로 method 지정,아무것도 안쓰면 get으로 암묵적 지정class PostViewSet(viewsets.ModelViewSet): queryset = Post.objects.all() serializer_class = PostSerializer @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer]) # 그냥 아래 문자열을 띄우는 custom api def highlight(self, request, *args, **kwargs): return HttpResponse("상도 덮*고 맛있다. 근데 밥도둑 맛은 맵다.")
rendering
response를 어떤형식으로 Rendering 시킬것인가
Router
🤔ViewSet을 하나의 path 함수 만으로 처리 가능할까? NO!
서로 다른 두 개 이상의 path함수들을 묶어주는 과정이 필요하다.
path 함수를 묶어준다. == path 함수의 두 번째 인자(로 오는 함수)를 묶는다.
두번 쨰 인자로 오는 함수들
list(), create(), retrieve(), update(), destroy(), partial_update()하나하나 다 path함수 설계할 것을 한 번에 가능
path를 묶어주는 .as_view()
as_view({'http_method':'처리할 함수'})
mypath = PostViewSet.as_view({ 'get' : 'retrieve', 'put' : 'update', 'patch' : 'partial_update', 'delete' : 'destroy, }) router = DefaultRouter() router.register(r'Post', views.PostViewSet) router.register(r'users', views.PostViewSet) urlpatterns = [ path('', mypath) ]
페이지네이션
🤔 백에서 꾸미는 것도 아닌데 굳이 필요할까요?
물론 꾸밀 필요는 없죠! 하지만 페이지네이션은 필요합니다.
client : 게시글 이만큼 갖다줘!
server : 하나의 request만으로 처리하기 어렵다. 여러 번 나눠서 보내야지Filtering & Search
https://www.django-rest-framework.org/api-guide/filtering/ 참고
Filtering Serch request 걸러 보내기 response 걸러 받기 내가 보낸 http request 참조하기
내가 보낸 request : self.request
내가 보낸 request user :author이 User에 따라 달라질 것임 -> author를 기반으로 Filtering
class UserPostViewSet(viewsets.ModelViewSet): queryset = UserPost.object.all() serializer_class = Userserializer filter_backends = [SearchFilter] #어떤 필터 종류인지 백엔드 변수에 할당 search_fields = ('title',) #어떤 칼럼을 기반으로 검색할건지 -> 무조건 튜플 마지막 , 쓰기 def get_queryset(self): #이 곳 내부에서 쿼리셋 지지고 볶기 qs = super(),get_queryset() #.exclude 또는 .filter #qs = qs.filter(author__id = 3) #아무도 로그인하지 않은 상태에는? anonymous user 오류 발생 >> 예외처리 ㄱㄱ if self.request.user.is_authenticated: qs = qs.filter(author__id = self.request.user) # 지금 로그인한 유저의 글만 필터링 해라 else: qs = qs.none() return qs
Authentication
https://www.django-rest-framework.org/api-guide/authentication/ 참고
서비스를 이용하는 데에 있어 내가 어느 정도의 권한이 있을을 알려주는 과정BasicAuthentication
HTTP 자제 기본인증에 기반한 인증 방식
HTTP 제어 헤더로 넘긴 ID, PW를 base64 encoding정신 나간거 아니면 보안을 위해서 이거만 쓰지는 않아요!
TokenAuthentication
BasicAuthentication + 보안
SessionAuthentication
RemoteUserAuthentication
유저 정보가 다른 서비스에서 이용될 떄 사용
setting the Authentication
전역으로 설정 -> settings.py에서 rest framework 설정 추가
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ] }
view단 마다 설정 -> views.py에서 import
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
Permission
setting the Permission
전역으로 설정 -> settings.py에서 rest framework 설정 추가
REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ] }
view단 마다 설정 -> views.py에서 import
from rest_framework.permissions import IsAuthenticated
Permission Class 종류
AllowAny (default)
인증된 요청이든 비인증 요청이든 전부 허용하겠다.
IsAuthenticated
인증된 요청에 대해서만 view 호출을 허용하겠다.
IsAdiminUser
staff User에 대해서만 요청을 허용하겠다
User.is_staff == True
일 떄IsAuthenticatedOrReadOnly
비인층요청에 대해서는 읽기만 허용하겠다.
장고의 표준 라이브러리 기반 permission 클래스도 있음.
궁금하면 공식 문서 go
Token Authenticatation
- username, password와 1:! 매칭되는 고유 key 생성, 발급
- 발급받은 Token을 API요청에 담아 인증처리
다른 것과 달리 그냥 띡 쓴다고 적용되지 않음.
앱 추가 -> migrate까지 해야함
WHY? 유저 별로 1:1 매칭 field -> OneToOneField를 이용해 Token 발금Token 발급
유저 인스턴스가 생성된다고 자동으로 토큰이 생성되는 것이 아님
- ObtainAuthToken을 이용한 생성
- Python 명령어를 통한 생성
python manage.py drf_create_token <username>
python manage.py drf_create_token <username>
자동으로 되게 원한다면 signal의 post_save 사용
post_save: DB에 뭔가가 저장된 직후에 특정 동작 수행from django.db.models signals import post_save @receiver(post_save, sender=settings.AUTJ_USER_MODEL) def create_auth_token(sender, instance=None, created=False, **kwargs): if created: Token.object.create(user=instance)
Token 획득
token을 획득할 수 잇는 url path 지정
-> 그 url에 대해 POST 요청응ㄹ 보냄으로써 획득발급받은 Token을 API요청에 담아 인증처리
'공부 > Django' 카테고리의 다른 글
[Django] 작성한 글의 Search 기능 구현 (0) 2020.08.05 [Django] 내가 작성한 글만 Filtering (0) 2020.08.05 [Django] Json (0) 2020.08.04 [Django] REST framework (0) 2020.08.04 django.db.utils.OperationalError: no such table: auth_user 에러 (0) 2020.07.28