Learning history to participate in team application development with Python ~ Build Docker / Django / Nginx / MariaDB environment ~

2020/04/27 postscript

-Added pip install when you want to add a module during development

-I had a very wrong understanding of the docker-compose run command, so I corrected it and changed to using the docker exec command as an alternative.

Introduction

Now that you've covered the basics of Python, the next step is to build an environment to get used to Django, and the planner's article has just a tutorial on the basics of Django. This is the output when you do that.

Article of the planner in creating this article

Articles that enable system development with Django (Python)_Introduction

Environment and directory structure to create

Create a django environment with docker-compose (MariaDB + Nginx + uWSGI) Docker-Compose with Python3.6 + NGINX + MariaDB10 + uWSGI's Django environment greedy set

It was built in the following environment with reference to the above two articles and the environment.

environment

・ Windows10 home ・ Docker version 19.03.1 (Docker Toolbox specification) -VirtualBox 6.0.16 ・ Python3.8 ・ Django 3 ・ Mariadb 10.1 ・ Nginx 1.13

directory

.
├── db
│   ├── data
│   └── sql
│       └── init.sql
├── docker-compose.yml
├── nginx
│   ├── conf
│   │   └── app_nginx.conf
│   ├── log
│   └── uwsgi_params
├── python
│   ├── Dockerfile
│   └── requirements.txt
├── src
│   └── project
│       ├── app
│ 		│ 	└── __init__.py
│ 		│ 	└── settings.py
│ 		│ 	└── urls.py
│ 		│ 	└── uwsgi.log
│ 		│ 	└── views.py
│ 		│ 	└── wsgi.py
│ 		│ 
│──── ──├── migrations
│       │ 
│       ├── project
│ 		│ 	└── __init__.py
│ 		│ 	└── settings.py
│ 		│ 	└── urls.py
│ 		│ 	└── uwsgi.log
│ 		│ 	└── views.py
│ 		│ 	└── wsgi.py
│ 		│ 
│       └── manage.py
│       └── uwsgi.ini
└── static

Dockerfile

FROM python:3.8-alpine
ENV PYTHONUNBUFFERED 1
ENV PYTHONIOENCODEING utf-8
ENV APP_PATH code
# /$APP_It is also possible with PARH, but this time it is described because it is bug avoidance and practice.
WORKDIR /code
# /code is$APP_Also possible with PATH
COPY ./requirements.txt /$APP_PATH
RUN apk add --no-cache --virtual .build-deps bash gcc musl-dev libffi-dev \
 g++ libgcc libstdc++ libxml2-dev libxslt-dev openssl-dev curl \
 && apk add --no-cache --virtual --update-cache\
 jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \
 mariadb-dev mariadb-connector-c-dev \
 && pip install cython && pip install -U pip && pip install -r requirements.txt
RUN rm -rf /var/cache/apk/* && \
    rm -rf /tmp/*

Let's review here again. As for FROM and ENV, I will take away the first appearance as it is like a spell. First, the notation alpine. It's like Alpine Linux, an ultra-lightweight version of Linux. And since Docker is like a book of Linux, trying to reduce the weight of the image in Docker based on this is like -alpine notation ... .. In addition, there is also -slim notation, and with -alpine, it seems that some packages such as Numpy may have a problem when building with Docker. This is because C-type processing is built into such a package, but the image of alpine notation is For the time being, let's just understand that the alpine notation in Docker makes the image lighter.

Next, WORKDIR and COPY, WORKDIR is the specification of the working directory in the virtual environment. Later, I will set it with docker-compose.yml, but this time I will share the src folder on the host side and the code folder of the virtual environment, that is, the project folder of the application will be expanded in this .. COPY is a command to copy the files in the path to the folder specified by $ APP_PATH in the virtual environment. Since the path on the host side is specified based on the directory where the Dockerfile is located, that is, the location one step down from the parent folder seen from the Dockerfile is specified, so it is as described above.

After that, install various packages with RUN, mainly for avoiding errors in C processing caused by using alpine (up to gcc ~ libxslt-dev) and connectors for connecting to MariaDB. Finally, put cython (accelerated Python using C language know-how) with pip, upgrade pip, and install the package described in requirements.txt.

requirements.txt

Django==3.0
django-bootstrap4
flake8==3.7.9
ipython==7.10.1
mysqlclient==1.4.6
Pillow==6.2.1
uwSGI==2.0.18
dj-static
whitenoise

I've included it as a trial, Django, django-bootstrap4, and mysqlclient are enough to use it at the level where you start studying like me. Since uwSGI is implemented as a reference source, I will try it because it is a big deal.

About django-bootstrap4

A module for simply writing Bootstrap registration / correction forms in Python. To explain how to use it in detail, it is necessary to understand the mechanism of View in Django a little more, so let's move on with the understanding that if you use bootstrap 4 with Django, you should include it.

mysqlclient

Modules needed to connect to MySQL with Python

About uWSGI

Simply put, it's an AP server, or application server. Up to this point, I have been learning with a fluffy understanding that I have to prepare for the Web server and application server for the time being. This time I had the opportunity to get into the technology called uWSGI, so I delved a little deeper based on the following reference articles.

Differences between web server and application server in Rails development (translation) Introduction to uWSGI About "uWSGI" and Application Server I will explain the difference between a web server and an application server in an easy-to-understand manner What is the role of the AP server? Let's understand the difference from the Web server

When I put it together

・ What is WSGI?

→ Abbreviation for "Web Server Gateway Interface", which is a rule of communication between a Python web application and a web server ... In other words, a protocol. Of course, communication is not possible without a protocol, but apparently the problem is that the framework's support and non-support for Web servers are different. In Python, it seems that the background of WSGI introduction is that by creating a common protocol in this way, any framework that supports WSGI can be operated.

・ Then what is uWSGI?

→ It is a kind of application server that supports WSGI. You can use HTTP communication and UNIX domain socket communication methods, but I'll leave this as it can't be helped if I dig deeper now.

・ What are Web servers and application servers in the first place?

→ The Web server is for performing some processing after receiving a request from the user. In other words, unless you are in a local environment, the image is that the sent request basically first goes to the Web server and decides what to do with it. Except for dynamic requests (requests for things that can change frequently such as CSS, Javascript, images, videos), you can process them here. Conversely, when those requests come in, it can be said that it has the role of passing the requests to the application server. Of course, in CSS and images, if they are static, you can process them here.

So, what is an application server? In short, it is the one that runs the application. Requests and responses are passed where it is easy to understand. Taking the login process as an example

  1. A request to log in flies to the web server.
  2. The login request is a dynamic request (the information ID and password differ depending on the requesting user), so it is passed to the application server.
  3. Since the path (request) came from the web server, the application server requests the application to process it.
  4. The application executes the process and generates dynamic content by requesting the DB as needed. In this case, the login information is acquired and the login is executed.
  5. Return 4 as a response to the web server
  6. The execution result is passed to the Web server, processed, and login is completed.

It turns out that. I felt that even one login is actually a rather complicated process.

docker-compose.yml


version: '3.7'
volumes:
  django.db.volume:
    name: django.db.volume
services:
  nginx:
    image: nginx:1.13
    container_name: app_nginx
    ports:
      - "8000:8000"
    volumes:
      - ./nginx/conf:/etc/nginx/conf.d
      - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
      - ./static:/static
      - ./nginx/log:/var/log/nginx
    depends_on:
      - python
  db:
    tty: true
    image: mariadb:10.1
    container_name: app_mariadb
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root
      TZ: 'Asia/Tokyo'
    volumes:
      - django.db.volume:/var/lib/mariadb
      - ./db/sql:/docker-entrypoint-initdb.d
  python:
    build: ./python
    image: python_blog
    container_name: app_python
    command: uwsgi --ini /code/project/uwsgi.ini
    environment:
        - DATABASE_HOST=db
    volumes:
      - ./src:/code
      - ./static:/static
      - ./python:/code/python
    expose:
      - "8888"
    depends_on:
      - db


As a recap, the path specification in volumes is only host side: virtual environment side and tty: true is a description that keeps the container up. This time, I was addicted to the swamp because I was dealing with Docker on Windows, or because of the system of Docker's container, it was quite difficult to eliminate the connection error to the DB. As a result of the investigation, both of them were raised as the reason, but I think that the reason for the latter is that I reflected the change and terminated Docker with down. Anyway, this time, in the volume of DB, prepare a directory to store DB information on the host side and share it to hold DB data in Docker. First of all, in order to do volume mount at the beginning, I solved it by making it only for persistence of mariadb container. By the way, django.db.volume doesn't exist in the directory on the Windows side? However, in the case of Docker Toolbox, this is very complicated, that is

Windows ⇒ Virtual Box ⇒ Docker == Main host ⇒ Host seen from Docker (guest when viewed from Windows) ⇒ Guest

That's the relationship, and since the Volume created this time is in Virtual Box, it will not be displayed on Windows, which is confusing. Anyway, one of the advantages of Docker is that you can easily create and delete containers, but if data such as DB is not persisted, it will cause a connection error, so be sure to make it a means of persistence. Let's take it. By the way, if you are using dokcer-compose.yml, you should probably make various changes and updates by playing with this instead of Dockerfile, but in that case, remember that build can be reflected in up without needing it. It was. In the case of Dockerfile, build is required, so if you do not make DB persistent, you will probably get an error (experienced)

For nginx, write the settings in the conf file and prepare a parameter file for uWSGI.

app_nginx.conf



upstream django {
	ip_hash;
	server python:8888;
}

# configuration of the server(Server configuration)

server {
	# the port your site will be served on

	listen		8000;

	# the domain name it will serve for

	server_name 127.0.0.1; # substitute your machine`s IP address or FQDN
	charset		utf-8;

	# max upload size
	client_max_body_size 75M;

	location /static {
		alias /static;
	}

	# Finally, send all non-media requests to the Django server.
	location / {
		uwsgi_pass django;
		include /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
	}

}

Access is first received on port 8000. Next, if it is a request to a static file, pass it to static of nginx. If you need to pass the request to another application server (uWSGI), skip the request to port 8888. Don't forget to specify the directory where the uWSGI parameter files are located at the end.


uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;

uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;

It's a so-called spell.

** Added on 2020/04/27 **

If you want to install additional packages during development, you can go directly into the container and do python -m pip install, but since I made requirements.txt, I will use this I want to do it. However, if nothing is done, an error will occur even if you use python -m pip install requirements.txt, for example. Did you do COPY and ʻADD` in DockerFile? I had a question, so I decided to check it for the time being.

# docker exec -it app_find from python bash./Command execution result

./
./project
./project/app
./project/app/admin.py
./project/app/apps.py
./project/app/migrations
./project/app/migrations/0001_initial.py
./project/app/migrations/0002_sampledb.py
./project/app/migrations/__init__.py
./project/app/migrations/__pycache__
./project/app/migrations/__pycache__/0001_initial.cpython-38.pyc
./project/app/migrations/__pycache__/0002_sampledb.cpython-38.pyc
./project/app/migrations/__pycache__/__init__.cpython-38.pyc
./project/app/models.py
./project/app/tests.py
./project/app/urls.py
./project/app/views.py
./project/app/__init__.py
./project/app/__pycache__
./project/app/__pycache__/admin.cpython-38.pyc
./project/app/__pycache__/apps.cpython-38.pyc
./project/app/__pycache__/models.cpython-38.pyc
./project/app/__pycache__/urls.cpython-38.pyc
./project/app/__pycache__/views.cpython-38.pyc
./project/app/__pycache__/__init__.cpython-38.pyc
./project/manage.py
./project/media
./project/polls
./project/polls/admin.py
./project/polls/apps.py
./project/polls/migrations
./project/polls/migrations/0001_initial.py
./project/polls/migrations/0002_auto_20200408_1848.py
./project/polls/migrations/__init__.py
./project/polls/migrations/__pycache__
./project/polls/migrations/__pycache__/0001_initial.cpython-38.pyc
./project/polls/migrations/__pycache__/0002_auto_20200408_1848.cpython-38.pyc
./project/polls/migrations/__pycache__/__init__.cpython-38.pyc
./project/polls/models.py
./project/polls/tests.py
./project/polls/urls.py
./project/polls/views.py
./project/polls/__init__.py
./project/polls/__pycache__
./project/polls/__pycache__/admin.cpython-38.pyc
./project/polls/__pycache__/apps.cpython-38.pyc
./project/polls/__pycache__/models.cpython-38.pyc
./project/polls/__pycache__/tests.cpython-38.pyc
./project/polls/__pycache__/urls.cpython-38.pyc
./project/polls/__pycache__/views.cpython-38.pyc
./project/polls/__pycache__/__init__.cpython-38.pyc
./project/project
./project/project/settings.py
./project/project/urls.py
./project/project/uwsgi.log
./project/project/views.py
./project/project/wsgi.py
./project/project/__init__.py
./project/project/__pycache__
./project/project/__pycache__/settings.cpython-38.pyc
./project/project/__pycache__/urls.cpython-38.pyc
./project/project/__pycache__/views.cpython-38.pyc
./project/project/__pycache__/wsgi.cpython-38.pyc
./project/project/__pycache__/__init__.cpython-38.pyc
./project/templates
./project/templates/admin
./project/templates/admin/base_site.html
./project/templates/admin/index.html
./project/templates/app_folder
./project/templates/app_folder/page01.html
./project/templates/app_folder/page02.html
./project/templates/app_folder/top_page.html
./project/templates/base.html
./project/templates/polls
./project/templates/polls/detail.html
./project/templates/polls/index.html
./project/templates/polls/results.html
./project/uwsgi.ini


The find ./ command is a Linux command, which means search and display directories under the root folder. I've also mixed some files for the Django tutorials from this article onwards, but a closer look reveals that requirements.txt is missing. In DockerFile, it is COPY ./requirements.txt / code and it is a container created based on that image, but I do not understand why. However, one thing I have in mind is that my understanding of the docker-compose run command was too diverse and too diverse, resulting in an infinite number of containers ... If anyone knows, please let me know.

Now, the solution for now is to mount requirements.txt on the container. So I added the description of ./python:/code/python to docker-compose.yml. Now you can do ʻup, enter with the docker execcommand, and dopip install -r ./python/requirements.txt`, so you can install additional packages.

Container launch

When you're ready, try creating a container with docker-compose up -d --build. As a recap, the -d option keeps the container running in the background.

…… It's okay to do it, but if you start it up as it is, it won't actually start up well. The reason is that uWSGI is not ready yet. If you look at docker-comopose.yml again, you'll see that the nginx and python containers have depend_on specified. We've previously learned that this is an indication of container dependencies. In other words, the nginx and python containers cannot be started unless the db container is started. However, the essential db server does not work without the application server, so uWSGI is not ready now and the container does not start ... This is what happens. So, first create a Django project and create a uwsgi config file.


docker-compose run --rm python django-admin.py startproject project

The run command ** creates a new container ** and executes the specified command. So ** If you use it a lot, you can create an infinite number of containers **. So be sure to use the --rm command unless you have a specific intention.

By the way, there is also a command called docker-compose exec $ {service_name} $ {command} that executes the command in the running container. For example, if you do docker-compose exec python bash, you can enter the container of the python service and execute the bash command there. Now, after run, specify the docker-compose.yml set service name and the spell that will generate the Django project. Then, a folder with the name specified after start project will be created in the area specified by WORKDIR. And finally the container is deleted with the --rm command. However, since the result of django-admin.py startproject project remains on the host side, it can be reflected in the subsequent container startup, so there is no problem.

uwsgi.ini



[uwsgi]
socket = :8888
module = project.wsgi
wsgi-file = /code/project/project/wsgi.py
logto = /code/project/project/uwsgi.log
chdir=/code/project
py-autoreload = 1


After that, create a configuration file as described above and place it in the same directory as manage.py of the created project file. What is written is the port used by uWSGI, the log file, and the location of the wsgi file. As an aside, keep in mind that Django projects always base their directory to manage.py and execute commands.

When you're done so far


$ docker-compose up -d
$ docker ps -a


And cast the spell. You can see the list of containers with the last spell, so make sure that 3 containers are up and all are up. As I mentioned at the beginning, when using MySQL with docker, it sometimes happens that it does not become up here but immediately becomes exit. This time, I think that there is no problem because I have taken measures that can be done so far, but personally I thought that it is better to prepare the following table first before casting the spell here. ..

Database table preparation

sql:./db/sql/init.sql



CREATE DATABASE IF NOT EXISTS Arbitrary database name CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER IF NOT EXISTS 'Any username'@'%' IDENTIFIED BY 'password';
Database name specified on GRANT ALL PRIVILEGES ON.* TO 'Database name specified above'@'%';

FLUSH PRIVILEGES; #will not forget


A spell with the familiar SQL style. MySQL always creates a database and a user, and if you do not first give the created user full rights in that database (it can also be given in table units), you will be in a bad mood, so set this first I will do it. By the way, the user name and password on the second line can also be set in docker-compose.yml. This time, the root password of MySQL is set in the part MYSQL_ROOT_PASSWORD: root. This time we will give full rights to the entire database, so set the root user. By the way, the user name and password are set as root, but of course this is allowed because it is local, and in the production environment it is too unpleasant for security, so let's set it properly. As you can see by writing so far, the point is that this is the SQL initialization file.

Once you've got this far, let's take a look at the Django config file.

project/project/settings.py



DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_test',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': 'db',
        'PORT': '3306',
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
            'charset': 'utf8mb4',
        },
    }
}

#Omission


STATIC_ROOT = '/static'
STATIC_URL = '/static/'

You can almost understand what is written. It seems that the ENGINE part may be different depending on the version of Django, but in that case please rewrite it to an appropriate one. After that, set the database name and items related to the root user. For the HOST part, set the service name of the container that contains the database specified in docker-compose.yml, and for PORT, set the port specified there as well. OPTION is a spell. 'init_command':" SET sql_mode ='STRICT_TRANS_TABLES' " It is recommended to specify this here, so let's set it. It seems to be a mode that solves many data integrity problems in MySQL. By the way, I got an error if I didn't set it. The last part is the directory setting of the static file, otherwise the static file will not be read.

When you're done so far

Enter the container with docker exec -it app_python bash

cd ./project/
python manage.py makemigrations
python manage.py migrate
python manage.py runserver


Let's cast the spell. If the migration is completed successfully without throwing an error here, it means that the database access was successful, so let's create a container with up -d earlier.

settings.py settings

Once you've done that, it's time to set up Django. Before that, first create an application folder in the project

Enter the container with docker exec -it app_python bash

cd ./project/
python manage.py startapp folder name

After executing, set settings.py as follows.

project/project/settings.py



"""
Django settings for project project.

Generated by 'django-admin startproject' using Django 2.1.5.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '&$l0ho%f&-w%&t_2l=@98u(i_58t15d-jn+ln5fuyvy(^l88^t'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app.apps.AppConfig', #add to
    'bootstrap4' #add to
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware', #add to
]

ROOT_URLCONF = 'project.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')], #Specify the directory of the folder containing the html template
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'builtins':[ 
                'bootstrap4.templatetags.bootstrap4',#Add here!
            ],
        },
    },
]

WSGI_APPLICATION = 'project.wsgi.application'


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

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_test',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': 'db',
        'PORT': '3306',
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
            'charset': 'utf8mb4',
        },
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

STATIC_URL = '/static/'
STATIC_ROOT = '/static'

# Custom User
AUTH_USER_MODEL = 'app.User'

#Media file settings

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


When excerpting


'app.apps.AppConfig', #add to
'bootstrap4' #add to

apps.py



from django.apps import AppConfig


class AppConfig(AppConfig):

    name = 'app'


This part specifies the location of ʻapps.py above. As you can see, ʻapps.py imports the ʻAppConfig object of the django.appsmodule. Don't forget to setname ='application folder name'where you created the class below. By the way, if you do not add'app.apps.AppConfig'` after creating the application folder, an error will be thrown (experience story). The bottom part is the setting for using bootstrap with Django, so add this as well.


'builtins':[
                'bootstrap4.templatetags.bootstrap4',#Add here!
            ],

Don't forget to add this as it is also a specification for using bootstrap.

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

Specify the time zone and Djnago setting language.


# Custom User
AUTH_USER_MODEL = 'app.User'


It is a setting for the custom user to be created later.


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


It becomes the access setting of the media file. It is used when implementing the upload function and the function to display the uploaded file as content on the screen in the application. This completes the basic preparations for using Django.

Creating a template

From here, we will create a template. Since Django is a framework, it's made up of MVC models like Laravel. This is called the MVT model in Django.

In other words, it's a Model / Tenmplaete / View model. As a role

Model: Database control Template: Control the screen. View's role in MVC model View: The role of the overall control. Role of controller in MVC model

It will be. Note that the View in the MVC model and the View in the MTV model have different roles. Therefore, Django can also use templates, so we will set them.

template/base.html




<!DOCTYPE html>

{% load bootstrap4 %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='slim' %}
{% load static %}

<html lang="ja">
<head>
	<meta charset="UTF-8">
	<link rel="stylesheet" type='text/css' href="{% static 'css/style.css' %}">
	<link rel="stylesheet" href="https://cccabinet.jpn.org/bootstrap4/css/style.css">
	<title>Django development sample</title>

	{% block extra_css %}{% endblock %}

</head>
<body>
	<!-- navbar -->
	<div class="container">
		<div class="row">
			<div class="col-12">
				<nav class="navbar navbar-expand-lg fixed-top navbar-dark bs-navbar" style="background-color:cadetblue;" id='bs-navbar'>
					<a class="navbar-brand mr-md-2" href="/app/top_page">
Sample development screen
					</a>

					<!-- layout_dammy -->
					<ul class="navbar-nav mr-auto">
						<li class="nav-item"><a  class='nav-link' href="#"></a></li>
						<li class="nav-item"><a  class='nav-link' href="#"></a></li>
					</ul>

					<!-- layout -->
					<ul class="navbar-nav">
						<li class="nav-item"><a class="nav-link" href="/#">Sample 1</a></li>
						<li class="nav-item"><a class="nav-link" href="/#">Sample 2</a></li>
						<li class="nav-item"><a class="nav-link" href="/#">Sample 3</a></li>
						<li class="nav-item"><a class="nav-link" href="/accounts/login">Login</a></li>
						<li class="nav-item"><a class="nav-link" href="/accounts/logout">Log out</a></li>
					</ul>
				</nav>
			</div><!-- /.col-12 -->
		</div><!-- /.row -->
	</div>

	<!-- sidebar&main-contents -->
	<div class="container-fluid" id="content">
		<div class="row flex-xl-nowrap">
			<!-- sidebar -->
			<div id="sidemenu" class="col-2 d-none d-md-block bg-light sidebar align-self-start">
				<div class="sidebar-sticky">
					<ul class='nav flex-column'>
						<li class="nav-item">
							<nav class="navbar navbar-light bg-light">
								<a class="navbar-brand" href="/#">
									<span data-feather=""></span>TOP page
								</a>
							</nav>
						</li>

						<li class="nav-item">
							<nav class="navbar navbar-light bg-light">
								<a class="navbar-brand" href="/#">
									<span data-feather=""></span>Sidebar 1
								</a>
							</nav>
						</li>	

						<li class="nav-item">
							<nav class="navbar navbar-light bg-light">
								<a class="navbar-brand" href="/#">
									<span data-feather=""></span>Sidebar 2
								</a>
							</nav>
						</li>	

						<li class="nav-item">
							<nav class="navbar navbar-light bg-light">
								<a class="navbar-brand" href="/#">
									<span data-feather=""></span>Sidebar 3
								</a>
							</nav>
						</li>	

						<li class="nav-item">
							<nav class="navbar navbar-light bg-light">
								<a class="navbar-brand" href="/#">
									<span data-feather=""></span>Sidebar 4
								</a>
							</nav>
						</li>	

						<li class="nav-item">
							<nav class="navbar navbar-light bg-light">
								<a class="navbar-brand" href="/#">
									<span data-feather=""></span>Sidebar 5
								</a>
							</nav>
						</li>
					</ul>
				</div><!-- /.sidebar-sticky -->
			</div><!-- /#sidemenu -->

			<div class="col-10">
				{% block contents %}
				{% endblock %}
			</div><!-- /.col-10 -->
		</div><!-- /.row -->
	</div><!-- /#content -->
</body>
</html>

<!-- sidebar-icon -->
<!-- use-icon: https://voyager-jp.com/blog/javascript/feather/  -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/feather-icons/4.9.0/feather.min.js"></script>
<script>feather.replace()</script>


First, create a template for the part common to each page. It's a header or a navigation bar. Notations like {%%} are spells used in templates. Here are some excerpts



{% load bootstrap4 %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='slim' %}
{% load static %}


Loads the bootstrap module, jQuery, and static files. If you don't specify it, bootstrap will not be applied, so don't forget. If there is a spell like a command like this


 <link rel="stylesheet" type='text/css' href="{% static 'css/style.css'%}">



You can also use it to simplify the path specification. By the way

{% block %}

{% endblock %}

That is the designation of the area. For example

{% block extra_css %}{% endblock %}

This means that block to ʻendblock will be changed to block named ʻextra_css. The block specified in this way becomes the default value, and when it is inherited by the child template below

The area if the name of block is not specified, such as{% block%} ~ {% endblock%}, or if{% block extra_css%} {% endblock%}is specified. Will be inserted.

Therefore

{% block contents %}
{% endblock %}

If you cast a spell like the one above, you can see that the part of the child template that casts the same spell is inserted.

By the way, this time I'm loading Bootstrap on the CDN, but there are various problems with the CDN, so if possible I would like to download the package and load the one that I put locally.

After that, I will make a child template

templates/app_folder/top_page.html



{% extends 'base.html' %}
{% block contents %}

<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center mt-5 border-bottom">
    <h1 class="h2">top page</h1>
</div>

<div>
    <h4>Describe the content here</h4>
</div>

{% endblock %}


templates/app_folder/page01.html



{% extends 'base.html' %}
{% block contents %}

<h4 class="mt-5 mb-4 border-bottom">Enter search conditions</h4>
<form action="{% url 'app:top_page' %}" method="post">
	<div class="container-fluid">
		<div class="row">
			<div class="col-2">
				<input class="form-control" type="next" name="input_data" maxlength="255">
			</div><!-- /.col-2 -->
			<div class="col-2">
				<div class="text-left align-bottom">
					<button type="submit" name="button" class="btn btn-info">
Search
					</button>
				</div><!-- /.text-left -->
			</div><!-- /.col-2 -->
		</div><!-- /.row -->
	</div><!-- /.container-fluid -->
	{% csrf_token %}
</form>

{% endblock %}

templates/app_folder/page02.html


{% extends 'base.html' %}
{% block contents %}

<h4 class="mt-5 mb-4 border-bottom">Display DB search results</h4>
<div class="container-fluid">
  <div class="row">
    <div class="col-4">
      <p>sample1:{{result_sample1}}</p>
      <p>sample2:{{result_sample2}}</p>
    </div>
  </div>
</div>

{% endblock %}

The {% extends'base.html'%} spell inherits the previous parent template. The contents of page01 and page02

・ Receive search conditions from the input form on the screen -Obtain data from DB based on search conditions -Pass the acquired data to the template (screen)

It means that you have created a UI to perform the process on the management screen.

Views.py and models.py settings

Now that the template is finished, set the View and the Model as well.

Quoting from the article of the planner listed in the reference article

app_config / views.py: Rough control over the whole app_folder / views.py: Control individual features

Note that the same views.py as, but with different roles, which is confusing. By the way, this time

ʻApp_config / views.py is project / project / views.py (located in the same directory as settings.py). Replace ʻapp_folder / views.py with project / app / views.py.

project/project/views.py




from django.shortcuts import render, redirect, reverse  
from django.views import View  

class index(View):
    def get(self, request, *args, **kwargs):
        return redirect(reverse('app:top_page'))
index = index.as_view()



If the system is accessed, it will be skipped to the top page.

project/app/views.py




from django.shortcuts import render  
from django.views import View  
from .models import SampleDB

class SampleView(View):  
    def get(self, request, *args, **kwargs):  
        return render(request, 'app_folder/page01.html')

    def post(self, request, *args, **kwargs):
     	input_data = request.POST['input_data']
     	result = SampleDB.objects.filter(sample1=input_data)
     	result_sample1 = result[0].sample1
     	result_sample2 = result[0].sample2
     	context={'result_sample1':result_sample1, 'result_sample2':result_sample2}
     	return render(request, 'app_folder/page02.html', context=context,)

top_page = SampleView.as_view()



Be careful of typos.

Don't make a mistake because from and import at the beginning are spells. The last part is to import the table objects from the Model.


def get(self, request, *args, **kwargs):  
    return render(request, 'app_folder/page01.html')


The render method will be a method that returns an HttpResponse object. By setting a request in the argument and specifying the path of the screen where you want to return the result in the second argument, the result including the information around session will be returned there.


def post(self, request, *args, **kwargs):
     	input_data = request.POST['input_data']
     	result = SampleDB.objects.filter(sample1=input_data)
     	result_sample1 = result[0].sample1
     	result_sample2 = result[0].sample2
     	context={'result_sample1':result_sample1, 'result_sample2':result_sample2}
     	return render(request, 'app_folder/page02.html', context=context,)


This is the method to receive the POST request from the input form. In the context variable, the process is to map from the "variable name" passed to the template to the "variable value" and pass it to the variable with the render method. You will return it to ʻapp_folder / page02.html`.

It's just right, so let's set the Model here as well.

project/app/models.py



from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    pass

class SampleDB(models.Model):
	class Meta:
		db_table = 'sample_table' # tablename
		verbose_name_plural = 'sample_table' # Admintablename
	sample1 = models.IntegerField('sample1', null=True, blank=True) #Variables and settings to store numbers
	sample2 = models.CharField('sample2', max_length=255, null=True, blank=True) #Variables and settings to store strings


Each class is each table. In other words, if you write like this, the User table and SampleDB will be created. Since the table used in the template created this time is SampleDB, create a class, set models.Model as an argument, and write the table settings under class Meta.

db_table = 'sample_table' # tablename
verbose_name_plural = 'sample_table' # Admintablename

This part is the setting of the table name and the table name displayed on the management screen.


sample1 = models.IntegerField('sample1', null=True, blank=True) #Variables and settings to store numbers
sample2 = models.CharField('sample2', max_length=255, null=True, blank=True) #Variables and settings to store strings


By writing like this, you will set the data of the table. In other words, this table will store ID and string data.

URL dispatcher settings

Set the URL required to access the system from the web screen. Is it a routing setting in Laravel?

Just like views.py

app_config / urls.py: System-wide URL design app_folder / urls.py: URL design within individual apps

Please note that it has the same file name as and has a different role. By the way, this time

ʻApp_config / views.py is project / project / urls.py (located in the same directory as settings.py). Replace ʻapp_folder / views.py with project / app / urls.py.

project/app/urls.py



from django.urls import path
from . import views

# URL_setting
app_name = 'app'
urlpatterns = [
    path('top_page/', views.top_page, name='top_page')
]


Write the path of each page in the part of ʻurl patterns`. This time, only the TOP page is created (page01 and page02 are excluded because they are templates for the management screen), so there is only one.

project/project/urls.py




"""project URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from . import views

urlpatterns = [
	#URL to access the management site
    path('admin/', admin.site.urls),
    #URL to access the application "app" created this time
    path('app/', include('app.urls')),
    #If you do not specify any URL (app_config/views.With py, "app" automatically_(Already set to access "folder")
    path('', views.index, name='index'),
]

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


Creating administrative users and management screens

Finally, create a management screen. Because you have to create an admin user for that Enter the container with docker exec -it app_python bash

cd ./project/

python manage.py createsuperuser


And cast the spell. What do you set your user name, email address, and password to when you get stuck? You will be asked, so please set it arbitrarily. Next is the setting of the management site.

project/app/admin.py




from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from .models import User
from.models import SampleDB

admin.site.register(User, UserAdmin)
admin.site.register(SampleDB)


The management site is

Normally, you need to learn MySQL etc. to operate DB Even though Python (Django) is full, MySQL is impossible. .. Let's operate the DB on the screen from the management site!

So, set it as above according to the table to be used.

System start-up

Now let's boot the system.

Before that, migrate the table and apply css at the management site. I did the migration once a long time ago, but since I have defined a new SampleDB table, I will do it again. This is the spell. Enter the container with docker exec -it app_python bash

cd ./project/


python manage.py migrate


Next is the application of css. This would be done when using uWSGI. The spells are:

python manage.py collectstatic


Finally when it's done safely


python manage.py runserver


The system is started. If no error is thrown, the startup is completed successfully. Thank you for your hard work. If that doesn't work, try docker-compose up -d again or see the error message as appropriate.

Finally

You can see how it works when you have lived the experience of doing Laravel. However, after all, I'm going to use the built-in method of the template and how to write the spell of the Model part in the first place, and I can not remember unless I check it each time and repeat the input and output steadily. I felt the same as when I made the portfolio in. Since I will dare to participate in the project this time with the skill level of Laravel on the shelf, I will understand Django and the skill level of the Web knowledge that accompanies it so that I can steal even a little contribution and stealing in the project. I hope it can be raised even a little. First, take a look at Django's official tutorial.

Reference site

Articles that enable system development with Django (Python)_Introduction Create a django environment with docker-compose (MariaDB + Nginx + uWSGI) Docker-Compose with Python3.6 + NGINX + MariaDB10 + uWSGI's Django environment greedy set Differences between web server and application server in Rails development (translation) Introduction to uWSGI About "uWSGI" and Application Server I will explain the difference between a web server and an application server in an easy-to-understand manner What is the role of the AP server? Let's understand the difference from the Web server

Recommended Posts

Learning history to participate in team application development with Python ~ Build Docker / Django / Nginx / MariaDB environment ~
Learning history to participate in team application development in Python ~ After finishing "Introduction to Python 3" of paiza learning ~
Build Django + NGINX + PostgreSQL development environment with Docker
[Python] Build a Django development environment with Docker
Build a machine learning application development environment with Python
Build a Django development environment with Docker! (Docker-compose / Django / postgreSQL / nginx)
Learning history for participating in team app development in Python ~ Django Tutorial 5 ~
Learning history for participating in team application development in Python ~ Index page ~
Learning history for participating in team app development in Python ~ Django Tutorial 4 ~
Learning history for participating in team app development in Python ~ Django Tutorial 1, 2, 3 ~
Learning history for participating in team app development in Python ~ Django Tutorial 6 ~
Learning history for participating in team app development in Python ~ Django Tutorial 7 ~
Create Nginx + uWSGI + Python (Django) environment with docker
Learning history for participating in team application development in Python ~ Supplement of basic items and construction of jupyterLab environment ~
How to build a Django (python) environment on docker
Build a development environment with Poetry Django Docker Pycharm
[Django] Build a Django container (Docker) development environment quickly with PyCharm
How to build a python2.7 series development environment with Vagrant
Build Mysql + Python environment with docker
Application development with Docker + Python + Flask
Learning history for participating in team application development in Python ~ Think a little about requirement definition ~
[DynamoDB] [Docker] Build a development environment for DynamoDB and Django with docker-compose
Build Jupyter Lab (Python) environment with Docker
Method to build Python environment in Xcode 6
Create Python + uWSGI + Nginx environment with Docker
Build AI / machine learning environment with Python
Build NGINX + NGINX Unit + MySQL environment with Docker
Environment maintenance made with Docker (I want to post-process GrADS in Python
I tried to build a Mac Python development environment with pythonz + direnv
Build a development environment using Jupyter and Flask with Python in Docker (supports both VS Code / code-server)
Build Python + django + nginx + MySQL environment using docekr
Build the fastest Django development environment with docker-compose
Build Python development environment with Visual Studio Code
Go (Echo) Go Modules × Build development environment with Docker
Build a Django environment with Vagrant in 5 minutes
Build a Django development environment with Doker Toolbox
Quickly build a Python Django environment with IntelliJ
Build PyPy and Python execution environment with Docker
Build a Python machine learning environment with a container
Flutter in Docker-How to build and use a Flutter development environment inside a Docker container
Python local development environment construction template [Flask / Django / Jupyter with Docker + VS Code]
How to run a Django application on a Docker container (development and production environment)
Build a local development environment with WSL + Docker Desktop for Windows + docker-lambda + Python
Build an interactive environment for machine learning in Python
Japanese can be used with Python in Docker environment
Launch a Python web application with Nginx + Gunicorn with Docker
[No need to build local environment] Deploy Python bottle application made with Cloud9 to Heroku
Web application made with Python3.4 + Django (Part.1 Environment construction)
Explaining how to make LINE BOT in the world's easiest way (2) [Preparing a bot application in a local environment with Django in Python]
A memo about building a Django (Python) application with Docker
[Django] Use VS Code + Remote Containers to quickly build a Django container (Docker) development environment.
[Cloud9] Try to build an environment with django 1.11 of Python 3.4 without understanding even 1 mm
Development environment in Python
[AWS] Development environment version that tried to build a Python environment with eb [Elastic Beanstalk]
How to use Docker to containerize your application and how to use Docker Compose to run your application in a development environment
[Learning memo] How to make an application with Django ~ From virtual environment to pushing to github ~
Setting to run application in subdirectory with nginx + uwsgi
Create a django environment with docker-compose (MariaDB + Nginx + uWSGI)
I tried to build an environment for machine learning with Python (Mac OS X)
How to build Anaconda virtual environment used in Azure Machine Learning and link with Jupyter
Try running python in a Django environment created with pipenv