Начинаем работу с Django — добавляем страницы

Чтобы добавить на сайт страницы, нужно понять логику работы Django.

Django использует простой и логичный подход. При работе с вашим сайтом пользователь вводит определенный адрес. Когда веб-сервер получает запрос, он передает этот адрес Django, после чего он проверяется на соответствие адресам, зарегистрированными в Django через urls.py через urlpatterns. 

При этом нет нужны регистрировать отдельно каждый возможный адрес — адреса добавляются через регулярные выражения, определяющие скорее формат возможного адреса. Например: /archive/<year>/<month>/.

Каждому url сопоставляется view — функция, которой Django передаст обращение при запросе данного адреса.

Для работы нашего сайта  мы создадим 4 view

  • «index» — главная страница – покажет несколько последних вопросов
  • “detail” – покажет конкретный вопрос и позволит выбрать вариант ответа
  • “results” – отобразит результаты определенного вопроса
  • отдельный view для процесса голосования

Содержание

Создание первого View

Откройте polls/views.py и добавьте туда следующий код:

from django.http import HttpResponse
def index(request):
    return HttpResponse("Hello, world. You're at the poll index.")

Это простейшая функция View, возможная в Django. Чтобы обратиться к ней, мы должны сопоставить ей URL — и для этого нам нужен URLconf.

Чтобы определить URLconf в приложении polls, создайте в нем файл urls.py. Каталог вашего приложения должен выглядеть так:

polls/
    __init__.py
    admin.py
    models.py
    tests.py
    urls.py
    views.py

В файле polls/urls.py добавьте:

from django.conf.urls import patterns, url

from polls import views

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

Осталось добавить созданный файл в корневой URLconf, определенный в файле mysite/urls.py. Вставьте include(), чтобы получилось следующее::

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

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

Теперь откройте в браузере http://localhost:8000/polls/ и вы увидите текст “Hello, world. You’re at the poll index.”, который мы прописали в index view.

Фукция url() получает 4 аргумента, из которых два — regex и view, — необходимы, и два можно не указывать: kwargs, и name.

url() argument: regex

“Regex” — это сокращение от “regular expression”, регулярные выражения. Регулярные выражения определяют синтаксис, позволяющий  задать шаблоны, с которыми будет сверятся строки. В данном случае мы определяем шаблон, на соответствие с которым будет сверяться адрес. Django последовательно проверит запрошенный адрес на соответствие всем URL patterns, начиная с первого шаблона, и передаст управление соответствующей функции View при совпадении.

Данные регулярные выражения не определяют параметры GET или POST, а также название домена, на котором расположен сайт. Для примера, при запросе http://www.example.com/myapp/, URLconf будет искать myapp/. При запросе http://www.example.com/myapp/?page=3, URLconf также будет искать myapp/.

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

url() argument: view

When Django finds a regular expression match, Django calls the specified view function, with an HttpRequest object as the first argument and any “captured” values from the regular expression as other arguments. If the regex uses simple captures, values are passed as positional arguments; if it uses named captures, values are passed as keyword arguments. We’ll give an example of this in a bit.

url() argument: kwargs

Arbitrary keyword arguments can be passed in a dictionary to the target view. We aren’t going to use this feature of Django in the tutorial.

url() argument: name

Naming your URL lets you refer to it unambiguously from elsewhere in Django especially templates. This powerful feature allows you to make global changes to the url patterns of your project while only touching a single file.

Добавление views

Давайте добавим нужные нам views в polls/views.py. Поскольку в данном случае нам нужно передать параметр — номер вопроса — они будут немного отличаться:

def detail(request, poll_id):
    return HttpResponse("You're looking at poll %s." % poll_id)

def results(request, poll_id):
    return HttpResponse("You're looking at the results of poll %s." % poll_id)

def vote(request, poll_id):
    return HttpResponse("You're voting on poll %s." % poll_id)

Соответственно для их работы добавим новые шаблоны URL в polls.urls:

from django.conf.urls import patterns, url

from polls import views

urlpatterns = patterns('',
    # ex: /polls/
    url(r'^$', views.index, name='index'),
    # ex: /polls/5/
    url(r'^(?P<poll_id>\\d+)/$', views.detail, name='detail'),
    # ex: /polls/5/results/
    url(r'^(?P<poll_id>\\d+)/results/$', views.results, name='results'),
    # ex: /polls/5/vote/
    url(r'^(?P<poll_id>\\d+)/vote/$', views.vote, name='vote'),
)

Теперь, если мы запросим в браузере http://localhost:8000/polls/23/, Django вызовет метод detail() и отобразит ID, который мы указали в URL.

Когда Django получает запрос к сайту, сначала загружается mysite.urls модуль, поскольку он указан в параметре ROOT_URLCONF в settings.py. Он находит переменную urlpatterns и проходит по порядку перечисленные в ней регулярные выражения. Функция include() позволяет включить в список URLconfs, определенные в другом месте. Обратите внимание, что регулярное выражение для include() на заканчивается символом $, означающим конец строки. но завершается слешем (/).

Когда Django встречает include(), она отрезает соответствующий ей URL из строки адреса и оставшуюся часть передает включенному URLconf для обработки..

Основная идея использования include() в том, чтобы сделать легко подключаемые URL. Поскольку мы определили все обрабатываемые приложением polls адреса в собственном URLconf (polls/urls.py), они могут біть расположены под “/polls/”, либо под “/fun_polls/”, или другой базовой частью адреса. и приложение будет работать.

Добавляем логику в функции views

Каждая функция view отвечает за две вещи. Она возвращает объект типа HttpRespons, в котором хранится содержимое запрошенной страницы. Либо она должна вызвать исключение Http404 — если искомой страницы не существует.

В остальном мы вольны делать что угодно. View могут обращаться к объектам базы данных, генерировать PDF документ либо ZIP архив, исползовать любую Python библиотеку. Без ограничений.

Мы будем использовать API Django для работы с базой данных. Чтобы вернуть при вызове index() view 5 последних вопросов,, используйте следующий код:

from django.http import HttpResponse

from polls.models import Poll

def index(request):
    latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
    output = ', '.join([p.question for p in latest_poll_list])
    return HttpResponse(output)

Django также предлагает нам возможность использовать шаблоны страниц, чтобы не генерировать каждый раз HTML код.

Использование шаблонов HTML в Django

Создайте каталог templates внутри polls. Там Django будет искать шаблоны для данного приложения.

Параметр TEMPLATE_LOADERS определяет список вызываемых объектов, которые знают как импортировать шаблоны из разных источников.

Одним из таких включенных изначально методов является django.template.loaders.app_directories.Loader. Он ищет каталог “templates” внутри каталога приложений, перечисленных в INSTALLED_APPS. Другим вариантом дать Django знать, где искать ваши шаблоны — изменить параметр TEMPLATE_DIRS,

Внутри каталога templates создайте другой каталог с названием polls, и добавьте туда файл index.html. Данный шаблон должен находится по адресу polls/templates/polls/index.html,  что позволит обнаружить его загрузчику шаблонов app_directories. При этом внутри Django к этому шаблону можно обращаться просто polls/index.html.

Добавьте в шаблон следующий код:

{% if latest_poll_list %}
    <ul>
    {% for poll in latest_poll_list %}
        <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

Теперь давайте обновим index view в polls/views.py для использования шаблона:

from django.http import HttpResponse
from django.template import Context, loader

from polls.models import Poll

def index(request):
    latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = Context({
        'latest_poll_list': latest_poll_list,
    })
    return HttpResponse(template.render(context))

Этот код загружает шаблон с названием polls/index.html и передает ему context. Context — это словарь, содержащий переменные — объекты Python — которые будут доступны в шаблоне.

Загрузив страницу “/polls/” вы увидите ненумерованный список вопросов, при этом каждый элемент списка будет ссылкой на страницу detail для конкретного вопроса.

Сгенерированный список вопросов

Вызов render()

Стандартный подход заключается в загрузке шаблона, заполнении context и возврате объекта HttpResponse со сгенерированной на базе шаблона страницей. Для этого Django предлагает функцию render(). Перепишем наш index() view:

from django.shortcuts import render

from polls.models import Poll

def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    context = {'latest_poll_list': latest_poll_list}
    return render(request, 'polls/index.html', context)

Теперь нам не нужно использовать loaderContext и HttpResponse.

Функция render() берет объект request в качестве первого аргумента, имя шаблона в качестве второго и словарь, содержащий объекты для формирования страницы в качестве необязательного треьего. После генерации страницы render возвращает объект HttpResponse.

404 ошибка

Теперь давайте обработаем detail view. Эта страница будет отображать вопрос и варианты ответов на него с возможностью выбора. При этом если запрошенного вопроса нет в базе, нужно вернуть 404 ошибку.

from django.http import Http404
# ...
def detail(request, poll_id):
    try:
        poll = Poll.objects.get(pk=poll_id)
    except Poll.DoesNotExist:
        raise Http404
    return render(request, 'polls/detail.html', {'poll': poll})

Если вы сейчас попробуете открыть страницу для одного из существующих вопросов, то Django вернет ошибку TemplateDoesNotExist.

Создайте файл polls/detail.html со следующим содержимым:

{{ poll }}

Функция get_object_or_404()

При получении из базы объекта обычно мы используем get() и поднимаем Http404 если объекта не существует. Для этого Django предлагает функцию get_object_or_404()

Давайте перепишем detail() view:

from django.shortcuts import render, get_object_or_404
# ...
def detail(request, poll_id):
    poll = get_object_or_404(Poll, pk=poll_id)
    return render(request, 'polls/detail.html', {'poll': poll})

Вызов get_object_or_404() использует определенную в Django модель в качестве первого аргумента и произвольное число ключей, которые будут переданы в метод get() определенный в рамках модели. Если объекта не существует, будет поднята ошибка 404.

Также существует get_list_or_404() которая аналогична get_object_or_404() за исключением использования метода filter() вместо get(). Эта функция поднимает Http404 если получает пустой список.

Доработка шаблона

Давайте доработаем шаблон, отображающий детали конкретного вопроса, который вызывается в detail() view:

<h1>{{ poll.question }}</h1>
<ul>
{% for choice in poll.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

HTML шаблоны, используемые Django, могут использовать передаваемые переменные и их атрибуты. В нашем случае {{ poll.question }} получит значение, хранящееся в атрибуте question прочитанного из базы объекта poll.

Далее внутри цикла {% for %} происходит вызов метода: poll.choice_set.all интерпретируется как питоновский код poll.choice_set.all(), который возвращает перечисляемый объект, состоящий из объектов Choice.

Посмотрите руководство по HTML шаблонам — template guide — для большей информации.

Убираем жесткие URL в шаблонах

В шаблоне polls/index.html мы жестко прописали часть ссылки:

<li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>

По мере роста проекта подобные жестко заданные вещи могут стать причиной проблем, поэтому их нужно стремится избегать. Поскольку мы задали аргумент name в определении url() в polls.urls, можно заменить соответствующую часть адреса, используя тег {% url %}:

<li><a href="{% url 'detail' poll.id %}">{{ poll.question }}</a></li>

Теперь если вы захотите изменить URL с polls/ на что-то другое, это будет достаточно сделать в файле polls/urls.py.

Наименование URL

До сих пор мы использовали только одно приложение polls. В настоящем проекте может быть 5-10 и больше приложений, и каждое из них может использовать detail. Чтобы Django могла определить правильное имя, нужно использовать более специфичные имена или добавить namespaces в основной URLconf. В mysite/urls.py включим namespace:

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

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

Теперь нужно изменить polls/index.html:

<li><a href="{% url 'detail' poll.id %}">{{ poll.question }}</a></li>

замените строкой с указанием namespace

<li><a href="{% url 'polls:detail' poll.id %}">{{ poll.question }}</a></li>

Теперь на сайте созданы все основные страницы. Давайте подключим формы, чтобы дать возможность голосования!

Начинаем работу с Django — добавляем страницы: 6 комментариев

  1. Пасябки за статью единственное в юрлах маленькая ошибка, в режексах лишний слеш. А так, нормуль ))

  2. Застряла на создании первого views.
    C самого начала делала все исключительно по инструкции, но почему-то именно в этом шаге не могу устранить ошибку: TypeError: ‘function’ object has no attribute ‘__getitem__’

    ругается на файл polls\urls.py

    from polls import views

    urlpatterns = patterns[»,
    url(r’^articles/$’, views.index)
    ]

  3. polls.urls лишний слеш
    urlpatterns = [
    url(r’^$’, views.index, name=’index’),
    url(r’^(?P\d+)/$’, views.detail, name=’detail’),
    url(r’^(?P\d+)/results/$’, views.results, name=’results’),
    url(r’^(?P\d+)/vote/$’, views.vote, name=’vote’)
    ]

  4. В строках для Django 1.8+ нужно заменить на:

    Теперь давайте обновим index view в polls/views.py для использования шаблона:
    from django.shortcuts import render
    from django.template import loader
    from django.http import HttpResponse
    from polls.models import Poll
    def index(request):
    latest_poll_list = Poll.objects.order_by(‘-pub_date’)[:5]
    template = loader.get_template(‘polls/index.html’)
    context = {
    ‘latest_poll_list’: latest_poll_list,
    }
    return HttpResponse(template.render(context))
    Короче убрать Context, так как он уже не используется.

  5. Для версии 2 и выше после того как в mysite/urls.py включим namespace:
    Нужно в файле polls/urls.py прописать
    from django.conf.urls import url
    from . import views

    app_name = ‘polls’
    urlpatterns = [
    url(r’^$’, views.index, name=’index’),
    url(r’^(?P\d+)/$’, views.detail, name=’detail’),
    url(r’^(?P\d+)/results/$’, views.results, name=’results’),
    url(r’^(?P\d+)/vote/$’, views.vote, name=’vote’)
    ]

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *