QnA

Rest Framework METHOD에 따른 호출 메소드

  • GET : get > list or retrieve
  • 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>

과제