Django Girls

  Edit This Page

Расширяем свое приложение

Мы уже выполнили часть необходимых шагов для создания веб-сайта: мы знаем как создать модель, URL, представление и шаблон. Мы также знаем как улучшить визуальный дизайн с помощью CSS.

Время практики!

Первое, что нам потребуется в блоге - страница для отображения конкретной записи, верно?

У нас уже есть модель Post, так что нам не нужно добавлять дополнительный код в файл models.py.

Создадим в шаблоне ссылку на страницу поста

Мы начнем с добавления ссылки внутри файла blog/templates/blog/post_list.html. Пока он выглядит следующим образом:

    {% extends "blog/base.html" %}

    {% block content %}
        {% for post in posts %}
            <div class="post">
                <div class="date">
                    {{ post.published_date }}
                </div>
                <h1><a href="">{{ post.title }}</a></h1>
                <p>{{ post.text|linebreaks }}</p>
            </div>
        {% endfor %}
    {% endblock content %}

Нам хотелось бы иметь ссылку с заголовка поста в списке на страницу подробной информации о посте. Давай изменим <h1><a href="">{{ post.title }}</a></h1> чтобы получилась ссылка на пост:

    <h1><a href="{% url "post_detail" pk=post.pk %}">{{ post.title }}</a></h1>

Самое время разобраться с загадочным {% url "post_detail" pk=post.pk %}. Как можешь предположить, синтаксис {% %} означает использование тегов шаблонов Django. На этот раз мы используем тот, что создаст для нас URL!

blog.views.post_detail это путь к представлению post_detail, которое нам нужно создать. Пожалуйста, обрати внимание: blog - это имя нашего приложения (директория blog), views - имя файла views.py без расширения и последнее - post_detail - имя представления.

Теперь, когда мы перейдем по адресу http://127.0.0.1:8000/ мы получим ошибку (как и ожидается, поскольку у нас нет прописанного URL и представления для post_detail). Она будет выглядеть следующим образом:

Ошибка NoReverseMatch

Создадим URL для страницы поста

Давай создадим URL в urls.py для представления post_detail!

Мы хотим, чтобы адрес страницы нашего первого поста был таким: URL: http://127.0.0.1:8000/post/1/

Давай создадим URL в файле blog/urls.py и укажем Django на представление под названием post_detail, которое будет отображать пост целиком. Добавь строчку url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail'), в файл blog/urls.py. Файл должен выглядеть примерно так:

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

    urlpatterns = [
        url(r'^$', views.post_list, name='post_list'),
        url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail'),
    ]

Фрагмент ^post/(?P<pk>[0-9]+)/$ выглядит страшновато, но не волнуйтесь — мы его сейчас объясним: - он начинается с ^, что означает, как мы помним, "начало строки" - post/ значит всего лишь, что после начала строки URL должен содержать слово post и косую черту /. Пока все в порядке. - (?P<pk>[0-9]+) - эта часть посложнее. Она означает, что Django возьмет все, что придется на эту часть строки и передаст представлению в качестве переменной pk. [0-9] означает, что допустимы только цифры (от 0 до 9), но не буквы. + означает, что цифр должно быть от одной и больше. Таким образом адрес http://127.0.0.1:8000/post// будет недействительным, а http://127.0.0.1:8000/post/1234567890/ совершенно правильным! - / - затем нам нужен еще один символ / - $ - "конец"!

Если ты введешь адрес http://127.0.0.1:8000/post/5/ в браузер, Django должен понять, что тебе требуется представление под именем post_detail, и передать информацию о переменной pk (равной 5) этому представлению.

pk сокращение от primary key (первичный ключ). Это имя часто используют в Django проектах. Но ты можешь назвать эту переменную как пожелаешь (помни: строчные буквы и _ вместо пробелов!). Для примера, вместо (?P<pk>[0-9]+) мы могли бы иметь переменную post_id, таким образом эта часть кода выглядела бы как: (?P<post_id>[0-9]+).

Славненько, мы добавили новый шаблон URL в файл blog/urls.py! Давай обновим страницу: http://127.0.0.1:8000/ Бууум! Ещё одна ошибка! Как и ожидалось!

AttributeError

Помнишь, каким должен быть следующий шаг? Конечно: добавить представление!

Добавим представление для страницы поста

В этот раз представление получит дополнительный параметр pk. Но как дать нашему представлению знать о нем? Для этого мы определим функцию как def post_detail(request, pk):. Обрати внимание, что мы должны использовать тоже имя переменной, что мы выбрали для обработки URL (pk). Пропуск переменной будет неправилен и приведет к ошибке!

Теперь мы хотим получить одну конкретную запись из блога. Для этого потребуется использовать QuerySet:

    Post.objects.get(pk=pk)

Однако в этом коде есть проблема. Если не существует экземпляра объекта Post с заданным primary key (pk) мы получим страшную ошибку!

Ошибка DoesNotExist

Мы этого не хотим! Однако, Django, конечно, имеет средство, которое позволит нам её обойти: get_object_or_404. В случае, если не существует экземпляра объекта Post с заданным pk, мы получим намного более приятную страницу (которая называется Page Not Found 404).

Страница не найдена

Хорошая новость в том, что ты можешь сделать свою страницу Page not found. Но для нас сейчас это не самая важная задача и мы её пропустим.

Хорошо, пришло время добавить представление в файл views.py!

Нам нужно открыть файл blog/views.py и добавить в него следующий код:

    from django.shortcuts import render, get_object_or_404

Рядом с другими строками, начинающимися с from. В конец же файла мы добавим наше новое представление:

    def post_detail(request, pk):
        post = get_object_or_404(Post, pk=pk)
        return render(request, 'blog/post_detail.html', {'post': post})

Именно. Теперь обнови страницу http://127.0.0.1:8000/

Представление списка записей

Заработало! Только что произойдет, если ты попробуешь перейти по ссылке из заголовка записи?

Ошибка TemplateDoesNotExist

Ой, нет! Другая ошибка! Но мы уже знаем как иметь с ней дело, верно? Нам нужно добавить шаблон!

Создадим шаблон для страницы поста

Мы создадим файл post_detail.html в директории blog/templates/blog.

Он должен содержать следующее:

    {% extends "blog/base.html" %}

    {% block content %}
        <div class="post">
            {% if post.published_date %}
                <div class="date">
                    {{ post.published_date }}
                </div>
            {% endif %}
            <h1>{{ post.title }}</h1>
            <p>{{ post.text|linebreaks }}</p>
        </div>
    {% endblock %}

И снова мы расширяем base.html. В блоке content мы отображаем дату публикации (published_date, если она существует), заголовок и текст. Нам также нужно обсудить пару важных вещей, хорошо?

{% if ... %} ... {% endif %} это тег шаблона, который мы можем использовать, если нам нужно что-то проверить (помнишь конструкцию if ... else .. из главы Введение в Python?). В данном случае мы хотим проверить, не пуста ли дата публикации published_date поста.

Отлично, можешь перезагрузить страницу и проверить пропала ли ошибка Page not found.

Отдельная страницы записи

Ура! Все работает!

Еще одна вещь: развертывание!

Было бы неплохо проверить, что веб-сайт все еще будет работать на PythonAnywhere, верно? Давай еще раз проведем развертывание.

    $ git status
    $ git add -A .
    $ git status
    $ git commit -m "Added view and template for detailed blog post as well as CSS for the site."
    $ git push
    $ cd my-first-blog
    $ source myvenv/bin/activate
    (myvenv)$ git pull
    [...]
    (myvenv)$ python manage.py collectstatic
    [...]
  • И нажми Reload на вкладке Web tab.

Вот и все! Поздравляем :)