Django1.9 チートシート

  • 29
    いいね
  • 0
    コメント

環境

  • Python3
  • Django1.9.2

はじめに

チラっと見る用です。

参照

用語

  • pk ... primary key

Version

Djangoのバージョンを確認。

version
$ python -c "import django; print(django.get_version())"

Install

Project

プロジェクトの作成。

project
$ django-admin startproject MY-PROJECT-NAME

Application

アプリケーションの作成。

app
$ python manage.py startapp MY-APP-NAME
$ # TODO: プロジェクトのsettings.py:INSTALLED_APPSの編集
$ # TODO: プロジェクトのurls.pyを必要なら編集
$ # TODO: models.py編集後 → Model

Server

開発用サーバーの起動。

server
$ python manage.py runserver
$ python manage.py runserver 0.0.0.0:8000

Admin

管理サイトのスーパーユーザーを作成。

admin
$ python manage.py createsuperuser

Shell

Django のインタラクティブな操作。
設定中の DB を Django から操作出来る他、各ライブラリの動作確認も出来る。

shell
$ python manage.py shell
example
$ python manage.py shell
>>> from MY-APP-NAME.models import MyModel
>>> MyModel.objects.all()
[]
>>> m = MyModel()
>>> m.save()
>>> m.id
1
>>>

DB

MySQL

settings.py のMySQLサーバーへの切り替え。

settings.py
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases

DATABASES = { 
    'default': {                                                              
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'MY-DB-NAME',
        'USER': 'MY-DB-USER',
        'PASSWORD': 'MY-DB-PASSWORD',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }   
}

MySQLのポートの確認方法。

mysql
SHOW GLOBAL VARIABLES LIKE 'PORT';

Migrate

更新とDBへの適用。Modelを追加したら行う。
ここはフレームワーク全般的に複雑なロジックが必要なためエラーが頻発しやすい操作。キャッシュ絡みや Foreign Key の不整合など色々起こる。

migrate
$ python manage.py makemigrations MY-APP-NAME
$ python manage.py migrate

SQLの確認。

sqlmigrate
$ python manage.py sqlmigrate MY-APP-NAME 0001

createdb

DBの作成。settings.pyでSQLiteを使っている場合はSQLiteに構築される。
たとえばCMSのMezzanineを試す時とか。

createdb
$ python manage.py createdb

--noinputオプションで対話モードに入らず自動設定。

createdb
$ python manage.py createdb --noinput

Model

Field

 Field のタイプは https://docs.djangoproject.com/en/1.9/ref/models/fields/#model-field-types を参照。
 各 Field は Field クラス (https://docs.djangoproject.com/en/1.9/_modules/django/db/models/fields/#Field) を継承している。Field クラスのコンストラクタは以下の仮引数を取る。

def __init__(self,
    verbose_name=None,
    name=None,
    primary_key=False,
    max_length=None,
    unique=False,
    blank=False,
    null=False,
    db_index=False,
    rel=None,
    default=NOT_PROVIDED,
    editable=True,
    serialize=True,
    unique_for_date=None,
    unique_for_month=None,
    unique_for_year=None,
    choices=None,
    help_text='',
    db_column=None,
    db_tablespace=None,
    auto_created=False,
    validators=[],
    error_messages=None
):

 フィールドの一覧。

  • AutoField
  • BigIntegerField
  • BinaryField
  • BooleanField
  • CharField
  • CommaSeparatedIntegerField
  • DateField
  • DateTimeField
  • DecimalField
  • DurationField
  • EmailField
  • FileField
  • FilePathField
  • FloatField
  • ImageField
  • IntegerField
  • GenericIPAddressField
  • NullBooleanField
  • PositiveIntegerField
  • PositiveSmallIntegerField
  • SlugField
  • SmallIntegerField
  • TextField
  • TimeField
  • URLField
  • UUIDField
  • ForeignKey
  • ManyToManyField
  • OneToOneField

Class

 models.py のクラスの定義例。

models.py
from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

Select

DBからモデルのオブジェクトを全て取得。

all
objs = MyModel.objects.all()

pkを指定してオブジェクトを取得。

get
obj = MyModel.objects.get(pk=MY-PRIMARY-KEY)

存在しない場合の例外。

exception
try:
    obj = MyModel.objects.get(pk=MY-PRIMARY-KEY)
except MyModel.DoesNotExist:
    pass

WHEREはfilterで指定。

filter
objs = MyModel.objects.filter(name='myname').all()

rawクエリーとリスト内包記法。objects.all() と等価。

raw
objs = [obj for obj in User.objects.raw('SELECT * FROM myapp_user')]

オブジェクトの取得または 404 例外の送出。

get_object_or_404
from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, pk=1)

オブジェクトのリスト取得または 404 例外の送出。

get_list_or_404
from django.shortcuts import get_list_or_404

objs = get_list_or_404(MyModel)
objs = get_list_or_404(MyModel.objects.order_by('-pub_date'))[:5]

Insert

DBにモデルを一つ挿入。

post
obj = MyModel()
obj.id # -> None
obj.save()
obj.id # -> Number of id

Update

挿入済みのモデルからDBを更新。

update
obj = MyModel.objects.get(pk=MY-PRIMARY-KEY)
obj.my_field = 'Update my field'
obj.save()

Delete

DBからモデルを一つ削除。

delete
obj = MyModel.objects.get(pk=MY-PRIMARY-KEY)
obj.delete()

Validate

バリデーション。

validate
obj.full_clean()

ForeignKey

以下のようなモデルの

models.py
class Parent(models.Model):
    name = models.CharField(default='my name is parent', max_length=32)

class Child(models.Model):
    parent = models.ForeignKey(Parent, on_delete=models.CASCADE)

リレーション周りの操作。

parent = Parent()
parent.save()

c = parent.child_set.create() # Parent から Child の作成
c.parent.id # Child から Parent の ID を参照
parent.child_set.all() # Parent から関連する全ての Child を取得
parent.child_set.count() # Parent から関連する Child 数を返す

urls.py

プロジェクトからアプリケーションの urls.py への委譲。

myprj/urls.py
from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),

    # myappに委譲
    url(r'^myapp/', include('myapp.urls')),
]

アプリケーションからアプリケーションの views.py への委譲。

myapp/urls.py
from django.conf.urls import url 
from . import views

urlpatterns = [                          
    # views.py 内の index 関数に委譲。name は名前空間を表し、template ファイル内の {% url 'namespace' %} などで参照される。                                    
    url(r'^', views.index, name='index'),
]

views.py

HTTPメソッド名で分岐。

method_test
from django.http import HttpResponse

def method_test(request):
    if 'GET' in request.method:
        return HttpResponse('Success to GET')

    elif 'POST' in request.method:
        return HttpResponse(status=200)

    else:
        return HttpResponse(status=501)

templateの描画。パスのオリジンは MY-APP-NAME/templates.

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

def render_test(request):
    if 'GET' in request.method:
        return render(request, 'MY-APP-NAME/MY-TEMPLATE-FILE-NAME', {
            'title': 'Render Test',
        })

    else:
        return HttpResponse(status=501)

admin.py

 参照: https://docs.djangoproject.com/ja/1.9/ref/contrib/admin/actions/

 /admin 管理画面で自作モデルを管理したい場合は admin.py を編集する。自作モデルと ModelAdmin を継承した自作モデル用の Admin クラスを作成し、自作モデルと一緒に登録する。

admin.py
from django.contrib import admin
from .models import User

class UserAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'modified')

admin.site.register(User, UserAdmin)

Templates and Static directory

開発時の静的ファイルの配置方法。これは制限がゆるく、ある程度をユーザー側にゆだねている印象。選択肢がいくつかある。
views.pyからロードされるtemplateファイル、それから絶対パスで参照されるstaticファイルの配置時に選択が必要になる。

前提としてsettings.pySTATIC_URLの設定値は/static/とする。

一つ目の配置方法は、アプリケーション毎にディレクトリを作成する方法。

myapp00/
    templates/myapp00/
        index.html

    static/
        js/jquery.min.js

    static/myapp00/
        js/myapp00.js

myapp01/
    templates/myapp01/
        index.html

    static/myapp01/
        js/myapp00.js

この配置だと例えばstaticのURLでは/static/myapp00/js/myapp00.js等で表現される。ディレクトリがアプリケーションのネームスペースになっているのがわかる。
jquery.min.js/static/js/jquery.min.jsで参照可能。全アプリケーションで共有されるライブラリはネームスペースを省略している。

二つ目の配置方法。一つのアプリケーションに全部放り込む方法。

myapp00/
    templates/myapp00/
        index.html

    templates/myapp01/
        index.html

    static/
        js/jquery.min.js

    static/myapp00/
        js/myapp00.js

    static/myapp01/
        js/myapp00.js

この方法だとアプリケーションのインストール/アンインストール(手動の削除)で削除忘れのゴミが残る可能性などがあり、設計としてはあまり綺麗ではない気がする。しかし、一つのアプリケーションからディレクトリ(アプリケーションごとのネームスペース)を見渡せるのでファイルの管理は楽。
ちなみにURLは一つ目と差異が無いので、一つ目に移行したい場合はネームスペース用のディレクトリを移動させればおk。

Static

JSライブラリやCSS、画像ファイルなど。DjangoはCDNもサポートしている。

開発用サーバーの場合

MY-PROJECT-NAME/setting.pyを編集する。

setting.py
INSTALLED_APPS = [
...
    'django.contrib.staticfiles', # <- これが必要
...
]
setting.py
STATIC_URL = '/static/' # <- これも必要

作成したアプリにstaticディレクトリを加える。

case-of-linux
$ mkdir myapp/static

ここでネームスペース用のディレクトリが必要になる。慣例でアプリ名のディレクトリを作成する。startappで自動作成してくれても良い気がしますが、作成しないのは何か理由があるのでしょうか。

case-of-linux
$ mkdir myapp/static/myapp

これでhttp://localhost:8000/static/myapp/file.txt等でアクセス出来る。
この配置は開発時には適しているが、本番動作時では適切ではないとドキュメントに書いてある。

本番動作時の場合

 本番動作では Apache2 と WSGI モジュールを使いコンテンツをネットに公開する。ディストロごとに環境依存の手順がある。
 参照: How to deploy with WSGI

 開発環境も含めると、Djangoのコンテンツの公開方法は3通り以上あるらしい。

 1. 開発サーバーによる公開(python manage.py runserver 0.0.0.0:80 等)
 2. Apache2 + WSGI による公開 
 3. Apache2 + WSGI + ファイルサーバー(nginx 等)による公開

 1. は開発時の公開方法。公開にあたって 2. または 3. の方法を選択する必要がある。
 また、本番動作時では setting.py 内の設定も必要になる。

setting.py
##
# DEBUG が False の時、開発用サーバーにディティールは表示されず、ALLOWED_HOSTS の設定が参照される。
# また、Django は static ファイルの管理をしなくなる。static ファイルをネットに公開するには、VirtualHost にエイリアスを設定するか、ファイルサーバーを立てる必要がある。
# VirtualHost のエイリアス設定は https://docs.djangoproject.com/ja/1.10/howto/deployment/wsgi/modwsgi/ を参照。
# Django を Apache2 + wsgi で動かす場合、開発者は開発用サーバーを使用しないのでこの値は False に設定されるだろう。
# この時、static ファイルの運用方法も決定されている必要がある。
#
DEBUG = True

ALLOWED_HOSTS = ['*'] # DEBUG が False のとき参照される

Static

 static ファイルの設定例。

setting.py
##
# STATIC_URL は template の {% static 'my/file.html' %} などのタグで使用される。
# STATIC_URL が '/static/' の場合、 タグは 'http://localhost/static/my/file.html' などに変換される。
# STATIC_URL が 'http://localhost:8080/static' の場合、タグは 'http://localhost:8080/static/my/file.html' などに変換される。
# static 用のファイルサーバーがネットに公開されていて、それを参照する場合、STATIC_URL は 'https://my.static.file.server.com/' などになるだろう。
#
STATIC_URL = '/static/'

##
# STATIC_ROOT は collectstatic の収集ファイルのぶちまけ先。
# ファイルシステム上のパスを記述する。
# STATIC_ROOT が '/tmp/mystatic/' の場合、collectstatic は /tmp/mystatic/ ディレクトリ内にファイルを集める。
# このディレクトリはただの収集ファイルの格納先であり、Django はこれを管理・使用しない。
# STATICFILES_DIRS も参照のこと。
#
STATIC_ROOT = os.path.join(BASE_DIR, 'static') # プロジェクト直下の static ディレクトリをルートに指定

##
# STATICFILES_DIRS は、collectstatic の収集元のディレクトリを記述する。
# os.path.join(BASE_DIR, 'myapp/static') が追加されていた場合、collectstatic は myapp/static ディレクトリ以下からファイルを収集する。
#
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'myapp/static'), # myapp 以下の static ディレクトリを収集する
]

Template

Django/template の文法は多彩です(´Α`)

extends

拡張。Django/template では extends で指定したファイルを別のファイルで拡張してから描画することが出来る。

tree
myprj/                     # Project Root
    myapp/                 # Application Root
        templates/         # Application template files
            base.html      # Base html for the extend
            myapp/         # Application namespace
                index.html # Extends file from here

拡張のベースとなるファイル。

base.html
<!DOCTYPE html>
<html>
<head>
        <title>myprj - {% block title %}{% endblock %}</title>
</script>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>

拡張例。 block ~ endblock を使うことでベースファイルに指定の文字列を流し込める。例ではベースファイルの block titleblock content に文字列を流し込んで拡張している。

index.html
{% extends 'base.html' %}

{% block title %}My title{% endblock %}

{% block content %}
        <h1>Hello, World!</h1>
{% endblock %}

comment

コメントは一行単位。

{# my_comment #}

varialble

変数の参照。

var
{{ my_variable }}

参照時のデフォルト値。

var-with-default
{{ my_variable|default:'This is default value' }}

長さ。

length
{{ my_list|length }}

リストを添字で参照。

index
{{ my_list.0 }}

CSRF

CSRFトークンの参照。通信周りで使う。

csrf
{{ csrf_token }}

static

staticファイルの読み込みとパスの参照。前述のStaticも参照のこと。

static
{% load staticfiles %}

{% static 'myapp/jquery.min.js' %}
{% static 'myapp/style.css' %}
{% static 'myapp/image.png' %}

include

パスはMY-APP-NAME/templates/がオリジン。

include
{% include 'myapp/template.html' %}

パラメータ指定。

include
{% include 'myapp/template.html' with title='The Title' message='Hello' %}

with

スコープ付きのエイリアス。

with
{% with 'myapp/template.html' as fname %}
{%   include fname %}
{% endwith %}

if

if-else構文。

if-else
{% if duck %}
    <div>{{ duck }}</div>
{% else %}
    <div>Nothing...</div>
{% endif %}

比較演算。

if-eq
{% if duck == 'Duck' %}
    <div>{{ duck }}</div>
{% endif %}
if-compare
{% if duck|length > 0 %}
    <div>{{ duck }}</div>
{% endif %}

for

for
{% for obj in objs %}
    <p>{{ obj }}</p>
{% endfor %}

forloop.counterの参照。ループ中に添え字が欲しいとき。
forloop.counter0で0オリジンな添え字を得られる。

for
{% for obj in objs %}
    <p>{{ forloop.counter }}</p>
    <p>{{ forloop.counter0 }}</p>
{% endfor %}

Django/templateの for 構文では rangeをサポートしていない。
代価えとしてリストを使う。

for-range
'mylist': range(0, 10),

...

{% for el in mylist %}
{% endfor %}

template tags

 template タグの自作。
 参照: https://docs.djangoproject.com/ja/1.9/howto/custom-template-tags/
 
 アプリのディレクトリ直下に templatetags ディレクトリを作成し、そこにタグのファイルを放る。

$ mkdir myapp/templatetags
$ vi myapp/templatetags/mytag.py

 タグは用途ごとに分別されているっぽい。以下は assignment_tag の一例。

mytag.py
from django import template

register = template.Library()

@register.assignment_tag
def my_ass_tag():
    return {'message': 'This is ass******* tag.'}

 テンプレートファイル内での使用例。

{# 読み込み #}
{% load mytag %}

{# assignment_tag を使う #}
{% my_ass_tag as data %}

{{ data.message }}

Form

 フォーム。
 参照: https://docs.djangoproject.com/en/1.9/topics/forms/

 Django ではフォームの作成をサポートしている。フォームを使うとバリデーションや HTML のコーディング量軽減が可能になる。
 フォームを使うにはまず myapp/forms.py に自作フォームを定義する。そして views.py で POST/GET に応じてバリデーションなどを行い、template にフォーム・オブジェクトを渡すことでフォームを描画を補助する。フォームのデータは一連の処理の間 myapp/forms.py のフォームで管理される。
 ログイン・フォームの作成例。

forms.py
from django import forms

class LoginForm(forms.Form):
        username = forms.CharField(label='User name', max_length=128)
        password = forms.CharField(label='Password', widget=forms.PasswordInput)

Auth

 認証システム。
 参照: https://docs.djangoproject.com/ja/1.9/ref/contrib/auth/

User

 認証システムの認証ユーザーはモデル User で管理される。現在の認証ユーザーの一覧。

from django.contrib.auth.models import User

users = User.objects.all()
print(users)

 User オブジェクトのパブリックな属性一覧。

DoesNotExist
Meta
MultipleObjectsReturned
REQUIRED_FIELDS
USERNAME_FIELD
backend
check
check_password
clean
clean_fields
date_error_message
date_joined
delete
email
email_user
first_name
from_db
full_clean
get_all_permissions
get_deferred_fields
get_full_name
get_group_permissions
get_next_by_date_joined
get_previous_by_date_joined
get_session_auth_hash
get_short_name
get_username
groups
has_module_perms
has_perm
has_perms
has_usable_password
id
is_active
is_anonymous
is_authenticated
is_staff
is_superuser
last_login
last_name
logentry_set
natural_key
normalize_username
objects
password
pk
prepare_database_save
refresh_from_db
save
save_base
serializable_value
set_password
set_unusable_password
unique_error_message
user_permissions
username
username_validator
validate_unique

Create

 認証ユーザーの作成と保存。

from django.contrib.auth.models import User

user = User.objects.create_user('ono', 'lennon@thebeatles.com', 'onopassword')
user.last_name = 'Yoko'
user.save()

Update

 認証ユーザーの更新と保存。

from django.contrib.auth.models import User

user = User.objects.get(username='ono')
user.set_password('new password')
user.save()

Authenticate and Processing

 Authenticate(認証する)と分岐処理。

from django.contrib.auth import authenticate

user = authenticate(username='ono', password='secret')
if user is not None:
    # 認証に成功
else:
    # 認証に失敗

Permission

 認証ユーザーはデータへのアクセス権限を持つ。権限はモデル Permission で定義される。各モデルにはデフォルトで add, change, delete の権限が用意される。

from django.contrib.auth.models import Permission

perms = Permission.objects.all()
print(perms)

has_perm

 ユーザーが特定のモデルに対して add, change, delete の権限を持っているかどうかは has_perm() で確認できる。Foo アプリケーションのモデル Bar について確認する場合は下記のように書式を渡して真偽値を取る。

user.has_perm('foo.add_bar') # ユーザーが Foo アプリの Bar モデルに add 権限を持つなら True
user.has_perm('foo.change_bar')
user.has_perm('foo.delete_bar')

Create, Add, Remove

 ユーザーに権限を付与する場合は、まず Permission で権限を取得するか作成する。その権限を user.user_permissions.add(Permission) でユーザーに付与する。
 Django はデフォルトで add, change, delete の権限をモデルに合わせて DB のテーブル auth_permission に作成するので、これらの権限については DB から取得できる。付与した権限をオブジェクトに反映させたい場合は、DB からユーザーを再取得する必要がある。

from django.contrib.auth.models import User, Permission

user = User.objects.get(username='ono')

# ono の Foo アプリケーションの Bar モデルに対する change の権限は 
print(user.has_perm('foo.change_bar'))

# Bar モデルへの change 権限を取得後、ユーザーに付与する
perm = Permission.objects.get(codename='change_bar')
user.user_permissions.add(perm)

# 権限を再確認するが False
print(user.has_perm('foo.change_bar'))

# 権限を付与したら DB からオブジェクトを再取得(fetch)する
user = User.objects.get(pk=user.id)
print(user.has_perm('foo.change_bar'))

# 権限の剥奪は user_permissions.remove(Permission) を使う
user.user_permissions.remove(perm)
user = User.objects.get(pk=user.id)
print(user.has_perm('foo.change_bar'))

User's permissions

 ユーザーの権限一覧。

perms = user.get_all_permissions()
print(perms)

perms = user.user_permissions.all()
print(perms)

In views.py

 views.py で認証ユーザーを判別する。

def example_view(request):
    if request.user.is_authenticated:
        # このユーザーは認証済み
    else:
        # このユーザーは未認証

ImageField

ImageFieldを使うにはMEDIAの設定が必要。

myprj/setting.py
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

myprj/urls.py
from django.conf import settings
from django.conf.urls.static import static

...

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)