Python Django Tutorial (5)

It is a material for study sessions This time, I will explain Form by taking a side road from the tutorial of the head family. Before entering the Form, I will introduce a little about updating django and namespace of url.

Tutorial Tutorial Tutorial 1 Tutorial 2 Tutorial 3 Tutorial 4

Other tutorials

Source (github)

Also, until the last time, I explained django1.8, but since django1.9 was released while I was skipping updates. In the following tutorials, we will explain django 1.9. Even if you change the version of django from 1.8 to 1.9, there is no special difference in the contents of tutorials 1 to 4.

Migration to django 1.9 and introduction of requirements.txt

Source → ʻee195d1`

django updates

You can update to the latest django (1.9.2 as of February 20, 2016) by typing the following command on the command line. If you are using a virtual environment, don't forget to activate the virtual environment with workon. (tutorial)$ pip install --upgrade django

(tutorial)$ pip install --upgrade django
Collecting django
  Using cached Django-1.9.2-py2.py3-none-any.whl
Installing collected packages: django
  Found existing installation: Django 1.8.6
    Uninstalling Django-1.8.6:
      Successfully uninstalled Django-1.8.6
Successfully installed django-1.9.2

If it finishes normally, it will uninstall the already installed django (1.8.6 in this case) as described above, and install the latest django.

requirements.txt In this tutorial, django is the only external library used so far, but the number of related libraries used in the future will increase steadily. Which library are you using at that time? , Is the version of the library the same? It is difficult to check each time. Fortunately, python has a package management system called pip and a virtual environment called virtualenv. You will not often suffer from this problem.

To get a list of libraries used in the current environment, type the following command in the shell.

(tutorial)$ pip freeze
Django==1.9.2
wheel==0.24.0

By convention in python, this content is output as requirements.txt. Another person can install the required libraries by importing this file. As you can see, the output contents also have a version number such as 1.9.2, so there is no need to worry about malfunctions due to different versions.

In the tutorial, I intended to put it already, but I did not create it, so I added it with commit: ʻee195d1`.

Output of the library you are using (creating requirements.txt)

(tutorial)$ pip freeze > requirements.txt

Just redirect the contents of pip freeze. In the case of django, it is recommended to put it in the same hierarchy as manage.py.

Import library

Use the pip install -r command to import. After confirming that it is workon in the environment you want to import, after -r Describe the path to the file you want to import (requirements.txt).

(tutorial)$ pip install -r requirements.txt

If you already have a library installed and want to update its version (tutorial)$ pip install -U -r requirements.txt You need the -U option like>. However, when I tried it at hand, it seems that if the version of pip is 8.0.2, it will be updated without adding it.

Add namespace to url

Source → bba5e4f

In Tutorial 3, I skipped the explanation of namespace conversion of url, Considering the future, it is convenient to separate them, so set namespace to polls url. To add it, just add namespace to the argument of the ʻinclude` function.

tutorial/urls.py


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

↓ Add namespace to the polls include argument

tutorial/urls.py


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

Be careful not to mistake the position of the closing parenthesis as an argument of the url function.

If you set namespace, you can pull url in the form of namespace: hoge. The namespace can also be written in a higher hierarchy, such as ʻapi: polls: create. I added the prefix poll_to the name of the include destinationpolls / urls.py`, If you use namespace, it will be unnecessary, so delete it.

polls/urls.py


urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'(?P<pk>\d+)/$', views.detail, name='poll_detail'),
    url(r'(?P<pk>\d+)/vote$', views.vote, name='poll_vote'),
    url(r'(?P<pk>\d+)/results$', views.results, name='poll_results'),
]

↓ Since the namespace is cut, the prefix named poll_ becomes unnecessary.

polls/urls.py


urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'(?P<pk>\d+)/$', views.detail, name='detail'),
    url(r'(?P<pk>\d+)/vote$', views.vote, name='vote'),
    url(r'(?P<pk>\d+)/results$', views.results, name='results'),
]

In django 1.9, instead of writing the namespace name in the include argument in tutorial / urls.py, You can also set it by writing ʻapp_name ='polls'inpolls / urls.py`. https://docs.djangoproject.com/en/1.9/releases/1.9/#passing-a-3-tuple-or-an-app-name-to-include

With this change, URLs are now pulled from names to polls: detail instead of poll_detail. Please check the source difference for the corrected part.

By cutting the namespace, the polls app will be more separated from the tutorial project and will be easier to use in other projects. With the previous method, if another application in the same project gives a name such as poll_detail to the URL, an error will occur. You need to care about the URL names of all the applications managed by the project. On the other hand, namespace is used to avoid conflicting namespace in root urls.py (tutorial / urls.py). You just have to be careful.

What is Form

Now, let's finally get into the story of Form. Form is used to pass data in a certain format from the client to the server, as the English translation of "form, application form" means. In Tutorial 4, Form was written directly in the template, and the processing after receiving was written on the view side. However, templates and views correspond to View and Controller in the MVC model, and it is not good for logic to be included here. Also, in the current format, the display of the radio button is described in the template, and its validation (determination of whether the selected data is correct), Furthermore, the processing using the input value (voting processing) is described in the view. However, because the input items, validation for them, and processing using the data are closely related. I want to handle these together. Form classes are available in django for general input (text, selectlists, radio buttons, etc.) and We provide the validator (input check).

The Form class makes it easier to write tests for Honke Tutorial 5. You can easily increase the number of input items, and you can easily reuse them in other places. Moreover, by linking with the class-based general-purpose View, the description of the view can be further reduced and made easier to understand.

Form class

Text field output

Source → 34698a1

First of all, let's try making a Form. Create a file called forms.py in the application folder and define the Form class in it. For the Form class, set the field class as a member. Please refer to the official document for the field class that can be set. https://docs.djangoproject.com/en/1.9/ref/forms/fields/

For the time being, let's set CharField, which is a field for inputting characters. CharField needs to specify the maximum number of characters (max_length) as a required argument, so set 100 characters for the time being.

polls/forms.py


from django import forms


class MyForm(forms.Form):
    text = forms.CharField(max_length=100)

Let's check the output to see what happens when we write this. Start a python shell with ./manage.py shell, create an instance of the Form class created earlier, and create an instance of the Form class. Let's print it.

$ ./manage.py shell
(InteractiveConsole)
>>> from polls.forms import MyForm
>>> f = MyForm()
>>> print(f)
<tr><th><label for="id_text">Text:</label></th><td><input id="id_text" maxlength="100" name="text" type="text" /></td></tr>

You can see that the text type input tag is output as the output. Since the field name is set to text, you can also confirm that the character string Text appears as the label.

Output to template, confirmation with browser

Source → 34f4914

Next, let's make the created Form class html. First, write the creation of Form and the passing to the template in views.py.

polls/views.py


from .forms import MyForm


def form_test(request):
    form = MyForm()
    return render(request, 'polls/form.html', {
        'form': form,
    })

Next, let's prepare a template. I specified a template path of polls / form.html, so the location of the file is It is polls / templates / polls / form.html.

polls/templates/polls/form.html


<html>
  <body>
    <form>
      {{ form }}
    </form>
  </body>
</html>

Now you should see the following string confirmed in the shell in {{form}}. <tr><th><label for="id_text">Text:</label></th><td><input id="id_text" maxlength="100" name="text" type="text" /></td></tr>

Finally, connect the form_test function and the url. Let's add the url to polls / urls.py.

polls/urls.py


urlpatterns = [
...
    url(r'^form$', views.form_test),
...
]

After writing so far, start the test server with ./manage.py runserver and check it with a browser. The url is http: // localhost: 8000 / polls / form.

Kobito.HyYPSO.png screen

It's a murderous scene, but for the time being, you can see that there is one input box.

Kobito.JxJa67.png html source

The html source looks like this. As expected, the {{form}} part has been replaced.

POST transmission process

Source → 06c8422

Well, the input box has been created, but there is no receiving process on the server side yet. When sending data to the server in html, describe ** where ** ** how ** data is sent in the <form> tag. Write the attributes ʻaction and method, respectively. Also, to send, you need to place the submit button inside the

tag. If you want to receive in the same view, you do not need to specify ʻaction. This time, I only write it in empty string.

Please note that csrf_token is required when sending data by POST.

Explanation of csrf → [Everyone loves wikipedia](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B5%E3% 82% A4% E3% 83% 88% E3% 83% AA% E3% 82% AF% E3% 82% A8% E3% 82% B9% E3% 83% 88% E3% 83% 95% E3% 82% A9% E3% 83% BC% E3% 82% B8% E3% 82% A7% E3% 83% AA)

Since POST communication is html data transmission to the server (= server information change), How it was entered is important. In django, you can add the csrf_token tag to ensure that the data was entered on your own page.

The modified html is as follows.

polls/templates/polls/form.html


<html>
  <body>
    <form action="" method="POST">
      {% csrf_token %}
      {{ form }}
      <input type="submit" value="Send"/>
    </form>
  </body>
</html>

Kobito.1nDDdU.png Confirm that the button is displayed in the browser

Even if you press the button, nothing happens because nothing is written after that, If you look at the log on the screen where runsever is executed, you can confirm that POST communication is being performed.

(tutorial)$ ./manage.py runserver 127.0.0.1:13000
Performing system checks...

System check identified no issues (0 silenced).
February 21, 2016 - 16:56:38
Django version 1.9.2, using settings 'tutorial.settings'
Starting development server at http://127.0.0.1:13000/
Quit the server with CONTROL-C.

[21/Feb/2016 17:34:28] "GET /polls/form HTTP/1.1" 200 343    #← Screen display
[21/Feb/2016 17:34:30] "POST /polls/form HTTP/1.1" 200 343   #← Click "Send" button

By the way, if you forget to write the {% csrf_token%} tag in html, it will be like this.

Kobito.Ffr1Uf.png

This check process is performed by MIDDLEWARE, and you can see that it is set by default by looking at settings.py.

tutorial/settings.py


MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',  #←←←←←←←← csrf check processing
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
)

Data reception processing

Source → 8ea545ce

The received POST data is stored in the request class with the name POST. Pass this data to the Form class, check the validity of the data, and if it is correct, perform some processing using that data.

The modified view looks like this.

polls/views.py


def form_test(request):
    if request.method == "POST":
        form = MyForm(data=request.POST)  #← Pass the received POST data
        if form.is_valid():  #← Confirmation of the validity of the received data
            pass  #← Processing when correct data is received
    else:  #← method is'POST'is not=Processing when the first page is displayed
        form = MyForm()
    return render(request, 'polls/form.html', {
        'form': form,
    })

After receiving the correct data, the data is registered in the DB etc. and the process such as redirecting to another page is performed. I haven't done anything this time. Let's check the operation from the browser in this state. You should see an error message displayed when you press the submit button without entering anything in Text.

Kobito.Bgm3aT.png

Field customization

Source → 5637b54

You can change the behavior and output of a field by passing an argument when creating it. For example, if you pass required = False, no error will occur even if there is no input value in that field. Also, you can change the character of Text by adding the label argument. Refer to the following for the arguments that can be specified. https://docs.djangoproject.com/en/1.9/ref/forms/fields/#core-field-arguments

This time, let's set the required and label explained earlier.

polls/forms.py


class MyForm(forms.Form):
    text = forms.CharField(max_length=100, required=False, label='text')

Kobito.WXYnel.png

Text is now Text. Also, I think you can confirm that the error message disappeared even if you pressed send without entering anything.

Voting Form Creation

Creating a Vote Form

Source → 9077adee

Let's rewrite the voting form created in Tutorial 4 using the Form class. The required input is a radio button to select a list of choices associated with the question model. ModelChoiceField can be used to select a model. This field requires a queryset to select the model, but when defining the field, for which question Since it is uncertain whether it is a form, we will override the __init__ method and receive it as an argument.

polls/forms.py


class VoteForm(forms.Form):
    choice = forms.ModelChoiceField(
        queryset=None,
        label='Choice',
        widget=forms.RadioSelect(),
        empty_label=None,
        error_messages={
            'required': "You didn't select a choice.",
            'invalid_choice': "invalid choice.",
        },

    )

    def __init__(self, question, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['choice'].queryset = question.choice_set.all()

Since the default widget of ModelChoiceField is a select list, specify RadioSelect for widget. Set queryset to None for the time being, and overwrite it with the argument in __init__. By setting error_messages, you can specify error messages when various invalid values are entered. If not specified here (required) and if a value not selected is entered (invalid_choice) An error message is set.

Next, let's put out the Form created by rewriting views.py and templates.

polls/views.py


from .forms import VoteForm


def detail(request, pk):
    obj = get_object_or_404(Question, pk=pk)
    form = VoteForm(question=obj)
    return render(request, 'polls/detail.html', {
        'form': form,
        'question': obj,
    })

polls/templates/polls/detail.html


<h1>{{ question.question_text }}</h1>

<form action="{% url 'polls:vote' question.id %}" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Vote" />
</form>

Kobito.5NwxDg.png Check with browser

The choice is Choice object. As I mentioned a little in Tutorial 2, the string representation of the instance is shaped like ModelName object by default. Open models.py and override the __str__ method.

polls/models.py


class Choice(models.Model):
...
    def __str__(self):
        return self.choice_text

Kobito.cya0WW.png

It has changed properly.

By the way, unlike the first screen, there is an extra black spot in front of each option, This is because by default each option is separated by a <li> tag. It is preferable to prevent black spots from appearing with css, If you really want to change the html, you can rewrite the inner_html of the renderer held by the widget.

polls/forms.py


class VoteForm(forms.Form):
    def __init__(self, question, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['choice'].queryset = question.choice_set.all()
        self.fields['choice'].widget.renderer.inner_html = '{choice_value}{sub_widgets}<br>'

Kobito.j0CZTQ.png

Description of receiving process

Source → 56f2b498

Tutorial 4 provided a function for receiving votes called vote, but it does not need to be separated. Let's describe the branch processing when POST is done, as when writing with form_test.

polls/views.py


def detail(request, pk):
    obj = get_object_or_404(Question, pk=pk)
    if request.method == "POST":
        form = VoteForm(question=obj, data=request.POST)
        if form.is_valid():
            # TODO:Voting process
            return redirect('polls:results', pk)
    else:
        form = VoteForm(question=obj)
    return render(request, 'polls/detail.html', {
        'form': form,
        'question': obj,
    })

Don't forget to fix the html side so that it jumps to detail instead of vote.

polls/templates/polls/detail.html


<h1>{{ question.question_text }}</h1>

<form action="" method="post">
  {% csrf_token %}
  {{ form }}
  <input type="submit" value="Vote" />
</form>

Erase in action. By the way, ʻerror_message` is also deleted because the Form class automatically issues it.

Description of post processing

Source → 38eb2ec47

It's finally the last work. Let's move the voting process (the process of adding +1 to the votes of the selected choice and saving) that was done in views.vote to the Form class.

What you want to do

polls/views.py


def vote(request, pk):
    question = get_object_or_404(Question, pk=pk)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        return redirect('polls:results', pk)

So, write the process to increment the votes of the Choice instance selected in the Form class. When the Form class executes ʻis_valid (), the data obtained by converting the input value into an appropriate format will be stored in cleaned_data. It's a bit confusing, but select it from html with a radio button and select it. The data passed to the server is a pk character representation of the Choice instance, such as " 2 "or" 3 ". When you execute ʻis_valid (), it will be converted to a ** Choice instance and put in ** in cleaned_data.

cleaned_data is in dictionary format with each field name as the key, so to take a choice instance Write as self.cleaned_data ['choice']. Based on the above, if you write a vote method that increments the votes of the selected Choice instance, it will be as follows.

polls/forms.py


class VoteForm(forms.Form):
...
    def vote(self):
        assert(self.is_valid())
        choice = self.cleaned_data['choice']
        choice.votes += 1
        choice.save()

assert is written to clarify that is_valid () has been executed and the input value must be normal.

Once the vote method is created, call it from the view side.

polls/views.py


def detail(request, pk):
    obj = get_object_or_404(Question, pk=pk)
    if request.method == "POST":
        form = VoteForm(question=obj, data=request.POST)
        if form.is_valid():
            form.vote()  #←←←←←←←←←←←← Add this
            return redirect('polls:results', pk)
    else:
        form = VoteForm(question=obj)
    return render(request, 'polls/detail.html', {
        'form': form,
        'question': obj,
    })

This completes the process. Since views.vote is no longer needed, let's delete it. Don't forget to delete it from urls.py as well.

field and widget

The field corresponds to the data held internally. For example, in the case of CharField, the internal data (cleaned_data) is text type, If it is ʻIntegerField, it will be a number type, and if it isModelChoiceField` like this time, it will be an instance of the selected model.

On the other hand, widget is a GUI that specifies what kind of parts should be displayed in the browser. In the case of ModelChoiceField, it is a select list by default, but you can change it to a radio button like this time. It is also possible to make a text box (although it is the worst input) and input Choice pk directly. There is also a widget that allows you to enter the date and time separately, such as SplitDateTimeWidget.

It can be confusing at first, but let's decide which one to change by considering whether you want to change the internal data or the display on the browser.

Cooperation with class-based general-purpose View

Rewrite test_form function

Source → 7d19e395

FormView is provided as a general-purpose view in django. First, let's rewrite form_test using this class.

By the way, the form_test function to be rewritten is divided into several processes although it is short.

polls.py


def form_test(request):
    if request.method == "POST":  #Processing when received by POST method
        form = MyForm(data=request.POST)  #Form creation process (+Pass the received data to Form)
        if form.is_valid():
            pass #Processing when the input value of form is correct
    else:  #Processing when received by GET method
        form = MyForm()  #Form creation process (no data)
    return render(request, 'polls/form.html', {  #Template rendering
        'form': form,
    })

It was messed up with comments, but first of all, it can be roughly divided into whether it was called by GET or POST. In the case of GET, it is the process when the page is displayed first, so there was no argument when creating the Form. On the other hand, in the case of POST, there is a process to receive the input data after displaying the page once. Data = request.POST is passed as an argument of Form. Furthermore, the validity of this data is judged (form.is_valid ()), and if it is correct, some processing will be performed. In the FormView class provided by django, each of these processes is a method. You can change the processing of the relevant part by overriding a specific method.

The following is a list of methods to be overridden when changing the operation as an example.

(In addition to this, there are methods that the base View has (methods that call get at the time of GET, etc.)).

Methods that can be overridden in FormView


    def get(self, request, *args, **kwargs):  #Called at access by GET method
    def post(self, request, *args, **kwargs):  #Called on access with the POST method
    def put(self, *args, **kwargs):  #Called on access with PUT method
    def get_initial(self):  #Set the initial value of Form
    def get_prefix(self):  #Set Form prefix
    def get_form_class(self):  #Get the Form class to use
    def get_form_kwargs(self):  #Get the arguments to pass to the Form class
    def get_form(self, form_class=None):  #Get an instance of the Form class
    def get_success_url(self):  #URL to redirect on successful completion
    def form_valid(self, form):  #Normal processing
    def form_invalid(self, form):  #What to do if the data is incorrect
    def get_context_data(self, **kwargs):  #Get the context to pass to the template
    def render_to_response(self, context, **response_kwargs):  #Create a response
    def get_template_names(self):  #Get the template name used for rendering

There are methods called get_form_class and get_template_names to specify the Form class and template to be used. Instead of overriding this method, just specifying field values such as form_class, template_name will work.

For a simple Form page, what you specify form_class: Form class to use template_name: Template used for rendering success_url: URL to jump to when successful There are only three.

In addition to this, you will want to do something on success, so you will override form_valid.

success_url is a character string specification. If you want to use the name set in url, use resolver. Since you cannot set the field with resolve_url when defining the class, use reverse_lazy or You will override get_success_url.

The rewritten form_testh is as follows.

polls/views.py


from django.views.generic import FormView
from django.core.urlresolvers import reverse_lazy

from .forms import MyForm
...
class FormTest(FormView):
    form_class = MyForm
    template_name = 'polls/form.html'
    success_url = reverse_lazy('polls:index')

form_test = FormTest.as_view()

Delete the def form_test (request): that you created first because it is unnecessary. Names collide and an error occurs.

When using class-based View, like the above source Execute view name = class name.as_view () after class definition.

The form_test function I wrote at the beginning contained if statements for method judgment and data judgment. If you use FormView, the judgment will be pushed into the Super class, so I was able to create a view with no if statement (= logic).

detail function rewriting

Source → 1efe74c527

Finally, let's rewrite the detail function to a class-based View. First of all, it's complicated enough that it's not suitable for tutorials. There are two reasons

  1. Need a Question instance in the template
  2. Requires a Question instance as an argument to the Form class

If it is only 1, the story is easy, and there is a DetailView to display the detail page of an instance. The display is completed just by specifying the Model and template to be used in this class, just like FormView. Of course, you can change the name of the argument received by \ # view (now pk) and the name of the object passed to the template. \ # Also, (* app_name * / * model_name * _detail.html) is used by default even if you do not specify a template.

Also, in No. 2, it is easy to increase the argument of Form class (just override get_form_kwargs), but The fact that you need a Question instance complicates the story.

Since SingleObjectMixin is prepared as a mixin of View class to get an instance of a certain model, This time, we will use this Mixin and FormView in combination.

def detail(request, pk): obj = get_object_or_404(Question, pk=pk) if request.method == "POST": form = VoteForm(question=obj, data=request.POST) if form.is_valid(): form.vote() return redirect('polls:results', pk) else: form = VoteForm(question=obj) return render(request, 'polls/detail.html', { 'form': form, 'question': obj, })

 Detail before rewriting

 ↓ Rewriting using class


#### **`polls/views.py`**
```python

from django.shortcuts import resolve_url
from django.views.generic import FormView
from django.views.generic.detail import SingleObjectMixin

from .forms import VoteForm
from .models import Question

...
class Detail(SingleObjectMixin, FormView):
    model = Question
    form_class = VoteForm
    context_object_name = 'question'
    template_name = 'polls/detail.html'

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super().get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super().post(request, *args, **kwargs)

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['question'] = self.object
        return kwargs

    def form_valid(self, form):
        form.vote()
        return super().form_valid(form)

    def get_success_url(self):
        return resolve_url('polls:results', self.kwargs['pk'])

detail = Detail.as_view()

First of all, both GET and POST require an instance of Question. self.object = self.get_object() I am trying to call. The model class to be acquired by this method is specified by model = Question. Since we are using a Question instance with the name question in the template, The name is specified by context_object_name ='question'.

Because we need this instance as an argument to VoteForm Overrides get_form_kwargs and adds the question argument.

I want to vote when the data is correct, so override form_valid and I am trying to call form.vote.

Finally, we are overriding get_success_url to rewrite the destination URL. Since the argument received by view is in self.kwargs, the pk received from the URL withself.kwargs ['pk']is passed to the result URL.

"It's not much simpler than the first detail function, it feels more complicated, but is it okay to make it a class? " Those who think that they have forgotten the important thing. Classes are ** inherited and reused **. If you create another View class with this class as a superclass, just as you created the Detail class by inheriting FormView When writing a View that performs the same processing, you can easily create a View that performs the same operation simply by rewriting four fields such as model. If you are writing a view as a function, please take this opportunity to consider switching to a class-based View.


Next, we will use bootstrap to clean the design.

To the next tutorial

Other tutorials

Recommended Posts

Python Django Tutorial (5)
Python Django Tutorial (2)
Python Django Tutorial (8)
Python Django Tutorial (6)
Python Django Tutorial (7)
Python Django Tutorial (1)
Python Django tutorial tutorial
Python Django Tutorial (3)
Python Django Tutorial (4)
Python Django tutorial summary
Python tutorial
Python Django Tutorial Cheat Sheet
Python tutorial summary
django tutorial memo
Start Django Tutorial 1
[Docker] Tutorial (Python + php)
Django python web framework
Django Polymorphic Associations Tutorial
django oscar simple tutorial
Django Python shift table
Try Debian + Python 3.4 + django1.7 ...
[Personal notes] Python, Django
Python OpenCV tutorial memo
[Python tutorial] Data structure
Django Girls Tutorial Note
Cloud Run tutorial (python)
Python Django CSS reflected
Do Django with CodeStar (Python3.6.8, Django2.2.9)
Get started with Django! ~ Tutorial ⑤ ~
Introduction to Python Django (2) Win
[Python tutorial] Control structure tool
Python
Do Django with CodeStar (Python3.8, Django2.1.15)
Python3 + Django ~ Mac ~ with Apache
Create ToDo List [Python Django]
Getting Started with Python Django (1)
Get started with Django! ~ Tutorial ④ ~
Django
Getting Started with Python Django (4)
Getting Started with Python Django (3)
Get started with Django! ~ Tutorial ⑥ ~
Install Python 3.7 and Django 3.0 (CentOS)
[Python] Decision Tree Personal Tutorial
GAE + python + Django addictive story
Getting Started with Python Django (6)
Getting Started with Python Django (5)
Until Python [Django] de Web service is released [Tutorial, Part 1]
EEG analysis in Python: Python MNE tutorial
8 Frequently Used Commands in Python Django
Python practice_Virtual environment setup ~ Django installation
Create new application use python, django
python + django + scikit-learn + mecab (1) on heroku
python + django + scikit-learn + mecab (2) on heroku
Run python3 Django1.9 with mod_wsgi (deploy)
Django Girls Tutorial Summary First Half
Stumble when doing the django 1.7 tutorial
Deploy the Django tutorial to IIS ①
Install Python framework django using pip
Introduction to Python Django (2) Mac Edition
[Python Tutorial] An Easy Introduction to Python
Learning history for participating in team app development in Python ~ Django Tutorial 5 ~