[PYTHON] Master Django Fastest Part1

at first

Recently, I think that many people choose Python for reasons such as "strong in machine learning" and "easy".

That's the wall of Django that people run into when they want to write the Web. (That's not the case? No, it's difficult!)

At first, I did only the Django Tutorial in Japanese, and after that I read all the English literature and studied by looking at the company code, but it was quite difficult.

I want to write the web, but ◯ uby is easier to write than Python, so ◯ uby is better ... So that I don't end up with Django, I've packed the basics and applications of Django that I've studied so far. I will write articles that can be mastered at the fastest speed! (It will be 3 or 4 articles long)

* Master Django fastest part2

Preparation

If you don't have Django,

$ pip install django

It seems that you can install it with.

I downloaded Python from a distribution called Anaconda, so it was included from the beginning. It's okay to eat up the capacity of your computer! I recommend you to download it from Anaconda. Besides Django, it's convenient because it's included from the beginning!

* Click here to install Anaconda * Easy-to-understand article about Anaconda installation

Finally start

For those who can already write the Web when studying the Web

I almost always hear that

You are told! !! !!

It's certainly a good study to make everything by yourself, but I think it's difficult (or almost impossible?) To make everything from nothing.

So, in this article, I'm going to proceed with the feeling of "** Learn Django while making one app in order **".

We will also create a repository on GitHub, so please refer to that for the code as well. (If there are people who say "It was helpful!" Throughout not only this article but also future articles, please add a star!)

* Click here for GitHub repository

What do you make

Since I want to implement various functions, the content of the app emphasizes practicality rather than fun.

As for the content, I would like to create a "management tool".

We are assuming two types of users, manager and worker. The manager creates a work set for the worker to take charge of, checks the working status of the worker, and checks the worker's work when it is completed. Workers, on the other hand, read help pages about their jobs and check their working conditions.

If you come up with other interesting functions, I will implement them, but for the time being, I would like to implement them in this area.

Django basics

First, create a project. In Django, one application is called a project, and several applications are running under one project.

Move to the directory where you want to create the application and execute the following command. (Manager_project is the name I gave to this application, so anything is fine in reality.)

$ django-admin startproject manager_project

The project has been created.

-- manager_project/
    -- manager_project/
        -- __init__.py
        -- setting.py
        -- urls.py
        -- wsgi.py
    -- manage.py

I think that the file structure is.

Of these, the ones we mainly use are setting.py, ʻurls.py, and manage.py. (However, this is only used for one point, and the main development will be models.py and views.py` to be created from now on)

Next, let's make an app.

$ cd path/to/manager_project
(If you continue to hit from the previous command,$ cd manager_Just project is fine. This directory"master directory"I will call it)

#For confirmation
$ ls
manage.py manager_project
#Is it?(One level higher manager_Please enter into the project)

$ python manage.py startapp manager

You should now have an app called manager.

The file structure in manager is as follows.

-- manager
    -- migrations
    -- __init__.py
    -- admin.py
    -- apps.py
    -- models.py
    -- tests.py
    -- views.py

Of these, the ones I often use are models.py, views.py, and tests.py.

I usually play around here.

Now you have to report to the project that you've created an app! Here, change setting.py as follows.

setting.py


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'manager',  #Additional part
]

The app is now complete.

Before we actually write the code, let's take a look at how Django responds after receiving a request.

From receiving a request to returning a response

スクリーンショット 2017-07-25 22.19.12.png

It's simple, isn't it? That's it!

First the client sends an http request (like https://google.com!)

In urls.py, there are some url regular expressions and corresponding views, and if there is a matching url, go to that View, otherwise it will return something like "page not found" (403) Or 404).


As an aside, I would like to introduce a site that is extremely useful when writing regular expressions! Online Regrex Tester


View is written in views.py and is the one that does the main operation. The necessary data from the database is fetched from the template as the original html file, the two are mixed, and returned as an HTTP response.

This allows the client to see a page with the data they want in a clean format!

Let's write a Model

So let's actually write the code.

There are things that I want to implement first, such as the login function and Hijack function, but it will be a little complicated, so this time I will implement a screen that displays a list of workers.

Create a model and create data. (Because it is the beginning, I will put all the code. Since it will be long, I will put only a part from the next time.)

models.py


from django.db import models


class Person(models.Model):

    MAN = 0
    WOMAN = 1

    HOKKAIDO = 0
    TOHOKU = 5
    TOKYO = 10
    CHIBA = 11
    KANAGAWA = 12
    SAITAMA = 13
    TOCHIGI = 14
    IBARAGI = 15
    CHUBU = 20
    KANSAI = 25
    CHUGOKU = 30
    SHIKOKU = 35
    KYUSHU = 40
    OKINAWA = 45

    #name
    name = models.CharField(max_length=128)
    #birthday
    birthday = models.DateTimeField()
    #sex
    sex = models.IntegerField(editable=False)
    #Birthplace
    address_from = models.IntegerField()
    #Current address
    current_address = models.IntegerField()
    #mail address
    email = models.EmailField()


class Manager(models.Model):

    #Department constant
    DEP_ACCOUNTING = 0  #accounting
    DEP_SALES = 5  #Sales
    DEP_PRODUCTION = 10  #Manufacturing
    DEP_DEVELOPMENT = 15  #development of
    DEP_HR = 20  #human resources
    DEP_FIN = 25  #Finance
    DEP_AFFAIRS = 30  #General affairs
    DEP_PLANNING = 35  #Planning
    DEP_BUSINESS = 40  #business
    DEP_DISTR = 45  #distribution
    DEP_IS = 50  #Information system

    #Man
    person = models.ForeignKey('Person')
    #Department
    department = models.IntegerField()
    #When to arrive
    joined_at = models.DateTimeField()
    #When I quit
    quited_at = models.DateTimeField(null=True, blank=True)


class Worker(models.Model):

    #Man
    person = models.ForeignKey('Person')
    #When to arrive
    joined_at = models.DateTimeField()
    #When I quit
    quited_at = models.DateTimeField(null=True, blank=True)
    #Boss in charge
    manager = models.ForeignKey('Manager')


Here is the code description. Model classes are basically created by inheriting models.Model.

The attribute must specify its data type, for example ʻIntegerField` for integer values.

--CharField: Character string (max_length must be specified for memory management) --IntegerField: Integer --DateTimeField: Time

There is also an attribute to put raw data such as

There are also attributes that represent the relationships between models, such as. Model field reference (list of fields)

For example, ForeignKey is a field that indicates a "1: many" relationship. In the Manager class,

person = models.ForeignKey('Person')

This means that person and manager show a "1: many" relationship. (The same person may become a manager in a different department several times!)

(※Caution: Starting with Django 2.0, ForeignKey requires the on_delete argument. Reference: https://qiita.com/lemmy/items/5fa7f6e5ca29d2446174 )

Also,

#Department constant
DEP_ACCOUNTING = 0  #accounting
DEP_SALES = 5  #Sales
DEP_PRODUCTION = 10  #Manufacturing
DEP_DEVELOPMENT = 15  #development of
DEP_HR = 20  #human resources
DEP_FIN = 25  #Finance
DEP_AFFAIRS = 30  #General affairs
DEP_PLANNING = 35  #Planning
DEP_BUSINESS = 40  #business
DEP_DISTR = 45  #distribution
DEP_IS = 50  #Information system

The method of setting constants is often used.

The numbers are skipped so that you can handle when you want to put something in between.


When you create a new model (class) or change the attributes in the model, you need to reflect the change in the database table.

Now, execute the following command from ** master directory **.

$ python manage.py makemigrations
$ python manage.py migrate

The changes will now be reflected in the database.

SQLite is used by default in Django, but I usually use MySQL. The database around here will be a long story, and development can be done without much concern, so I will ignore it in this article.

Let's create data based on this model.

For example, when you're messing with the database, go to Django's Shell.

Execute the following command from ** master directory ** to see it.

$ python manage.py shell
スクリーンショット 2017-07-17 23.39.01.png

I think the screen will look like this! (The smile mark and the current GitHub branch name are displayed on the right is because you are changing the terminal from bash to zsh. You can see the command break and the current branch name. It's very convenient, so refer to this article Please change it!)

You're now in Django's Shell! I will create the data here.

I made the place of origin and gender quite appropriately.

import datetime
import pytz
from manager.models import Person, Manager, Worker

for i in range(200):
      birthday = datetime.datetime(year=1980 + i % 20, month=1 + i % 12, day=1 + i % 28, tzinfo=pytz.timezone('Asia/Tokyo'))
      Person.objects.create(name="person{}".format(i), birthday=birthday, sex=Person.MAN, address_from=Person.TOKYO, current_address=Person.TOKYO, email="person{}@gmail.com".format(i))

First, create data based on Person.

I will omit the datetime and pytz.

There are two ways to create an object.

1) MODEL_NAME.objects.create(kwargs)

2) obj = MODEL_NAME(kwargs)
   obj.save()

# MODEL_NAME is the name of the model class
#ksargs is a keyword argument

is.

The create function even saves it to the database.

By the way, to delete the created model instance (individual data), do as follows.

obj.delete()  #obj is an instance

Feel free to create data like this! (I made 1200 people by changing their place of origin and gender)

About the contents of range

range(200)
range(200, 300)
range(300, 400)
...

If you change it like this, you don't have to cover your name or email address.

We will create not only Person but also Manager linked to Person.

dep_list  = [Manager.DEP_ACCOUNTING, Manager.DEP_SALES, Manager.DEP_PRODUCTION, Manager.DEP_DEVELOPMENT, Manager.DEP_HR, Manager.DEP_FIN, Manager.DEP_AFFAIRS, Manager.DEP_PLANNING, Manager.DEP_BUSINESS, Manager.DEP_DISTR, Manager.DEP_IS]

for i in range(1, 201):
    p = Person.objects.get(id=i)
    joined_date = datetime.datetime(year=2005 + i % 10, month=1 + i % 12, day=1 + i % 28, tzinfo=pytz.timezone('Asia/Tokyo'))
    Manager.objects.create(person=p, department=dep_list[i % 11], joined_at=joined_date)

There are several ways to get a Person object from a database (see below), but here we use one of them, get.

I am creating a Manager that is associated with each Person instance whose id is 1 to 200.

Workers made the same with the remaining 1000 people.

for i in range(201, 1201):
    p = Person.objects.get(id=i)
    m = Manager.objects.get(id=1 + i % 200)
    joined_date = datetime.datetime(year=2005 + i % 10, month=1 + i % 12, day=1 + i % 28, tzinfo=pytz.timezone('Asia/Tokyo'))
    Worker.objects.create(person=p, manager=m, joined_at=joined_date)

The get function has a weakness. The point is that when an instance (object) that meets the conditions is not found, an error (ObjectDoesNotExist) that is not an HTTP error is returned.

In other words, when you type in a strange URL, you will get an error, which is inconvenient (there are some security issues).

Here, I used the get function on the assumption that the object always exists if the id is in this range, but usually when I get the object in views.py etc., I use the get_object_or_404 function.

views.py


from django.shortcuts import get_object_or_404

get_object_or_404(Person, id=20)

Use it like this.

This will return an HTTP 404 error even if the object doesn't exist, so you can display it like Page Not Found. (You can also create your own page not found screen and display it every time you get a 404)

This is safe for both the purpose you saw and the security.

Therefore, you can use the get function that you can easily apply under the premise that "an object always exists", but if you want to get one object in other situations, use the get_object_or_404 function.


Next, write a view and display the page!

Write View

Before writing the view, write ʻurls.py`.

urls.py


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

import manager.views as manager_view

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^worker_list/', manager_view.WorkerListView.as_view())  #Combine URL and View!
]

He was finding a View that matched the URL.

First, we will create a page that displays a list of workers, so we combine the URL base_url / worker_list / with the View WorkerListView!

Next, write a View.

views.py


from django.shortcuts import render, redirect, get_object_or_404
from django.views.generic import TemplateView

from manager.models import *


class WorkerListView(TemplateView):
    template_name = "worker_list.html"

    def get(self, request, *args, **kwargs):
        context = super(WorkerListView, self).get_context_data(**kwargs)
        return render(self.request, self.template_name, context)

View was a mixture of data and templates.

Here, first, let's display only the template without extracting the data.

manager
    - templates
        - worker_list.html

Create a folder called templates and a file called worker_list.html so that the hierarchy is as follows.

By doing this, Django will recognize that worker_list.html is a template.

To briefly explain the contents of the get function, it is like creating a container for data called context and mixing the template and context with the render function (also request).

Keep the html file simple as follows.

worker_list.html


It Works!!

Try to display the page

First, launch the Django server.

Execute the following command from the master directory.

$ python manage.py runserver 8000

You don't have to add the 8000 at the end. (I just wanted to say that you can specify the port number at the end!)

Let's make a request with the url we created earlier.

スクリーンショット 2017-07-18 22.17.39.png

You can now display it!

What are you going to do now?

I skipped the following two points.

--Getting data with View --Making HTML gorgeous

Let's do these two points.

Get data from database in View

views.py


def get(self, request, *args, **kwargs):
        context = super(WorkerListView, self).get_context_data(**kwargs)

        workers = Worker.objects.all()  #Get the object from the database
        context['workers'] = workers  #Put in a container
        
        return render(self.request, self.template_name, context)

When retrieving the database, write like this.

workers = Worker.objects.all()

The meaning is "take all the objects associated with the Worker model".

You can take some instead of all.

workers = Worker.objects.filter(person__sex=Person.MAN)

Then you will only get workers whose gender is male.


person__sex means the sex attribute of the Person object in the person attribute of the worker (complex in words? Lol).

When you refer to a constant in another class, like Person.MAN, you need to give it a class name (which is the basis of Python!).


Also,

context['workers'] = workers

By doing so, the variable workers will be enabled in the html file! (The content of [''] is the variable name that can be used in the html file.)

Write a Django template

Let's display the workers in a tabular format.

worker_list.html


<!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">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Worker List</title>
</head>
<body>
  <table>
    <thead>
      <tr>
        <th>ID</th>
        <th>name</th>
        <th>sex</th>
        <th>birthday</th>
      </tr>
    </thead>
    <tbody>
        {% for worker in workers %}
        <tr>
          <td>{{worker.id}}</td>
          <td>{{worker.person.name}}</td>
          <td>{{worker.person.sex}}</td>
          <td>{{worker.person.birthday}}</td>
        </tr>
        {% endfor %}
    </tbody>
  </table>
</body>
</html>

Django provides a template language so that you can (slightly) use Python in your HTML files. Build in tamplate tags and filters

here,

{% for worker in workers %}
{% endfor %}

Is that!

You can use workers here because you put it in context in View!

Other,

--{% block variable%} {% endblock%} like {% block body%} {% endblock%} --{% load variable%} --{% if condition%} {% else%} {% endif%}

I often use such things! (Let's actually use it below!)

Insert BootStrap

CSS and JavaScript are quite annoying to write in full, so I will include Bootstrap. * Bootstrap page

It's easy because it originally wrote CSS and JavaScript. Here we use SB admin2. (Very versatile!)

スクリーンショット 2017-07-19 23.23.45.png

Download from the red circle.

Unzip the Zip file and copy the files inside to / static / bootstrap /.

Then delete everything except the folders named dist, js, vendor.

スクリーンショット 2017-07-19 23.37.59.png

I feel like this.

Load these files from HTML.

However, it is troublesome to write or copy link or script in the head element every time.

So, create an html file that will be the base and extend it (it's hard to understand if it's written ?? lol).

Create base.html under/ manager / templates /.

base.html


<!DOCTYPE html>
<html lang="ja">
  <head>
    {% load staticfiles %}
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">

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

    <link href="{% static 'bootstrap/vendor/bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
    <link href="{% static 'bootstrap/vendor/metisMenu/metisMenu.min.css' %}" rel="stylesheet">
    <link href="{% static 'bootstrap/dist/css/sb-admin-2.css' %}" rel="stylesheet">
    <link href="{% static 'bootstrap/vendor/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet" type="text/css">

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

    <script type="text/javascript" src="{% static 'bootstrap/vendor/jquery/jquery.min.js' %}"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/jquery-ui.min.js"></script>

    <script src="{% static 'bootstrap/vendor/bootstrap/js/bootstrap.min.js' %}"></script>
    <script src="{% static 'bootstrap/vendor/metisMenu/metisMenu.min.js' %}"></script>
    <script src="{% static 'bootstrap/vendor/datatables/js/jquery.dataTables.min.js' %}"></script>
    <script src="{% static 'bootstrap/vendor/datatables-plugins/dataTables.bootstrap.min.js' %}"></script>
    <script src="{% static 'bootstrap/vendor/datatables-responsive/dataTables.responsive.js' %}"></script>
    <script src="{% static 'bootstrap/dist/js/sb-admin-2.js' %}"></script>

    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
        <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div id="wrapper">
      <nav class="navbar navbar-default navbar-static-top manager-nav no-margin" role="navigation">
        <div class="navbar-header">
          <a class="navbar-brand">gragragrao Company</a>
        </div>
        <div class="navbar-default sidebar" role="navigation">
          <div class="sidebar-nav navbar-collapse">
            <ul class="nav" id="side-menu">
              <li><a href="/worker_list/"><i class="fa fa-bar-chart" aria-hidden="true"></i>Worker list</a></li>
            </ul>
          </div>
        </div>
      </nav>
      {% block body %}
      {% endblock %}
    </div>
  </body>
</html>

For {% block title%} {% endblock%} and {% block body%} {% endblock%}, substitute the script contained in the same block in the extended HTML file as it is. Is to do.

Extend this to create worker_list.html!

worker_list.html


{% extends "base.html" %}
{% block title %}Worker List{% endblock %}
{% load staticfiles %}

      {% block body %}
      <div id="wrapper">
        <div id="page-wrapper">
          <div class="row">
            <div class="col-lg-6 full-width margin-top-20percent" >
              <div class="panel panel-default full-width">

                <div class="panel-heading">
                  Edit Help
                </div>

                <div class="panel-body full-width full-height">
                  <table id="worker-list-table" class="table table-striped table-bordered table-hover dataTable no-footer dtr-inline full-width">
                    <thead>
                      <tr>
                        <th>ID</th>
                        <th>name</th>
                        <th>sex</th>
                        <th>birthday</th>
                      </tr>
                    </thead>
                    <tbody>
                    {% for worker in workers %}
                      <tr>
                        <td>{{worker.id}}</td>
                        <td>{{worker.person.name}}</td>
                        <td>{{worker.person.sex}}</td>
                        <td>{{worker.person.birthday}}</td>
                      </tr>
                    {% endfor %}
                    </tbody>
                  </table>
                </div>

              </div>
            </div>
          </div>
        </div>
      </div>
      <script>
        $(document).ready(function() {
            $('#worker-list-table').DataTable({
                responsive: true,
                //Disable sort function
                ordering: false,
                //Change the number of pages displayed
                displayLength: 20,
            });
        });
      </script>
      {% endblock %}


--{% extends filename%} to extend the original file --{% load staticfiles%} loads static files under / static /. (It cannot be read without this!)


However, what is static files as it is? So let's tell Django what it is with setting.py.

Add the following somewhere in the setting.py file.

setting.py


# Static file settings
STATIC_ROOT = os.path.join(BASE_DIR, 'assets')

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

After that, I will prepare a little CSS.

structure.css


/* ---------------------------------------------------------- */
/* general */
/* ---------------------------------------------------------- */

.full-height {
    height: 100%;
}

.full-width {
    width: 100%;
}

.margin-top-20percent {
    margin-top: 20px;
}

.no-margin {
    margin: 0 !important;
}

In base.html, this file is called as follows.

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

Files under static can be called like this!

So, let's put structures.css under/ static / manager / css /!


This is complete for the time being! !!

If it doesn't work, I would appreciate it if you could report it in the comments.

Launch Django's server from your terminal and see what the page looks like!

From the master directory, do the following!

$ python manage.py runserver

This will come out below

Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Let's connect to http://127.0.0.1:8000/worker_list/ from your browser!

スクリーンショット 2017-07-25 1.11.42.png

It turned out to be something like this!

It's been long, so I'll leave it around here this time!

From now on, let's extend this app and master Django!

It was helpful! If you like it, please like it!

* Reference GitHub repository

* Master Django fastest part2

Recommended Posts

Master Django Fastest Part1
Django begins part 4
Test Driven Development with Django Part 2
Django
Test Driven Development with Django Part 1
Test Driven Development with Django Part 5
[Django] Error encountered when deploying heroku Part 2
Deploy the Django app on Heroku [Part 2]
Deploy the Django app on Heroku [Part 1]
[Django] Trouble encountered when deploying heroku Part 1