Django


Overview:

Django is Django.

End of extended support:
  1.8 LTS -APR2018
  1.11LTS -APR2020
  2.0     -APR2019  Python3.4-
  2.2 LTS

Client                template
                     form
[HTTP Req GET|POST]   model
                       
urls -> [request] -> view -> [response] -> Client

django2.0:

  RemovedInDjango20Warning: $ python -Wd manage.py

Project Files:

    <DIR>
    ├── manage.py               # Django Utilities
    ├── db.sqlite3              # DB
    ├── <SITE>                  #
       ├── settings.py         # Django Settings
       ├── urls.py             # URL Config
       └── wsgi.py             # WSGI Config
    └── <APP>                   #
        ├── admin.py            # Admin
        ├── apps.py             # App
        ├── models.py           # Model
        ├── tests.py            # Test
        ├── urls.py             # URL Config
        ├── views.py            # View
        ├── migrations          #
           └── 0001_initial.py # DB Migration Script
        ├── templates           # Template Files
           └── <APP>           # for Name Space
               └── base.html   # html
        └── static              # Static Files
            └── <APP>           # for Name Space
                └── style.css   # CSS

:

Create Project:
    $ django-admin startproject <SITE> ./
TimeZone Setting:
    file: <SITE>/setting.py
DB Setting (MYSQL):
    file: <SITE>/setting.py
DB Migration:
    $ python manage.py migrate
Create App:
    $ python manage.py startapp <APP>
Add App:
    file: <SITE>/setting.py
Run Server:
    $ python manage.py runserver 0:8000  # from all PC

Create Models:
    file: <APP>/models.py
Updates DB Schema:
    $ python manage.py makemigrations <APP>
    $ python manage.py migrate
Enable Admin Site:
    $ python manage.py createsuperuser
    file: <SITE>/admin.py
    file: <SITE>/urls.py

Install:

shel:
    $ pip install django

    $ python -m django --version

Create Project:

$ django-admin startproject <SITE> ./
> python myvenv\Scripts\django-admin.py startproject <SITE> . # win

    <DIR>
    ├── manage.py         # Django Utilities
    └── <SITE>            #
        ├── __init__.py   #
        ├── settings.py   # Django Settings
        ├── urls.py       # URL Config
        └── wsgi.py       # WSGI Config

TimeZone Setting:

file: <SITE>/setting.py
  edit: |
      LANGUAGE_CODE = 'ja-JP'
      TIME_ZONE     = 'Asia/Tokyo'
      USE_TZ        = False

DB Setting (MYSQL) (default:SQLite):

file: <SITE>/setting.py
  edit: |
      import pymysql
      pymysql.install_as_MySQLdb()

      DATABASES = {
        'default': {
          'ENGINE': 'django.db.backends.mysql',
          'NAME': '<NAME>',
          'USER': '<USER>',
          'PASSWORD': '<PASS>',
          'HOST': '',
          'PORT': '',
        }
      }

ENGINE:
  'django.db.backends.sqlite3'
  'django.db.backends.postgresql'
  'django.db.backends.mysql'
  'django.db.backends.oracle'

NAME:
  os.path.join(BASE_DIR, 'db.sqlite3')

CREATE DATABASE <NAME>;

DB Migration (Create Table):

$ python manage.py migrate

    <DIR>
    └── db.sqlite3

Create App:

$ python manage.py startapp <APP>

    <DIR>
    └── <APP>
        ├── __init__.py
        ├── admin.py
        ├── apps.py
        ├── models.py
        ├── tests.py
        ├── views.py
        └── migrations
            └── __init__.py

file: <SITE>/settings.py
  edit: |
      INSTALLED_APPS = [
        '<APP>.apps.<APP>Config'  # <APP>.apps.py
      ]

Run Server:

$ python manage.py runserver         # local PC only
$ python manage.py runserver 0:8000  # from all PC

# Log:
$ python manage.py runserver --traceback 0:8000     # ???
$ python manage.py runserver 2>&1 | tee django.log  # ???

Model:

pk: PrimaryKey

SQL
QuerySet

Updates DB Schema:

$ python manage.py makemigrations <APP>   # Make Migration Script
$ python manage.py migrate                # Do Migrate

Enable Admin Site:

file: <SITE>/settings.py
  edit: |
      INSTALLED_APPS = [
        'django.contrib.admin',
      ]

file: admin.py
  edit: |
      from django.contrib import admin
      from .models import <MODEL>

      admin.site.register(<MODEL>)

file: urls.py
  edit: |
      from django.contrib import admin
      #admin.autodiscover()  # ??

      urlpatterns = [
        path('admin/', admin.site.urls),
      ]

$ python manage.py createsuperuser

URL:
    http://127.0.0.1:8000/admin

Site urls:

file: <SITE>/urls.py
  edit: |
      from django.conf.urls import include, url

      urlpatterns = [
        url(r'', include('<APP>.urls')),
      ]

App urls:

file: <APP>/urls.py
  edit: |
      from django.conf.urls import include, url
      from . import views

      urlpatterns = [
        url(r'^$', views.post_list, name='post_list'),
      ]

view:

file: <APP>/view.py
  edit: |
      from .models import <MODEL>

      def post_list(request):
        return render(request, '<APP>/post_list.html', {'posts': <MODEL>})

Log:

file: <SITE>/settings.py
  edit: |
      LOGGING = {
          'version': 1,
          'formatters': {
              'all': {                            # Format Name
                  'format': '\t'.join([
                      "[%(levelname)s]",
                      "asctime:%(asctime)s",
                      "module:%(module)s",
                      "message:%(message)s",
                      "process:%(process)d",
                      "thread:%(thread)d",
                  ])
              },
          },
          'handlers': {
              'file': {                           # Handler Name
                  'level': 'DEBUG',
                  'class': 'logging.FileHandler',
                  'filename': os.path.join(BASE_DIR, 'django.log'),
                  'formatter': 'all',
                  #'maxBytes': 1024 * 1024 * 10, # 10GBでローテーション
                  #'backupCount': 10, # バックアップファイルは10ファイルまで
                },
              'console': {                        # Handler Name
                  'level': 'DEBUG',
                  'class': 'logging.StreamHandler',
                  #'stream': sys.stdout,
                  'formatter': 'all'
              },
          },
          'loggers': {
              'command': {                          # Logger Name
                  'handlers': ['file', 'console'],
                  'level': 'DEBUG',
                  #'propagate': True,
              },
          },
      }

usag:
    from logging import getLogger
    logger = getLogger('command')     # Logger Name 'command'

    logger.debug('DEBUG!!')           # output

DJango App Packaging:

setuptools

file: README.rst
file: LICENSE
file: setup.py
file: MANIFEST.in
  edit: |
    include LICENSE
    include README.rst
    recursive-include docs *
    recursive-include <APP>/static *
    recursive-include <APP>/templates *

$ python setup.py sdist

$ pip install --user <APP>/dist/<APP>-0.1.tar.gz
$ pip uninstall <APP>

    <DIR>
    ├── LICENSE                 # License Info
    ├── README.rst              # Readme Info
    ├── MANIFEST.in             # Include Files
    ├── setup.py                # Setup
    └── <APP>                   # App Folder
        ├── static              #
        └── templates           #

:

{{ <VALUE> }}
{% extends 'myapp/base.html' %}
{% load staticfiles %}
{% if user.is_authenticated %}{% else %}{% endif %}
{% block content %}{% endblock %}
{% for post in posts %}{% endfor %}
{% csrf_token %}

:

model:
  Post(models.Model):
  - author         = models.ForeignKey('auth.User')
  - title          = models.CharField(max_length=200)
  - text           = models.TextField()
  - created_date   = models.DateTimeField(default=timezone.now)
  - published_date = models.DateTimeField(blank=True, null=True)

site_urls:
  url(r'^admin/',            admin.site.urls),
  url(r'^accounts/login/$',  views.login,   name='login'),
  url(r'^accounts/logout/$', views.logout,  name='logout', kwargs={'next_page': '/'}),
  url(r'',                   include('myapp.urls')),

app_urls:
  url(r'^$',                          views.post_list,        name='post_list'      ),
  url(r'^drafts/$',                   views.post_draft_list,  name='post_draft_list'),
  url(r'^post/(?P<pk>[0-9]+)/$',      views.post_detail,      name='post_detail'    ),
  url(r'^post/new/$',                 views.post_new,         name='post_new'       ),
  url(r'^post/(?P<pk>[0-9]+)/edit/$', views.post_edit,        name='post_edit'      ),
  url(r'^post/(?P<pk>\d+)/publish/$', views.post_publish,     name='post_publish'   ),
  url(r'^post/(?P<pk>\d+)/remove/$',  views.post_remove,      name='post_remove'    ),

view:
  post_list(request)
    - render(request, 'myapp/post_list.html',       {'posts': posts})
  post_draft_list(request)
    - render(request, 'myapp/post_draft_list.html', {'posts': posts})
  post_detail(request, pk)
    - render(request, 'myapp/post_detail.html',     {'post':  post})
  post_new(request)
    - redirect(post_detail, pk=post.pk)
    - render(request, 'myapp/post_edit.html',       {'form':  form})
  post_edit(request, pk)
    - redirect(post_detail, pk=post.pk)
    - render(request, 'myapp/post_edit.html',       {'form':  form})
  post_publish(request, pk)
    - redirect('post_detail', pk=pk)
  post_remove(request, pk)
    - redirect('post_list')

template:
  base.html
    - <a href="{% url 'post_draft_list' %}">
    - <a href="{% url 'post_new'        %}">
    - <a href="{% url 'login'           %}">
    - <a href="{% url 'logout'          %}">
  post_list.html
  post_draft_list.html
  post_edit.html
  post_detail.html
    - <a href="{% url 'post_publish' pk=post.pk %}">
    - <a href="{% url 'post_edit'    pk=post.pk %}">
    - <a href="{% url 'post_remove'  pk=post.pk %}">

URL Dispatcher:

file: settings.py
  ROOT_URLCONF = '<SITE>.urls'

file: <SITE>.urls
  urlpatterns = [
    path('', include('<APP>.urls')),
    path('admin/', admin.site.urls),
  ]


  URLパターンを順に調べ、最初にマッチしたところで

  from django.urls import include, path
  path(route, view, kwargs=None, name=None)
  path(route, include('app.urls'))
  re_path(route, view, kwargs=None, name=None)     #  (?P<name>pattern)

  url(regex, view, kwargs=None, name=None, prefix='')     # name for reverse() # kwargs is dict-args
  url(regex, include('app.urls'))


Path converters:
  - <str:name>     # non-empty string, excluding the path separator, '/'.  :default
  - <path:name>    # non-empty string, including the path separator, '/'.
  - <int:name>     # zero or any positive integer. Returns an int.
  - <slug:name>    # slug-string (ASCII letters or numbers & '-','_')
  - <uuid:name>    # a formatted UUID. To prevent multiple URLs from mapping to the same page, dashes must be included and letters must be lowercase. Returns a UUID instance.

Custom path converter:
  Create Functions

Regular expressions:
 'r' # Python raw string, can't escape, only ¥" ¥'
 '^' # Begin
 '$' # End

  r'^$'       # ''
    url(r'^$', views.index, name='index')

  r'^aaa/$'    # 'aaa/'
    url(r'^aaa/$', views.aaa, name='aaa')

  (?P<name>pattern)   # Named Capturing Group
      url(r'^(?P<pk>[0-9]+)/$', views.detail, name='detail')    # pk <- [0-9]+