이후에 views에서 return super가 나오게 되면 자동적으로 absolute_url 이 실행
/photo/models.py
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
# Create your models here.classPhoto(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user')
#장고에서 구현하는 user 불러와 photo를 fk로 연결
text = models.TextField(blank=True)
image = models.ImageField(upload_to = 'timeline_photo/%Y/%m/%d')
#timeline_photo 폴더에 연, 월, 일 만들어 사진 저장
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def__str__(self):return"text : " + self.text
#admin 사이트 화면 표시 구현 classMeta:
ordering = ['-created']
#ordering 정렬defget_absolute_url(self):return reverse('photo:detail', args = [self.id])
#상세페이지로 이동하도록 absolute_url 설정, views 에서 return super가 나오게되면 absolute가 실행
from django.shortcuts import render
from django.views.generic.listimport ListView
from django.views.generic.edit import UpdateView, CreateView, DeleteView
from django.views.generic.detail import DetailView
from .models import Photo
# class형 뷰의 generic view를 이용하여 구현# ListView/CreateView/UpdateView/DeleteView/DetailView 구현classPhotoList(ListView):
model = Photo
template_name_suffix = '_list'#PhotoList:가장 메인에서 보여줄 로직 / 모델을 불러와서 데이터를 활용할 것이라고 기제classPhotoCreate(CreateView):
model = Photo
fields = ['text', 'image']
template_name_suffix = '_create'
success_url = '/'# PhotoCreate:# 모델을 활용하는데생성할 때 채워야 할 필드 확인이후 연결될 템플릿 이름은 Photo_create 일 것이다.#성공하면 메인 페이지로 돌아가도록 연결(이후 url로 연결)classPhotoUpdate(UpdateView):
model = Photo
fields = ['text', 'image']
template_name_suffix = '_update'
success_url = '/'#photoUpdate : Create와 동일classPhotoDelete(DeleteView):
model = Photo
template_name_suffix = '_delete'
success_url = '/'classPhotoDetail(DetailView):
model = Photo
template_name_suffix = '_detail'#PhotoDelete와 PhotoDetail#삭제와 상세페이지는 특별한 로직이 필요하지 않음#템플릿과 연결을 잘 시킬 수 있도록
<URL 연결하기>
- Photo URL 연결하기
photo의 urls.py 생성 ( instagram/photo/urls.py)
URL 작성하기
from django.urls import path
from .views import PhotoList, PhotoDelete, PhotoDetail, PhotoUpdate, PhotoCreate
app_name = "photo"#app_name 설정을 통해 namespace(이름공간)확보#다른 앱들과 url pattern 이름이 겹치는 것을 방지하기 위해 사용한다.
urlpatterns = [
path("create/", PhotoCreate.as_view(), name = 'create'),
path("delete/<int:pk>/", PhotoDelete.as_view(), name = 'delete'),
path("update/<int:pk>/", PhotoUpdate.as_view(), name = 'update'),
path("detail/<int:pk>/", PhotoDetail.as_view(), name = 'detail'),
path("", PhotoList.as_view(), name = 'index'),
]
- Config URL 연결하기
config의 url.py에 작성(instagram/config/urls.py)
2차 URL을 설정한 이후에 가장 기본이 되는 URL과 연결시켜준다.
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('photo.urls')),
]
-> 사용자가 게시판의 전체 글 목록 요청 시 / 수 많은 데이터들을 한번에 전달 한다면 데이터를 받는 사용자는 많은 데이터를 감당할 수 없을 것
API 서버에서 Pagination을 사용하는 이유
- > 하나의 Request 만으로 처리하기 어려운 레코드들을 여러 Request로 나누어 전송하기 위해 사용
DRF에서의 Pagination
PageNumberPagination
LimitOffsetPagination
CursorPagination
CustomizedPagination
1, 2, 3 = Django restFramework 에서 제공하는 Pagination class
4 = 직접 Pagination class를 직접 커스터마이징 해서 사용하는 방법
흔히, PageNumberPagination 을 Default로 설정하고
추가적인 페이지 설정 부분만 CustomizedPagination 을 사용
프로젝트 기본 세팅
1. Django Project 생성
2. 경로 이동 및 앱 생성
3. settings.py에 앱 등록
4. models.py 작성 및 admin.py 연결
5. makemigrations 및 migrate 진행
6. serializer.py 생성 및 작성
from .models import Post
from rest_framework import serializers
classPostSerializer(serializers.ModelSerializer):classMeta:
model = Post
fields = "__all__"
7. views.py 작성
from .models import Post
from .serializer import PostSerializer
from rest_framework import viewsets
classPostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
8. urls.py 작성
- 앱 내부의 urls.py
from rest_framework.routers import DefaultRouter
from django.urls import include, path
from .views import PostViewSet
router = DefaultRouter()
router.register('post', PostViewSet)
urlpatterns = [
path('', include(router.urls))
]
- 프로젝트 폴더의 urls.py
from django.contrib import admin
from django.urls import path, include
import post.urls
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(post.urls)),
]
DRF로 Pagination 구현하기
DEFAULT_PAGINATION_CLASS는 None이며 PAGE_SIZE 또한 NONE이다.
settings.py에 Pagination관련 설정하기
- DEFAULT_PAGINATION_CLASS에 Pagination 클래스를 설정하고, PAGE_SIZE에 한 번에 가져올 레코드의 수 설정
아래와 같이 한 번에 10개씩의 레코드만 가져오는 것을 확인할 수 있다. 총 데이터의 개수인 count와 이전 10개의 레코드 주소인 previous 다음 10개의 레코드를 위한 next또한 RESPONSE에 생겼다.
유의 사항
Pagination을 사용할 때는 반드시 레코드를정렬해 사용해야 한다. => views.py를 아래와 같이id로정렬하도록 수정해준다.
from .models import Post
from .serializer import PostSerializer
from rest_framework import viewsets
classPostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all().order_by('id')
serializer_class = PostSerializer
CustomizedPagination 클래스 작성하기
PageNumberPagination을 상속받는 클래스를 선언하고 클래스 내부에page_size변수에 원하는 레코드의 수를 지정한 후 적용할 모델의ViewSet의pagination_class에 클래스를 지정하면 된다.
classPhoto(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user')
#장고에서 구현하는 user 불러와 photo를 fk로 연결
text = models.TextField(blank=True)
image = models.ImageField(upload_to = 'timeline_photo/%Y/%m/%d')
#timeline_photo 폴더에 연, 월, 일 만들어 사진 저장
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def__str__(self):return"text : " + self.text
#admin 사이트 화면 표시 구현 classMeta:
ordering = ['-created']
#ordering 정렬
-settings.py 에서 media 루트 설정 (/instagram/config/settings.py)
STATIC_URL = '/static/'
MEDIA_URL = '/res/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
#MEDIA_URL 을 설정하고 ROOT를 잡아주어서 저기로 사진이 저장되도록 한다.
-모델 설계 이후 migration
Image파일을 업로드 하기 위해 pip install Pillow를 해줘야 한다.
pip install Pillow
python manage.py makemigrations photo
python manage.py migrate
-admin site 등록(/instagram/photo/admin.py)
from django.contrib import admin
from .models import Photo
# Register your models here.
admin.site.register(Photo)
- 확인해보기
$ python manage.py createsuperuser
Username (leave blank to use 'user'): admin
Email address: 282532@naver.com
Password:
Password (again):
This password is too short. It must contain at least 8 characters.
This password is too common.
This password is entirely numeric.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
from rest_framework import viewsets
from .models import Post
from .serializer import PostSerializer
classPostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class =
ViewSet
-> View(CRUD)를 설계하는 가장 간단한 방법
2. APIView
-> 장점 : 자신의 마음대로 코드를 작성할 수 있다.
-> 단점 : CBV의 장점인 중복되는 코드 제거를 하지 못한다.
-> Response : 클라이언트가 요청한 형태로 콘텐츠를 렌더링 해준다.
-> Status : Http 에서 제공하는 상태 코드는 숫자이지만, REST 프레임워크에서는 문자 형태의 식별자를 제공하여 상태를 좀더 잘 구분할 수 있도록 한다.
-> APIView를 상속해서 view를 설계할 때는 status 와 Response를 import 하여 Response과정을 작성
-> APIView를 상속해서 만든 CBV의 내부 함수들은 필요로 하는 HTTP Method 이름으로 갖는다.
-> 어떠한 status를 받고 response를 전달할지 직접 정하는 것이 apiview를 상속받은 cbv
classPost(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
serializer.py 생성 및 작성
from .models import Post
from rest_framework import serializers
classPostSerializer(serializers.ModelSerializer):classMeta:
model = Post
fields = "__all__"
views.py 작성 1) 필요한 모듈 추가(apiview를 상속받은 cbv를 작성하기 위해 아래 모듈 추가)
from CBV.models import Post
from CBV.serializer import PostSerializer
from django.http import Http404
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView
2)객체들 목록 가져오는 PostList 작성
- post 메서드는 다수의 post 객체를 반환하는 메서드
- 다수의 객체를 직렬화 하기위해서는 PostSerializer 에 many = True를 넘겨야 한다
- post 메서드는 새로운 객세 생성하는 함수
- 저장 성공시 상태코드 201(created) / 반환 실패시 400(bad request)반환
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views
urlpatterns = [
path("post", views.PostList.as_view()),
path("post/<int:pk>", views.PostDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
- 프로젝트 폴더 내부의 urls.py 에 include
from django.contrib import admin
from django.urls import path, include
import post.urls
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(post.urls)),
]
실행하고 http://127.0.0.1:8000/post 에 접속하면 정상 작동한다
4. Generic CBV
<분류>
기반 뷰(Base View): 뷰 클래스를 생성하고 다른, 제너릭 뷰의 부모 클래스가 되는 기본 제너릭 뷰
view : 최상위 부모 제너릭 뷰 클래스
TemplateView : 주어진 템플릿으로 렌더링
RedirectView : 주어진 URL로 리다이렉트
제너릭 보기 뷰(Generic Display View): 객체의 목록 또는 하나의 객체 상세 정보를 보여주는 뷰
DetailView : 조건에 맞는 하나의 객체 출력
ListView : 조건에 맞는 객체 목록 출력
제너릭 수정 뷰(Generic Edit View): 폼을 통해 객체를 생성, 수정, 삭제하는 기능을 제공하는 뷰
FormView : 폼이 주어지면 해당 폼 출력
CreateView : 객체 생성하는 폼 출력
UpdateView : 기존 객체 수정하는 폼 출력
DeleteView : 기존 객체 삭제하는 폼 출력
제너릭 날짜 뷰(Generic Date View): 날짜 기반 객체의 연/월/일 페이지로 구분해 보여주는 뷰
YearArchiveView : 주어진 연도에 해당하는 객체 출력
MonthArchiveView : 주어진 월에 해당하는 객체 출력
DayArchiveView : 주어진 날짜에 해당하는 객체 출력
TodayArchiveVIew : 오늘 날짜에 해당하는 객체 출력
DateDetailView : 주어진 연, 월, 일 PK(OR 슬러그)에 해당하는 객체 출력
<제너릭 뷰 오버라이딩>
속성 변수 오버라이딩
model : 기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다.
queryset : 기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다.queryset을 사용하면model속성은 무시된다.
template_name : TemplateView를 포함한 모든 제너릭 뷰에서 사용한다. 템플릿 파일명을 문자열로 지정한다.
context_object_name : 뷰에서 템플릿 파일에 전달하는 컨텍스트 변수명을 지정한다.
paginate_by : ListView와 날짜 기반 뷰(예, YearArchiveView)에서 사용한다. 페이징 기능이 활성화 된 경우 페이지당 출력 항목 수를 정수로 지정한다.
date_field : 날짜 기반 뷰(예, YearArchiveView)에서 사용한다. 이 필드의 타입은 DateField 또는 DateTimeField이다.
form_class : FormView, CreateView, UpdateView에서 폼을 만드는데 사용할 클래스를 지정한다.
success_url : FormView, CreateView, UpdateView, DeleteView에서 폼에 대한 처리가 성공한 후 리디이렉트할 URL 주소이다.
메소드 오버라이딩
def get_queryset() : 기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다. 디폴트는queryset속성을 반환한다.queryset속성이 지정되지 않은 경우 모델 매니저 클래스의 all() 메소드를 호출해 QuerySet 객체를 생성해 반환한다.
def get_context_data(**kwargs) : 뷰에서 템플릿 파일에 넘겨주는 컨텍스트 데이터를 추가하거나 변경하는 목적으로 오버라이딩한다.
def form_valid(form)
<모델을 지정하는 방법 3가지>
model 속성 변수 지정
queryset 속성 변수 지정
def get_queryset() 메소드 오버라이딩
##실습
1. views.py 작성하기 전
django-admin startproject <project-name>
cd <project-name>
python manage.py startapp <app-name>
settings.py에 App, rest_framework추가
models.py에 모델 작성 및 migrate
serializer.py생성 및 작성
2. views.py 작성하기
1) 필요한 모듈 추가하기
- 사용할 모델, serializer , rest_framework 의 generics 추가
from .models import Post
from .serializer import PostSerializer
from rest_framework import generics
2) PostList 클래스 작성하기
- generics의 ListCreateAPIView 는 mixins에서 사용한 list 함수와 create 함수를 가지고 있는 class 이다.
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views
urlpatterns = [
path("post", views.PostList.as_view()),
path("post/<int:pk>", views.PostDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
- 프로젝트 폴더 내부의 urls.py 에 include
from django.contrib import admin
from django.urls import path, include
import post.urls
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(post.urls)),
]
-> 실행 결과 : http://127.0.0.1:8000/post로 접속하면 잘 작동하는 것을 확인할 수 있다.
5. ViewSet
rest_framework의 viewsets.py내부에는 4개의 클래스가 존재한다.
ViewSet
GenericViewSet
ReadOnlyModelViewSet
ModelViewSet
모두 간단하게 구성되어 mixins와 GenricViewSet, APIView등을 묶는 역할을 한다.
ReadOnlyModelViewSet
- 상속 받은 클래스
mixins.RetrieveModelMixin
mixins.ListModelMixin
GenericViewSet
상속받은 클래스로 보아 해당 viewset 역할은 retrieve 함수와 list 함수 기능을 가지고 있다
- 기능 : 특정 객체 가져오는 역할(retrieve) / 객체 목록을 가져오는 역할(list)
ReadOnly를 수행하는 viewSet 이다
아래와 같이 작성하면
기존 postlist, postDetail 클래스의 기능을 따로 작성하지 않아도 ReadOnlyModelViewSet을 상속받아 사용할 수 있다.
from rest_framework import viewsets
from .models import Post
from .serializer import PostSerializer
classPostViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Post.objects.all()
serializer_class =
-> customAPI 함수는 default로 GET 메서드로 처리된다. / 다른 메서드로 처리하고 싶다면 action의 format 인자로 지정 가능
%action decorator
@는 decorator, 장식자라고 부른다. ViewSet을 이용하면 CRUD는 간단하게 구현되나 다른 논리들을 구현할 때 @action를 사용한다. 개발자가 임의로 View를 설계할 수 있도록 해준다.
%renderer_classes
Response를 어떤 형식으로 Rendering시킬 것인가를 정의
renderer.JSONRenderer
renderer.BrowsableAPIRenderer
renderer.StaticHTMLRenderer
TemplateHTMLRenderer
등 여러가지가 있다. 기본 설정은 JSONRenderer와 BrowsableAPIRenderer다.
##실습해보기
viewSet 적용하기
1. views.py 작성하기 전
django-admin startproject <project-name>
cd <project-name>
python manage.py startapp <app-name>
settings.py에 App, rest_framework추가
models.py에 모델 작성 및 migrate
serializer.py생성 및 작성
2. views.py 작성하기
1) 필요한 모듈 추가
from .models import Post
from .serializer import PostSerializer
from rest_framework import viewsets
from rest_framework import renderers
from rest_framework.decorators import action
from django.http import HttpResponse
2) ReadOnlyModelViewSet을 상속해 클래스 작성하기
- viewsets의 ReadOnlyModelViewSet을 상속받아 작성한다. ReadOnlyModelViewSet을 상속받았으므로 읽기만 가능하다.
-> 앱 폴더 내부에 urls.py 생성(계층적 url 관리 위해 분할) ##아니 나는 urls.py를 완성하지 못하면 마이그레이트가 안된다.. 이거먼저!
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter() #ROUTER 객체 생성
router.register('post', views.PostViewSet) #ROUTER 객체의 REGISTER 함수(첫번째 인자 = URL, 함수)
urlpatterns = [
path('', include(router.urls))
]
#rest_framework는 ROUTER을 사용해 URL을 결정한다#CRUD 기반으로 URL을 설정한다
-> 프로젝트 폴더 urls.py 에 모듈 추가
from django.contrib import admin
from django.urls import path, include
import post.urls
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(post.urls)),
]
앱 폴더의 models.py 에 모델 클래스 정의 (post/models.py)
from django.db import models
classPost(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
# Create your models here.
앱 폴더 내부에 serializer.py 생성 및 작성 (어떤 클래스의 어떤 필드를 사용할 것인지)
from .models import Post
from rest_framework import serializers
classPostSerializer(serializers.ModelSerializer):classMeta:
model = Post
fields = ['title', 'body']
# 모든 필드를 사용할 경우 '__all__' 사용 가능
views.py 작성하기
-> rest_framework 의 viewsets 모듈과 작성한 모델과 serializer 추가
from rest_framework import viewsets
from .models import Post
from .serializer import PostSerializer
classPostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
Django Rest Framework는 Class base View 이기에
view로서 class를 작성한다
viewsets의 ModelViewSet을 상속받는다.
##ROUTER
▪ ViewSet에는 여러가지 메소드(함수)들이 있다
ex) retrieve() , destroy(), …
▪ 이 모든 것을 하나의 path()로 처리해 주는 것은 불가능하다.
▪ Path함수를 묶어준다 == path함수의 두번째 인자로 오는 함수를 묶는다
-> Path(요청을 처리할 url, 요청을 인자로 받아 처리할 함수, namespace)
#실행 결과(api 서버 보기)
1. api root : urls.py 에 작성된 DefaultRouter 에 의해 만들어진 기본적인 환경 / 처음 보여지는 화면은 GET 요청을 할 수 있는 URL이 보여진다.
2.POST API
-request : 127.0.0.1/8000/post
-response
post 후 post/1로 들어가면 allow 부분에 put이 들어간 것을 볼 수 있음
각 객체에 pk 값 부여하기
-> serializer.py 파일의 PostSerializer 을 수정한다
classPostSerializer(serializers.ModelSerializer):classMeta:
model = Post
fields = ['id', 'title', 'body']
새로 고침 후 보면 id 값을 직접 확인할 수 있는 것을 볼 수 있다.
id 값 : 각 인스턴스에 붙는 고유 번호
Read Only 설정하기
-> serializer.py 파일의 PostSerializer을 수정한다
-> read_only_fields 에 read_only 속성을 적용할 필드를 tuple에 추가한다
-> 수정 시 read_only 부분이 true로 바뀌는 것을 볼 수 있다.
-> write_only부분도 같은 방법으로 수정하면된다!
classPostSerializer(serializers.ModelSerializer):classMeta:
model = Post
fields = "__all__"
read_only_fields = ("title",)