IT/WEB

[Django] Django 개발에 대한 기본 상식

개발자 두더지 2020. 9. 14. 18:02
728x90

 Django에 대한 기본적인 상식없이 당장 주어진 것만 급하게 하다보니 문제 해결에 시간을 너무 잡아먹어서 기본적인 Django 개발 방식에 대한 일본 포스팅을 찾아 해석해보았다.

 

이번 포스팅을 통해 확인할 수 있는 것


1. Django에는 어떠한 작성법이 있는가? (CoC등과 같은 대응 수준)

2. 소위 MVC (MTV)라는 것은 Django에서는 어떻게 작성 하는가?

3. 모델 (Model)은 어떻게 작성하는가?

4. View (Controller와 같은 것) 은 어떻게 작성하는가?

5. Template (View와 같은 것) 은 어떻게 작성하는가? 공통부분과 개별 부분은?

6. 라우팅은 어떻게 설정하는가?

7. WebAPI는 필수이므로 json은 어떻게 리턴하는가?

 

 

다른 프레임워크 이용자가 Django를 사용할 때 당황하는 부분


다른 프레임워크를 이용하였거나 하고 있는 사람들은 다음과 같은 차이점을 파악해두는 것이 좋다.

 

1) MVC 모델이 아닌 MTV모델

 Djangosms MVC이 아닌, MTV모델이라는 것을 채용하고 있다. 모델 자체가 다르다기보다는 단지 VC부분을 부르는 이름이 다른 이미지일뿐이므로 특별히 새로운 개념이 필요한 느낌은 아니다.

 MVC와 MTV의 대응은 아래와 같은 느낌이다.

- M → M (Model)

- V → T (Template)

- C → View

 간략히 설명하자면 Viwe(view.py)를 다루는 행위는 MVC에 있어서 Controller를 다루는 분위기로, Template은 MVC의 View(HTML의 기재)를 다루는 느낌이다. 

 

2. 프로젝트, 어플리케이션이라는 개념이 있다.

 프로젝트라는 커다란 개념에는 어플리케이션이 포함되어 있는 느낌이다.

 전체적으로 공통의 설정은 프로젝트라고 하는 스코프에서 설정하고 각 어플리케이션의 설정이나 코드의 작성은 어플리케이션에서 이루어지는 이미지이다.

 

3. 디렉토리의 구조가 조금 알기 어렵다.

 이것은 익숙해지면 해결될 문제라고 생각되지만, 예를 들어 django_test라는 이름의 프로젝트를 작성하면 아래와 같은 파일들이 생성된다.

django_test/
    manage.py
    django_test/
        __init__.py
        settings.py
        urls.py
        wsgi.py

 프로젝트 전체를 유지하는 디렉토리 (최상의 django_test)와 프로젝트의 관련 파일을 유지하는 같은 이름의 서브 디렉토리가 생성되기 때문에 단순히 django_test 디렉토리라고 말하면 어떤 쪽의 django_test 디렉토리인가 알기 힘든 경향이 있다.

 또한, 이번 포스팅에 있어서 조작은 최상위의 django_test를 '작업 디렉토리(カレンドディレクトリ)'로써의 조작이 된다.

 

4. form(의 자동생성)이 조금 특수하다.

 이 포스팅에서는 다루지 않지만, Django에서는 form을 자동으로 생성되도록 만들기 때문에 이것을 위해 Form클래스나 ModelForm클래스라는 것들이 존재한다. 기회가 된다면 이 부분에 대해 다루도록 하겠다.

 

 

개발 환경


 나는(원 포스팅의 작성자) Mac에서 venv로 가상환경을 만들어 이용하고 있다. 환경 셋업에 대한 것을 여기를 참고하길 바란다.

python의 버전읜 3.6.1로 pip freeze의 결과는 다음과 같다.

pip freeze
Django==1.11
django-bootstrap-form==3.2.1
PyMySQL==0.7.11
pytz==2017.2

 

 

준비와 확인


구체적인 코딩에 들어가기 앞서, 프로젝트의 작성이나 설정을 실시하자.

 

1) 프로젝트의 파일 생성

 먼저 Django의 프로젝를 작성하자. 아래의 명령을 실행하면, django_test라는 디렉토리와 필요한 디렉토리, 파일들이 생성된다.

django-admin.py startproject django_test

 

2) 먼저 server가 잘 동작하는지 확인

python manage.py runserver

 이 시점에서 먼저 django가 잘 동작하는지 확인하자. cmd에서 프로젝트 파일로 이동한 후 위의 커맨드를 입력하면 개발용 서버를 기동된다. 서버가 잘 작동되면 아래의 URL로 접속할 수 있다.

http://localhost:8000

 migrate가 되어있지 않다는 경고가 표시되지만 후에 이 문제를 해결할 것이므로 문제 없다. 신경쓰인다면 migrate 명령문을 실행시키면 이러한 경고가 사라진다. 

 서버가 잘 작동되는지 확인하였다면 Ctrl + C 를 눌러 개발 서버를 종료시켜 두자.

 

3) 데이터 베이스의 설정

 데이터 베이스 사용을 위한 설정을 하자. Django에서는 기본적으로 SQLite를 이용할 수 있도록 해두었지만, 나는 MySQL을 사용하려고 한다.

(1) 필요한 패키지 설치

 계속해서 Python으로부터 MySQL를 이용하기 위해 필요한 패키지를 설치하자. 다양한 종류가 있는듯하나, PyMySQL를 이용하자.

pip install PyMySQL

(2) settings.py의 편집

 패키지를 설치하였다면 데이터 베이스에의 접근 정보를 설정하자. Django에서는 프로젝트 디렉토리 아래 (여기서는 django_test)의 settings.py에 데이터 베이트의 접속 정보를 설정한다.

 DATABASE를 아래와 같이 작성하자. 데이터 베이스명, 접근 정보는 본인의 상황에 맞게 적절히 변경하길 바란다.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'djangodb',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': '',
        'PORT': '',
    }
}

 당연하지만 MySQL의 가동에 있어 djangodb라는 데이터베이스가 작성되어야 있어야 한다는 전제가 깔려 있다. 

 또한, 프로젝트가 PyMySQL 패키지를 읽어 들이도록, settings.py의 위쪽에 아래의 코드를 작성하자.

import pymysql
pymysql.install_as_MySQLdb()

 

4) 마이그레이션의 실행

데이터 베이스의 접속 정보를 설정하였으므로 cmd창에서 프로젝트 디렉토리로 이동한 후 마이그레이션을 실행해보자.

python manage.py migrate

 데이터 베이스의 설정이 잘 되었다면 문제 없이 위 코드가 실행되고 아래의 테이블이 생성된다. 에러가 발생하였다면 settings.py를 다시 확인해보자.

+----------------------------+
| Tables_in_djangodb         |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+

 

5) LANGEAGE_CODE와 TIME_ZONE의 설정

settings.py에 다른 설정도 조금 건드려 놓자.

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

 

 

어플리케이션을 작성한다.


프로젝트 전체와 관련된 설정이 끝났으므로 어플리케이션을 작성하도록 하겠다.

여기에서는 sample이라는 명의 어플리케이션을 작성할 것이다. 

 

1) 어플리케이션의 작성

cmd에서 다음의 커맨드를 실행시키면 어플리케이션이 실행된다.

python manage.py startapp sample

실행하면 sample이라는 디렉토리가 추가되고 그 아래에 어플리케이션과 관련된 파일도 동시에 생성된다. 디렉토리의 구조는 아래와 같이 된다.

django_test/
    sample/
        __init__.py
        admin.py
        apps.py
        migrations/
            __init__.py
        models.py
        tests.py
        views.py
    manage.py
    django_test/

 

2) 어플리케이션의 추가

어플리케이션을 작성하였으면, 이것을 프로젝트에 인식시키기 위해 settings.py의 INSTALLED_APPS에 추가한다. 

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
+   'sample',
]

 

 

모델(Model)의 정의


드디어 코딩을 하는 부분이다. 어느 부분부터 작성해도 상관없지만, 먼저 모델부터 작성해 나가고자 한다.

 

1) 모델 작성

 sample아래에 생성된 models.py에 모델을 기재하자 (sample/models.py). 여기서는 회원정보를 저장하는 느낌의 Member 모델을 정의해볼 것이다. 이 코드는 마이그레이션 파일 생성시의 디폴트 생성 정보로써 이용된다. 

class Member(models.Model):
    name = models.CharField('氏名', max_length=255)
    email = models.CharField('E-Mail', max_length=255)
    age = models.IntegerField('年齢', blank=True, default=0)

    def __str__(self):
        return self.name

 

2) 마이그레이션 파일의 생성 및 실행

 그럼 마이그레이션 파일을 생성하고 마이그레이트를 실행시키자. 모델을 생성한 어플리케이션 명을 지정하여 마이그레이션 파일을 생성한다. 

python manage.py makemigrations sample
python manage.py migrate

 모델을 변경하거나 추가할 때 마다 이 작업을 실시해야한다는 것을 잊지말자. 마이그레이트가 성공적으로 됐다면 이제 생성된 테이블을 살펴보자. 참고로 테이블은 (복수형이 아닌) applicationname_tablename이라는 이름으로 생성된다.

+----------------------------+
| Tables_in_djangodb         |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
| sample_member              |
+----------------------------+

 

 테이블(sample_member)의 컬럼 구성을 살펴보자.

+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(255) | NO   |     | NULL    |                |
| email | varchar(255) | NO   |     | NULL    |                |
| age   | int(11)      | NO   |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+

 

관지라 사이트의 이용


 Django에는 Scaffold(スキャフォールド)가 없지만, 모델을 관리하는 관리 사이트를 사용할 수 있도록 처음부터 정의되어 있다. DB에 직접 데이터를 INSERT해도 좋지만, 관리자 사이트를 사용해보자.

 

1) 로그인 유저의 작성

  http://localhost:8000/admin 에 접속하면 관리(Admin) 사이트 자체에 접근할 수 있지만 로그인하는 유저와 패즈워드가 정의되어있지 않기 때문에 이용할 수 없다. 그러므로 유저를 생성하자.

python manage.py createsuperuser

 

2) 관리대상 테이블 추가

 관리 사이트에 로그인해보면 그룹과 유저 항목이 있지만, 우리가 정의한 모델인 Member는 보이지 않는다. 관리 대상으로 하기 위해서는 관리 대상 등록을 해줘야한다. 관리 대상 등록은 sample아래의 admin.py 파일에 다음과 같이 작성하자.

from django.contrib import admin
from sample.models import Member

# Register your models here.
admin.site.register(Member)

 입력 후 관리자 페이지를 새로고침하면 Memebers가 추가되어 있는 것을 확인할 수 있을 것이다. 

 

 

화면에 표시해보자 (준비편)


등록한 데이터를 표시하기 전에 어떤 방식으로 표시를 하는지에 대해 먼저 알아보도록 하겠다.

표시하기 위해서는 

- 템플릿(HTML)의 정의

- View의 정의 (MVC라고 부르는 컨트롤러)

- 라우팅(ルーティング)의 정의

와 같은 흐름을 거치는 것이 일반적이지만, 먼저 템플릿을 이용하지 않고, View와 라이팅 정의만을 이용해보자.

 

1) 1단계 ; 우선 무엇이라도 표시해보자.

(1) views.py 작성

그럼 View를 정의해보자. 구체적으로는 sample아래의 views.py 파일에 코드를 추가하는 것이다. index라는 메소드에 "index입니다!"라고 직접 리턴하는 짧은 코드를 작성해보자.

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
def index(request):
    # 우선 문자열을 리턴
    return HttpResponse("Index입니다!")

(2) 라우팅의 정의

계속해서 URL와 메소드를 맵핑시키자. 참고로, Django에서 라우팅의 정의는 1개의 파일에 완전히 작성하지 않고,

- 각 어플리케이션 폴더에 urls.py를 생성하고 어플리케이션 단위로 라우팅을 작성한다.

- 프로젝트 전체의 urls.py에 각 어플리케이션의 urls.py 파일을 include한다.

 위와 같은 작성법이므로 이 순서대로 작성해보자.

 먼저 sample 어플리케이션 아래에 urls.pt를 생성하고 아래와 같이 코드를 작성하자.

from django.conf.urls import url
from sample import views

urlpatterns = [
    url(r'^members/$', views.index, name='index'),
]

 라우트 (맵핑하는 URL)은 정규 표현으로 기재한다. 또한, 처음의 r은 ''(따옴표)내에 \등의 특수 문자를 보통의 문자로써 다루를 때 사용하는 것이다.

 어플리케이션 내 urls.py의 코드 작성이 끝났으므로, 프로젝트 (django_test) 아래의 urls.py에 아래와 같이 작성한다.

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^sample/', include('sample.urls', namespace='sample'))
]

 정의가 끝났으며로 cmd창에서 서버를 실행해 아래의 url에 접근해보자.

http://localhost:8000/sample/index 에 접속하였을 때, "Index입니다!"라고 표시되면 OK이다.

 

2) 2단계 ; 템플릿을 이용해보자.

 views.py와 urls.py의 관계에 대해 알아보았으므로 이제 템플릿 (html)을 정의하고 이용해보자.

(1) 공통 파일 작성

 Django에 있어서 공통이 되는 템플렛 파일은 project_name/application_name/templates/base.html 의 규칙으로 배치한다. 그러므로 여기에서는 django_test/sample/templates/base.html 이 된다. 따라서 이와 같은 파일을 생성하고 코드를 작성해보자.

 나는 Bootstrap을 자주 사용하므로, Bootstrap의 샘플을 조금 변경 (CDN을 이용하도록 변경)하여 공통 템플릿으로 사용할 것이다. 

{% load staticfiles %}
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{% block title %}My books{% endblock %}</title>
    <!-- Bootstrap -->
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
    <div class="container">
      {% block content %}
        {{ content }}
      {% endblock %}
    </div>
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
  </body>
</html>

 위 코드에서 아래의 부분은 필요에 따라 다르게 작성될 수 있다.

<title>{% block title %}My books{% endblock %}</title>

<div class="container">
      {% block content %}
        {{ content }}
      {% endblock %}
</div>

 static 컨텐츠를 두는 디렉토리의 정의 등도 가능하지만, 여기서는 생략하도록 하겠다. 같은 의미로 {% load staticfiles %}라는 코드도 이번 포스팅 범위에서는 다루지 않겠다.

(2) 각 페이지(index.html) 작성

 각 페이지를 작성해보자. Django에서는 다른 MVC 프레임워크의 View(컨트롤러) 명과 폴더 명과의 밀접한 연관이 없는듯하지만, 여기서는 templates아래에 members라는 디렉토리를 작성하고 그 아래에 index.html을 생성-코드 작성한다.

 base.html을 읽어들이면 먼저 base.html에 정의된 block에 맵핑되는 콘텐츠를 읽어 들이는 코드를 작성해보자.

# smaple/templates/members/index.html

{% extends "base.html" %}

{% block title %}
목록을 표시할 예정
{% endblock title %}

{% block content %}
<h1>어떠한 콘텐츠</h1>
{% endblock content %}

Django의 템플릿 시스템에는 

{% block block_name %}{% endblock block_name %} 이라는 룰에 따라 블록을 정의하고 block_name에 대응하는 변수를 준비를 하는 것으로 동적인 표시를 가능하게 할 수 있다.

(3) views.py를 각각 다른 베이스를 보러 가도록 하기 위해 코드를 변경한다.

방금 "Index입니다!"라고 다이렉트로 response하고 있는 코드를 작성한 index.html을 보러 갈 수 있도록 views.py를 아래와 같이 변경하자 (smaple/views.py).

def index(request):
    # return HttpResponse("Index입니다!")
    return render(request, 'members/index.html')

 코드 편집을 완료하였다면,  http://localhost:8000/sample/members/ 에 접속하고 잘 표시되는지 확인해보자.

 

 

데이터 표시


이제 데이터베이스에 저장했던 데이터를 표시해보자.

1) (sample/)views.py 재편집

views.py 파일을 열어 데이터 베이스로 부터 값을 취득하여 템플릿에 리턴하도록 코드를 이와 같이 작성하자.

from django.shortcuts import render
from django.http import HttpResponse

from sample.models import Member

# Create your views here.
def index(request):
    # return HttpResponse("Index입니다!")
    members = Member.objects.all().order_by('id') #값을 취득
    return render(request, 'members/index.html', {'members':members}) #Template에 값을 넘겨줌

 

2) index.html의 편집

표시하는 쪽의 파일도 편집하자. 받은 데이터를 테이블 형식으로 표시해보자.

{% extends "base.html" %}

{% block title %}
一覧を表示する予定
{% endblock title %}

{% block content %}
<h3>목록</h3>
<table class="table table-striped table-bordered">
    <thead>
        <tr>
            <th>ID</th>
            <th>이름</th>
            <th>E-Mail</th>
            <th>나이</th>
        </tr>
    </thead>
    <tbody>
        {% for member in members %}
        <tr>
            <td>{{ member.id }}</td>
            <td>{{ member.name }}</td>
            <td>{{ member.email }}</td>
            <td>{{ member.age }}</td>
        </tr>
        {% endfor %}
    </tbody>
</table>
{% endblock content %}

특별히 어려운 부분은 없지만, 루프 부분을 아래와 같이 작성한다는 것을 알아두자.

{% for member in members %}
<tr><td>반복문 돌릴 내용</td></tr>
{% endfor %}

완료하였다면 서버를 실행시켜 확인해보자.

 

 

JSON을 리턴


 마지막으로 WebAPI를 작성해보자. 필요한 것은 json에의 response이다. 안타깝게도 Django에서는 어느 정도 손을 움직여야한다 (API 작성을 위해 Django Rest Framework라는 것을 사용하기도 하는 것 같다).

1) views.py 코드 변경

sample/views.py에 아래와 같은 메소드를 추가한다. 어떠한 이름으로 명명해도 상관없으며, 여기서는 api라는 메소드를 작성하였다.

취득한 데이터를 OrderedDict에 격납하고 response해도 괜찮은 것 같다. 참고로 OderDict는 이름 대로 정렬화된 딕셔너리형으로 보통의 딕셔너리형에서는 데이터의 순서가 제멋대로 되어 있으므로 OderDict형에 데이터를 저장하였다.

def api(request):
    members = []
    for member in Member.objects.all().order_by('id'):
        member_dict = OrderedDict([
                ('id',member.id),
                ('name',member.name),
                ('email',member.email),
                ('age',member.age),
            ])
        members.append(member_dict)

    data = OrderedDict([
            ('status','ok'),
            ('members',members),
        ])

    json_str = json.dumps(data, ensure_ascii=False, indent=2)
    return HttpResponse(json_str, content_type='application/json; charset=utf-8')

템플릿을 이용하지 안혹, views.py에 직접 response한다. 최상위 계층에 status라는 것을 추가해보았다 (개인적으로 자주 사용하므로).

 

2) 라우팅을 추가

추가한 api메소드에 대응하는 URL을 맵핑한다. urls.py에 아래와 같이 작성하였다.

from django.conf.urls import url
from sample import views

urlpatterns = [
    url(r'^members/$', views.index, name='Index'),
    url(r'^members/api/$', views.api, name='Api'),
]

이제 서버를 실행시켜(runserver) http://localhost:8000/members/api/에 접근해 기대하고 있는 json이 반환되어 올지 확인해보자. 확인해보면 아래와 같은 json형태로 잘 반환되어 옴을 알 수 있다.

{
  "status": "ok",
  "members": [
    {
      "id": 1,
      "name": "hoge",
      "email": "hoge@hoge.com",
      "age": 20
    },
    {
      "id": 2,
      "name": "foo",
      "email": "foo@foo.com",
      "age": 40
    }
  ]
}

참고자료

qiita.com/kaki_k/items/b76acaeab8a9d935c35c

qiita.com/zaburo/items/0e15f6c150caa13ca34c

728x90