QnA
Rest Framework METHOD에 따른 호출 메소드
- GET : get >
list
orretrieve
- POST : post >
create
- DELETE : delete >
destroy
- PUT : put >
update
- PATCH : patch >
partial_update
폼
View로 구현
PostForm 사용
fields
content
photos
POST 요청을 받았을 때,
1. 해당 request.user를 author로 하는 Post 인스턴스 생성
2. 만약 form.cleaned_data['content']가 빈 값이 아니면 PostComment 인스턴스 생성
3. request.FILES.getlist('photos')를 loop하며 PostPhoto 인스턴스 생성
4. return redirect('post:post-list')
post/form.py
from django import forms
class PostForm(forms.Form):
content = forms.CharField(required=False)
photos = forms.ImageField(
widget=forms.ClearableFileInput(
attrs={
'multiple': True
}
)
)
post/ursl/views.py
urlpatterns = [
url(r'^create/$', views.PostCreate.as_view(), name='post-create'),
]
post/views/post.py
class PostCreate(View):
form_class = PostForm
template_name = 'post/post_create.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
context = {
'form': form
}
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
# print(request.POST)
# print(request.FILES)
author = request.user
post = Post.objects.create(author=author)
form = self.form_class(request.POST, request.FILES)
if form.is_valid():
content = form.cleaned_data.get('content', '').strip()
if content != '':
PostComment.objects.create(
post=post,
author=author,
content=content
)
for file in request.FILES.getlist('photos'):
PostPhoto.objects.create(
post=post,
photo=file
)
return redirect('post:post-list')
else:
return HttpResponse(form.errors)
FromView로 리팩토링
attributes
template_name
form_class
success_url
method
form_valid(self, form)
request는 self.request로 접근 가능
form.cleand_date는 바로 사용 가능
redirect는 정의해줄 필요 없음
post/views/post.py
class PostCreate(FormView):
form_class = PostForm
template_name = 'post/post_create.html'
success_url = '/post/'
def form_valid(self, form):
# print(form.cleaned_data)
# print(form)
author = self.request.user
post = Post.objects.create(author=author)
content = form.cleaned_data.get('content', '').strip()
photos = self.request.FILES.getlist('photos')
if content != '':
PostComment.objects.create(
post=post,
author=author,
content=content
)
for file in photos:
PostPhoto.objects.create(
post=post,
photo=file
)
return super().form_valid(form)a
PostDelete 구현
DeleteView로 구현
post/views/post.py
class PostDelete(DeleteView):
model = Post
success_url = reverse_lazy('post:post-list')
templates/post/include/post.html
post/urls/views.py
url(r'^(?P<pk>[0-9]+)/delete/$', views.PostDelete.as_view(), name='post-delete')
Javascript
프로젝트 설정
$ mkdir javascript
$ cd javascript
$ mkdir jquery-ajax
$ cd jquery-ajax
$ bower install jquery
$ bower install swiper
$ mkdir app
$ git init
$ cp ~/fastcampus/projects/django/instagram-api/.gitignore .
$ vi .gitignore
# app/static/css/ 추가
# css 폴더 추가 안되는 것 확인
# instagram-api/static 디렉토리를
# app/static 디렉토리로 복사
app/index.html 생성
base.html과 동일하게 만들기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>jQuery Ajax Test</title>
<link rel="stylesheet" href="static/css/normalize.css">
<link rel="stylesheet" href="static/css/sprites.min.css">
<link rel="stylesheet" href="static/css/base.min.css">
<link rel="stylesheet" href="static/css/common.min.css">
<link rel="stylesheet" href="static/css/header.min.css">
<link rel="stylesheet" href="static/css/post_list.min.css">
<!-- Swiper-->
<link rel="stylesheet" href="../bower_components/swiper/dist/css/swiper.min.css">
</head>
<body>
<div id=wrap>
<header class="main-header">
<nav class="header-nav">
<span class="nav-group nav-left">
<a href="" class="logo sprite-logo"></a>
</span>
<span class="nav-group nav-center">
<input type="text">
</span>
<span class="nav-group nav-right">
<a href="" class="btn-header-nav sprite-explore"></a>
<a href="" class="btn-header-nav sprite-activity"></a>
<a href="" class="btn-header-nav sprite-profile"></a>
</span>
</nav>
</header>
<div class="content-container">
</div>
</div>
<script src="../bower_components/jquery/dist/jquery.min.js"></script>
<script src="../bower_components/swiper/dist/js/swiper.jquery.min.js"></script>
</body>
</html>
서버
작은 서버를 띄어, index.html
이 정상적으로 동작하고 불러온 파일이 잘 로드되는지 확인
$ python -m http.server 8080
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /app/static/css/normalize.css HTTP/1.1" 200 -
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /app/static/css/sprites.min.css HTTP/1.1" 200 -
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /app/static/css/base.min.css HTTP/1.1" 200 -
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /app/static/css/common.min.css HTTP/1.1" 200 -
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /app/static/css/header.min.css HTTP/1.1" 200 -
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /app/static/css/post_list.min.css HTTP/1.1" 200 -
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /bower_components/swiper/dist/css/swiper.min.css HTTP/1.1" 200 -
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /bower_components/jquery/dist/jquery.min.js HTTP/1.1" 200 -
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /bower_components/swiper/dist/js/swiper.jquery.min.js HTTP/1.1" 200 -
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /app/static/images/sprites.png HTTP/1.1" 200 -
127.0.0.1 - - [21/Mar/2017 16:57:07] "GET /bower_components/swiper/dist/js/maps/swiper.jquery.min.js.map HTTP/1.1" 200 -
Ajax
app/index.html
<script>
$.ajax('http://localhost:8000/api/post/')
.done(function(data) {
console.log('done');
console.log(data);
})
.fail(function(data) {
console.log('fail');
console.log(data);
});
</script>
http://localhost:8080/app/에 접속하면 다음과 같이 실패한다.
fail
(index):48 Object {readyState: 0, status: 0, statusText: "error"}
HTTP 접근 제어 (CORS)때문에 에러가 발생한다. 에러를 해결하기 위해 복잡한 설정을 해줘야 하므로 프레임워크를 사용한다.
django-cors-headers
django-cors-headers는 새로운 미들웨어를 생성해서 작동하게 해준다. (장고는 미들웨어를 통해 request, response를 처리한다.)
$ pip install django-cors-headers
settings.py
INSTALLED_APPS = [
# ...
'rest_framework',
'corsheaders',
]
MIDDLEWARE = [
# 가장 위에 선언해줘야 한다.
'corsheaders.middleware.CorsMiddleware',
# ...
]
CORS_ORIGIN_WHITELIST = (
'localhost:8080',
)
http://localhost:8080/app/에 접속하면 성공한 것을 확인할 수 있다.
(index):43 done
(index):44 Object {next: "http://localhost:8000/api/post/?cursor=cD0yMDE3LTAzLTIxKzA2JTNBMDMlM0E1Ny40MTM4OTElMkIwMCUzQTAw", previous: null, results: Array[5]}
또한 크롬: 개발자 도구 > Network > XHR > Headers
에서 Access-Control-Allow-Origin:http://localhost:8080
이 등록되어 있는 것을 확인할 수 있다.
페이지네이션
utils/pagination.py
원활한 결과 확인을 위해 페이지네이션 수 조정
from rest_framework.pagination import CursorPagination
class PostPagination(CursorPagination):
page_size = 2
ordering = '-created_date'
app/index.html
다음 페이지를 바로 요쳥하기 위해 아래와 같이 수정한다.
<script>
$('#load-post-list').click(function(){
loadPostList();
})
// loadPostList ajax 요청의 response data에 'next'값이 있으면 해당 값을 할당할 변수
var next;
// PostList GET 요청 API 함수
function loadPostList() {
// 첫 loadPostList API 요청 URL
var url = 'http://localhost:8000/api/post/';
// next 값이 있다면 요청 url은 해당 값을 사용
if(next != undefined && next != null) {
url = next;
}
console.log(url)
$.ajax(url)
.done(function(data) {
// ajax 요청의 응답에서 next 값을 가져와 변수에 할당
next = data.next;
// 응답에 results를 results의 길이만큼 순회하며
for(var i=0; i < data.results.length; i++) {
// 매 loop마다 result[i]의 값을 curPost 변수에 할당
var curPost = data.results[i];
console.log(curPost.pk);
}
})
.fail(function(data) {
console.log('fail');
console.log(data);
});
}
</script>