Обложка: Создаем своё первое веб-приложение с помощью Django

Создаем своё первое веб-приложение с помощью Django

3
18

Django — это Open Source фреймворк для создания веб-приложений различной сложности. Одним из основных преимуществ Django является то, что вам нужно позаботиться только о логике вашего будущего приложения, остальное сделает Django.

Мы создадим веб-приложение, у которого будет панель администратора и возможность загружать загадки, а у пользователей, соответственно, возможность отвечать на них. Во время разработки будут использоваться Python 3.4.3 и Django 1.9.1.

Устанавливаем Django

Делается это очень просто, в командной строке нужно написать: pip install Django==1.9.1.

Создаем проект

Если вы правильно установили Django, то после запуска django-admin --version вы увидите текущую версию фреймворка. Теперь создадим проект. Это можно сделать следующим образом: django-admin startproject django_example.

Как только создание проекта будет завершено, взглянем на директорию нашего проекта:

  • django_example/__init__.py — пустой файл, который говорит Python, что данная директория должна восприниматься в качестве пакета.
  • django_example/settings.py содержит конфигурацию нашего проекта.
  • django_example/urls.py — здесь объявляются URL.
  • django_example/wsgi.py — с помощью него приложение может работать с веб-сервером по протоколу WSGI.
  • manage.py позволяет взаимодействовать с проектом.

Теперь пришло время запустить наше приложение. Для этого в командной строке нужно написать python manage.py runserver. После этого в адресной строке браузера нужно написать: http://127.0.0.1:8000/. Если вы увидели «You have unapplied migrations; your app may not work properly until they are applied.», то не волнуйтесь, мы вернемся к этому чуть позже.

Создаем приложение

Определим различие между проектом и приложением. Приложение — это программа, которая что-то делает, а проект — это группа приложений.

Итак, приступим к созданию приложения. Это делается следующим образом: python manage.py startapp riddles.
Как только приложение создано, давайте напишем простой вид, по правилам Django все виды должны храниться в файле views.py.

riddles/views.py

from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, World!")

Теперь, чтобы привязать наш вид к URL, создадим файл urls.py.

riddles/urls.py

from django.conf.urls import url

from . import views

app_name = 'riddles'

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

В urls.py мы должны написать следующее:

django_example/urls.py

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

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

Теперь, если мы запустим наше приложение http://127.0.0.1:8000/riddles/, мы увидим «Hello, World!».

Установка базы данных

По умолчанию в Django используется SQLite, если она вас не устраивает, то вы можете ознакомиться с нашей статьей, в которой мы рассказываем, как безболезненно перейти с SQLite на MySQL.

Теперь откроем django_example/settings.py и взглянем на переменную INSTALLED_APPS, она хранит все приложения, которые активны в текущем проекте. По умолчанию она содержит:

  • django.contrib.admin — админка, скоро мы ей воспользуемся.
  • django.contrib.auth — система аутентификации.
  • django.contrib.contenttypes — фреймворк для content types.
  • django.contrib.sessions — сессионный фреймворк.
  • django.contrib.messages — фреймворк для отправки сообщений.
  • django.contrib.staticfiles — фреймворк для работы со статичными файлами.

Некоторые из этих приложений используют базы данных, но они еще не установлены, поэтому мы и видели «You have unapplied migrations; your app may not work properly until they are applied.». Поправить это можно следующим образом: python manage.py migrate. Вы должны увидеть следующее:

Operations to perform:
  Apply all migrations: admin, sessions, auth, contenttypes
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying sessions.0001_initial... OK

Теперь создадим нашу модель. Для начала создадим Riddle и Option. В Riddle будет содержаться загадка, в Option — один из возможных ответов на нее.

riddles/models.py

from django.db import models


class Riddle(models.Model):
    riddle_text = models.CharField(max_length=255)
    pub_date = models.DateTimeField('date published')


class Option(models.Model):
    riddle = models.ForeignKey(Riddle, on_delete=models.CASCADE)
    text = models.CharField(max_length=255)
    correct = models.BooleanField(default=False)

Данная модель обеспечивает Django информацией, необходимой для создания схемы базы данных и database-access API для доступа к объектам. Теперь нам нужно привязать наше приложение к нашему проекту, делается это следующим образом:

django_example/settings.py

INSTALLED_APPS = [
    'riddles.apps.RiddlesConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

После этого нужно сделать миграцию: python manage.py makemigrations riddles. Вы должны увидеть следующее:

Migrations for 'riddles':
  0001_initial.py:
    - Create model Option
    - Create model Riddle
    - Add field riddle to option

Так мы говорим Django, что в моделях были сделаны некоторые изменения, и их нужно сохранить в качестве миграции.

Проверить, что сделает миграция, можно так: python manage.py sqlmigrate riddles 0001 (0001 — версия миграции, которую мы хотим проверить). На выходе мы получим:

BEGIN;
--
-- Create model Option
--
CREATE TABLE "riddles_option" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "text" varchar(255) NOT NULL, "correct" bool NOT NULL);
--
-- Create model Riddle
--
CREATE TABLE "riddles_riddle" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "riddle_text" varchar(255) NOT NULL, "pub_date" datetime NOT NULL);
--
-- Add field riddle to option
--
ALTER TABLE "riddles_option" RENAME TO "riddles_option__old";
CREATE TABLE "riddles_option" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "text" varchar(255) NOT NULL, "correct" bool NOT NULL, "riddle_id" integer NOT NULL REFERENCES "riddles_riddle" ("id"));
INSERT INTO "riddles_option" ("riddle_id", "id", "text", "correct") SELECT NULL, "id", "text", "correct" FROM "riddles_option__old";
DROP TABLE "riddles_option__old";
CREATE INDEX "riddles_option_a7c97949" ON "riddles_option" ("riddle_id");

COMMIT;

Заметьте, что команда sqlmigrate нужна только для проверки, каждый раз ее запускать необязательно.

Теперь мы можем начать пользоваться панелью администратора. Но для этого нам нужен пользователь. Создать его можно следующим образом: python manage.py createsuperuser. После этого запускаем сервер, если он не запущен, и переходим на http://127.0.0.1:8000/admin/. Вы увидите следующее:
Форма логина

Теперь дадим админу возможность изменять наши модели. Делается это так:

riddles/admin.py

from django.contrib import admin

from .models import Option, Riddle

admin.site.register(Riddle)
admin.site.register(Option)

Вот что получится в итоге:

Панель администратора

Возьмите небольшую паузу и поиграйтесь с панелью администратора. Вы будете приятно удивлены тем, что умеет Django.

Главная страница

Что нам нужно для создания главной страницы?

  • Templates: скелет нашей страницы.
  • Views: функция на Python для отображения контента.

Начнем с шаблонов. Создадим папку templates внутри папки riddle, а в ней создадим index.html.

riddles/templates/index.html

<h1>Available Riddles</h1>

{% if message %}
    <p><strong>{{ message }}</strong></p>
{% endif %}

{% if latest_riddles %}

    <ul>
        {% for riddle in latest_riddles %}
            <li>
                <a href="/riddles/{{ riddle.id }}/">
                    {{ riddle.riddle_text }}
                </a>
            </li>
        {% endfor %}
    </ul>

{% else %}
    <p>No riddles are available right now.</p>
{% endif %}

Теперь создадим макет для ответов:

riddles/templates/answer.html

<h1>{{ riddle.riddle_text }}</h1>

{% if error_message %}
    <p>
        <strong>{{ error_message }}</strong>
    </p>
{% endif %}

<form action="answer' riddle.id %}" method="post">
    {% csrf_token %}
    {% for option in riddle.option_set.all %}
        <input type="radio" name="option" id="option{{ forloop.counter }}" value="{{ option.id }}" />
        <label for="option{{ forloop.counter }}">{{ option.text }}</label><br>
    {% endfor %}
    <input type="submit" value="Answer" />
</form>

Здесь мы используем csrf_token, он нужен для защиты от межсайтовой подделки запроса, каждая внутренняя форма должна его использовать. Теперь напишем виды для рендеринга наших шаблонов:

riddles/views.py

from django.http.response import HttpResponse
from django.shortcuts import get_object_or_404, render

from .models import Riddle, Option


def index(request):
    return render(request, "index.html", {"latest_riddles": Riddle.objects.order_by('-pub_date')[:5]})


def detail(request, riddle_id):
    return render(request, "answer.html", {"riddle": get_object_or_404(Riddle, pk=riddle_id)})


def answer(request, riddle_id):
    riddle = get_object_or_404(Riddle, pk=riddle_id)
    try:
        option = riddle.option_set.get(pk=request.POST['option'])
    except (KeyError, Option.DoesNotExist):
        return render(request, 'answer.html', {'riddle': riddle, 'error_message': 'Option does not exist'})
    else:
        if option.correct:
            return render(request, "index.html", {"latest_riddles": Riddle.objects.order_by('-pub_date')[:5], "message": "Nice! Choose another one!"})
        else:
            return render(request, 'answer.html', {'riddle': riddle, 'error_message': 'Wrong Answer!'})

  Давайте пройдемся по каждой функции отдельно:

  • index: Index использует функцию render. На вход она получает HttpRequest, местонахождение шаблона и его содержимое, а возвращает HttpResponse с окончательным html.
  • detail: Detail делает практически то же самое, но только функция get_object_or_404 возвращает HttpResponse404, если нужный объект не был найден.
  • answer: Answer ищет предоставленную загадку (и возвращает 404, если она не найдена) и проверяет правильность ответа.

Теперь добавим наши функции в urls.py:

riddles/urls.py

from django.conf.urls import url

from . import views

app_name = 'riddles'

urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^(?P[0-9]+)/$', views.detail, name='detail'),
    url(r'^(?P[0-9]+)/answer/$', views.answer, name='answer')
]

Добавим немного стилей

Для начала создадим директорию static, а в ней создадим файл main.css.

riddles/static/main.css

body{
    margin:40px auto;
    max-width:650px;
    line-height:1.6;
    font-size:18px;
    color:#444;
    padding:0 10px;
}
h1,h2,h3{
    line-height:1.2;
    text-align: center;
}
a {
    color: blue;
}

form {
    margin: 0 auto;
    padding: 1em;
    border: 1px solid #CCC;
    border-radius: 1em;
}
form div + div {
    margin-top: 1em;
}
label {
    display: inline-block;
    text-align: center;
    width: 40%;
}
input {
    font: 1em sans-serif;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    border: 1px solid #999;
    width: 50%;
}
input:focus {
    border-color: #000;
}
p, div.button {
    text-align: center;
}

p.error-message {
    color: lightcoral;
}

Немного изменим наши шаблоны:

riddles/templates/index.html

{% load staticfiles %}

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

<h1>Available Riddles</h1>

{% if message %}
    <p><strong>{{ message }}</strong></p>
{% endif %}

{% if latest_riddles %}

    <ul>
        {% for riddle in latest_riddles %}
            <li>
                <a href="/riddles/{{ riddle.id }}/">
                    {{ riddle.riddle_text }}
                </a>
            </li>
        {% endfor %}
    </ul>

{% else %}
    <p>No riddles are available right now.</p>
{% endif %}

riddles/templates/answer.html

{% load staticfiles %}

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

<h1>{{ riddle.riddle_text }}</h1>

{% if error_message %}
    <p>
        <strong>{{ error_message }}</strong>
    </p>
{% endif %}

<form action="answer' riddle.id %}" method="post">
    {% csrf_token %}
    {% for option in riddle.option_set.all %}
        <input type="radio" name="option" id="option{{ forloop.counter }}" value="{{ option.id }}" />
        <label for="option{{ forloop.counter }}">{{ option.text }}</label><br>
    {% endfor %}
    <input type="submit" value="Answer" />
</form>

Первая строка загружает статические файлы, потом мы используем {% static '#' %}, где # — путь к вашему файлу. Аналогичная процедура проводится и для JavaScript.

Теперь вы можете создавать свои собственные приложения на Django. В качестве подсказки на старте работы с фреймворком воспользуйтесь одной из наших шпаргалок по Python.

Исходный код нашего приложения можно скачать по этой ссылке.

Если этот проект показался сложным, попробуйте пройти двухчасовой видеокурс. На нём вы пошагово создадите 3 веб-приложения: сокращатель ссылок, ToDo List и словарь английских слов.

Перевод статьи «Python Django Tutorial»

Что думаете?