Django tutorial

Django

setup

  1. 가상환경 생성
python3 -m venv VenvDjango
cd VenvDjango/ && source bin/activate
  1. 프로젝트 디렉토리 파기
mkdir django-drf-react-quickstart && cd $_
  1. 장고 & 장고 rest framework 깔기, 프로젝트 시작
pip install django djangorestframework
django-admin startproject project

Django Application 만들기

Django는 많은 Application 로 구성되있다. 각 application은 이상적으로는 가지 일만 해야한다. Django application 들은 모듈화되있고 재사용가능하다. Post를 작성하고 리스팅하는 application을 만들 수 있다. 다른 프로젝트가 같은 앱을 필요로하면 난 해당 앱을 패키지 매니저로 부터 설치하면된다.

이전에 만든 startproject 를 통해 project라는 폴더가 생성되 있었을텐데, 해당 폴더에 들어가서

django-admin startapp app_name

아래 명령어로 application 을 생성해주도록 한다. 여기선 app_name을 leads라고 하겠다.

그럼 프로젝트 구조는 django-drf-react-quickstart 아래

  • project |— leads |— project |— manage.py

이 구조가 된다. 이때 project/settings.py에

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'leads', # 방금 생성한 app 을 추가한다
	]

이제 첫번째 모델을 추가해보자.

Django Model 만들기

첫번째 model을 만들 차례다. model은 테이블의 데이터를 표현하는 객체이다. 장고 뿐만 아니라 거의 모든 웹 프레임워크가 모델의 개념을 가지고 있다. 장고 모델은 1개 이상의 Field를 가지는데, Field는 테이블의 Column(열)을 의미한다.

예시 모델은 3개의 Field를 가진다고 해보자.

Django는 자동으로 created_at column을 추가해주지 않으므로 누락하지 않도록 한다.

leads/models.py를 열고 lead model을 작성한다.

from django.db import models
class Lead(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()
    message = models.CharField(max_length=300)
    created_at = models.DateTimeField(auto_now_add=True)

cf. 모델 field에 대한 자세한 documentation Use-case에 가장 적합한 필드를 고르도록한다.

모델을 작성했으니, migration 을 만들어본다.

python manage.py makemigrations leads

그리고 db에 migrate한다.

python manage.py migrate

Testing

테스팅을 위해 아래 패키지를 설치한다

pip install coverage
coverage run --source='.' manage.py test
// run converage
coverage html
// report 를 html 로 보고싶을때
coverage report
// command line에서 보고 싶을때

project/htmlcov/index.html을 browser에서 열어보면 뭘 테스트해야하는지 알수있을 것이다.

REST serializer

serialization은 객체를 어떤 데이터 포맷으로 변형시키는 것이다. 그렇게 하면 파일로 저장하거나 네트워크를 통해 보낼 수 있는 형태가 된다. Django에서도, 모델이 Python class이고 이를 브라우저에서 렌더링하기 위해 JSON으로 바꿔야하기 때문에 Django REST Serializer가 필요하다. 이는 JSON을 객체로 바꾸는 데에도 적용된다.

./leads/serializer.py라는 파일을 만들고, 아래와 같이 작성한다.

from rest_framework import serializers
from leads.models import Lead

class LeadSerializer(serializers.ModelSerializer):
	class Meta:
		model = Lead
		fields = ('id', 'name', 'email', 'message')

field를 한땀한땀 작성하지 않고,

class LeadSerializer(serializers.ModelSerializer):
	class Meta:
		model = Lead
		fields = '__all__'

이렇게 작성할 수도 있다. 이제 Views/Urls를 살펴보자

Controller가…아니고 Views

다른 프레임워크를 쓰다보면, Django에 Controller가 없다는 게 꽤 놀랍다. 보통 컨트롤러는 Request를 처리하고 Response를 내보내는 로직을 포함한다. 전통적인 MVC 아키텍쳐에서는 Model, View, Controller 세 개가 존재한다. Rails등이 있다. Django는 MVT 프레임워크인데, Model, View, Template이다. View가 Response/Request 라이프사이클을 관리한다. Django에는 Function View, Class based view, generic view 같이 다양한 종류의 View가 존재한다.

우리는 여기서 generic API views를 기준으로 실습을 진행한다.

generics api 내에는 조회와 생성을 위한 view가 존재하는데, 아래와 같은 식으로 사용하면 된다 ./leads/views.py 을 열고

from leads.models import Lead
from leads.serializers import LeadSerializer
from rest_framework import generics

class LeadListCreate(generics.ListCreateAPIView):
	queryset = Lead.objects.all()
	serializer_class = LeadSerializer

3줄만 추가했는데, GET/POST request를 handling하는 view를 만들었다

Route..가 아니고 urls

Rails나 다른 프레임워크를 썼던 사람들은 Django에 Route 설정이 없다는 것에 꽤 놀랄 수 있다. View와 url을 mapping하는 가장 쉬운 방법은 url mapping이다.

위에서 만들었던 LeadListCreate를 api/lead/와 묶어보자. 즉, 모델 조회와 생성시에 api/lead/로 GET/POST 요청을 날리고 싶다. URL mapping을 설정하기 위해서는 ./project/urls.py 을 연다.

from django.urls import path, include

urlpatterns = [
	path('', include('leads.urls'))
]

그리고 ./leads/urls.py로 가서

from django.urls import path
from . import views

urlpatterns = [
	path('api/lead/', views.LeadListCreate.as_view()),
]

이렇게 작성해준다.

마지막으로 설정의 INSTALLED_APPS 에서 아래와 같이 rest_framework를 추가해주면,

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'leads', 
    'rest_framework' # enable rest framework
]
python manage.py runserver

위 화면을 확인 가능하다.

DB seeding

Django fixture을 통해 DB에 데이터를 넣을 수 있다. Fixture는 frontend 데이터를 가지고 데모할 때 유용하게 쓸수 있다. ./leads/fixtures 라는 폴더를 만들고 leads.json라는 파일을 아래와 같이 작성한다.

[
    {
        "model": "leads.lead",
        "pk": 1,
        "fields": {
            "name": "Armin",
            "email": "something@gmail.com",
            "message": "I am looking for a Javascript mentor",
            "created_at": "2018-02-14 00:00:00"
        }
    },
    {
        "model": "leads.lead",
        "pk": 2,
        "fields": {
            "name": "Tom",
            "email": "tomsomething@gmail.com",
            "message": "I want to talk about a Python project",
            "created_at": "2018-01-14 00:00:00"
        }
    }
]

저장한 후 fixture를 불러온다

python manage.py loaddata leads

끝!

React Frontend

Django 와 React 를 어떻게 하면 잘 붙힐 수 있을까에 대해서는 여러가지 의견이 있다.

결과적으로는 얼마나 많은 Javascript가 필요한지에 따라 다르다. 보통은 3가지 패턴이 존재한다.

  1. React를 Django의 “frontend” app으로 가져간다: single HTML 템플릿을 load하고 React가 frontend를 관리하게 한다
  2. Django를 독립적인 Rest API로 두고, React도 독립적인 SPA로 둔다 (인증을 위해 JWT가 필요하다)
  3. 섞는다: 작은 React app 들을 Django 템플릿안에 넣는다

1번으로 가는 경우:

React를 Django에 가깝게 유지하는 것은 인증이나 다른 것들을 쉽게 해준다. Django 빌트인 인증을 이용해서 사용자들의 등록이나 로그인을 만들 수 있고, 빌트인 Session 인증을 통해 토큰이나 JWT을 걱정하지 않을 수 있다.

3번으로 가는 경우:

React 설정과 웹팩

1번으로 실습을 진행한다

Django와 React 조합의 장점은 API를 제공하는 Django Rest Framework이다. React는 “frontend”라는 개별적 app 으로 만들 것이다.

이전과 마찬가지로

django-admin startapp frontend

로 app을 만들어준다.

$HOME/django-drf-react-quickstart/project/ 밑에 frontend라는 디렉토리가 생겼을것이다.

mkdir -p ./frontend/src/components
mkdir -p ./frontend/{static,templates}/frontend
cd ..
// $HOME/django-drf-react-quickstart
npm init -y
// npm 환경 초기화
npm i webpack webpack-cli --save-dev

webpack과 webpack-cli를 설치한 후 package.json에 scripts를 아래와 같이 설정한다

"scripts": {
  "dev": "webpack --mode development ./project/frontend/src/index.js --output ./project/frontend/static/frontend/main.js",
  "build": "webpack --mode production ./project/frontend/src/index.js --output ./project/frontend/static/frontend/main.js"
}

그리고 코드 트랜스파일을 위해 babel을 설치한다

npm i @babel/core babel-loader @babel/preset-env @babel/preset-react babel-plugin-transform-class-properties --save-dev

babel-plugin-transform-class-properties 는 ES6 클래스 static property를 사용하기 위해 필수적이다.

npm i react react-dom prop-types --save-dev

React와 prop-type도 설치한다

프로젝트 폴더내에 .babelrc 라는 새로운 파일을 만들어 babel 을 설정해준다.

{
    "presets": [
        "@babel/preset-env", "@babel/preset-react"
    ],
    "plugins": [
        "transform-class-properties"
    ]
}

마지막으로 webpack.config.js라는 파일을 작성하여 babel-loader를 설정해준다

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};

셋팅 끝!

React Frontend

./frontend/views.py를 작성한다.

from django.shortcuts import render
def index(request):
	return render(request, 'frontend/index.html')

./frontend/templates/frontend/index.html을 작성한다

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
  <title>Django DRF - React : Quickstart - Valentino G. - www.valentinog.com</title>
</head>
<body>
  <section class="section">
    <div class="container">
          <div id="app" class="columns"><!-- React --></div>
    </div>
  </section>
</body>
</html>

마지막에서 frontend/main.js를 호출한다

./project/urls.py를 열고

urlpatterns = [
    path('', include('leads.urls')),
    path('', include('frontend.urls')),
]

frontend.urls를 포함해준다

./frontend/urls.py를 열고

from django.urls import path
from . import views
urlpatterns = [
    path('', views.index ),
]

view를 연결해준다.

./project/settings.py를 열고 installed_app에 frontend를 추가해준다

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'leads', 
    'rest_framework',
    'frontend' # enable the frontend app
]

지금 서버를 실행시켜보면

python manage.py runserver

아무것도 안뜬다. React가 없기 떄문.

React frontend를 만들기 위해서 3개의 컴포넌트가 필요한데,

  1. Mother Component인 App
  2. data fetching을 위한 stateful component인 Dataprovider
  3. 데이터 display를 위한 stateless component 인 Table

App

에 react를 붙히기 위한 메인 컴포넌트이다. ./frontend/src/components/App.js라는 파일을 만든다.

import React from "react";
import ReactDOM from "react-dom";
import DataProvider from "./DataProvider";
import Table from "./Table";
const App = () => (
  <DataProvider endpoint="api/lead/" 
                render={data => <Table data={data} />} />
);
const wrapper = document.getElementById("app");
wrapper ? ReactDOM.render(<App />, wrapper) : null;