---
created: 2021-08-26T18:29:33 (UTC +03:00)
source: https://ritza.co/showcase/repl.it/beginner-web-scraping-with-python-and-repl-it.html
author: Ivan Yastrebov
---
# Chapter 5: Blog app #
Как описано в предыдущих главах, наши шаги для создания нового проекта Django будут
выглядеть следующим образом:
• Создать новую папку для нашего проекта на рабочем столе с именем blog
• Установить Django в новой виртуальной среде
• Создать новый проект Django с именем blog\_project
• Создать новое приложение blog
• Выполнить миграцию для установки базы данных
• Обновить settings.py
Выполните следующие команды в новой консоли командной строки. Обратите внимание, что фактическое имя виртуальной среды будет (blog-XXX), где XXX представляет случайные символы. Я использую здесь (blog), чтобы было проще, так как мое имя будет
отличаться от вашего.
И не забудьте указать пробел с точкой `. ` в конце команды для создания нашего нового blog_project . (прм. переводчика. Создает проект Django в существующей директории без создания новой папки, не обязательно)
```bash=
mkdir -p $HOME/Projects/project_blog;cd $_
```
```bash=
python3 -m venv .venv
pipenv install django
pipenv shell
```
```bash=
cat > .envrc <<ZZZ
source /home/zzz/Projects/project_blog/.venv/bin/activate
ZZZ
```
```bash=
direnv allow
```
```bash=
django-admin startproject blog_project .
python manage.py startapp blog
python manage.py migrate
python manage.py runserver
```
Чтобы убедиться, что Django знает о нашем новом приложении, откройте текстовый редактор и добавьте новое приложение в INSTALLED_APPS в нашем settings.py файле:
```python=
# blog_project/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # new
]
```
Если вы перейдете по http://127.0.0.1:8000/ должны увидеть следующую страницу.
**Хорошо, первичная настройка закончена! Далее мы создадим модель базы данных для записей блога.**
## Database Models **(модели базы данных)**
Каковы характеристики типичного приложения для блога? В нашем случае давайте придерживаться простоты и предположим, что у каждого сообщения есть название, автор и тело. Мы можем превратить это в модель базы данных, откроем файл `blog/models.py` и введем показанный ниже код :
```python=
# blog/models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
body = models.TextField()
def __str__(self):
return self.title
```
В верхней части мы импортируем класс models а затем создаем подкласс models.Model с именем Post. Используя функционал подкласса, мы автоматически имеем доступ ко всему в `django.db.models.Models` и можем добавлять дополнительные поля и методы по своему желанию.
Для title мы ограничиваем длину до 200 символов, а для body мы используем текстовое поле, которое будет автоматически расширяться по мере необходимости, чтобы
соответствовать тексту пользователя. В Django доступно много типов полей; вы можете увидеть [полный список здесь. ](https://docs.djangoproject.com/en/2.0/ref/models/fields/)
Для поля author мы используем ForeignKey это предоставляет связь *многие*- *к*- *одному* .
Это означает, что данный пользователь может быть автором многих различных постов в блоге, но не наоборот. Ссылка на встроенную модель User которой Django обеспечивает аутентификацию. Для всех связей многие-к-одному таких как ForeignKey нам также необходимо указать параметр on_delete .
Теперь, когда наша новая модель базы данных создана, нам нужно создать для нее новую запись миграции и перенести изменения в нашу базу данных. Это двухэтапный процесс и может быть выполнен следующими командами:
```bash=
python manage.py makemigrations blog
python manage.py migrate blog
```
**Наша база настроена! Что дальше?**
## Admin
*Нам нужен способ доступа к нашим данным. Войдем в Django админ!*
Но сначала создадим учетную запись суперпользователя, введя приведенную ниже команду и после отвечая на
запросы введем адрес электронной почты и пароль. Обратите внимание, что при вводе пароля он не будет отображаться на экране по соображениям безопасности.
```bash=
python manage.py createsuperuser
```
Теперь снова запустите сервер Django с помощью команды python manage.py runserver и откройте Django админ http://127.0.0.1:8000/admin/. Войдите с новой учетной записью суперпользователя.
Упс! А где наша новая Post модель?
Мы забыли обновить blog/admin.py так что давайте сейчас сделаем это.
```python=
# blog/admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
```
Если вы обновите страницу, вы увидите это обновление.Давайте добавим два сообщения в блог, чтобы у нас были примеры данных для работы. Нажмите кнопку **\+ Add** рядом с Posts чтобы создать новую запись. Не забудьте добавить `“author”` к каждому сообщению, так как по умолчанию все поля модели обязательны длязаполнения. Если вы попытаетесь ввести сообщение без автора вы увидите ошибку. Если мы захотим изменить это, мы можем добавить `field options(параметры поля)` в нашу модель, чтобы сделать это поле необязательным или заполнить его значением по умолчанию
+ **Первый пост**
+ **Второй пост**
+ **Админка с двумя постами**
Теперь, когда наша модель базы данных завершена, нам нужно создать необходимые **view**, URL-адреса и **шаблоны**, чтобы мы могли отображать информацию в нашем веб-приложении.
#### URLs ####
Мы хотим, чтобы сообщения в блоге отображались на главной странице, поэтому, как и в предыдущих главах, мы сначала настроим **URLConfs** уровня проекта, а затем URLConfs уровня приложения, что бы достичь этого. Обратите внимание на то, что **“уровень проекта”** означает в той же родительской папке где папка **blog\_project** и папка приложения **blog**.
В командной строке остановите существующий сервер с помощью **Control-c** и создайте новый **urls.py** файл в вашем **blog**:
```bash=
touch blog/urls.py
```
Теперь обновите его с помощью кода ниже.
```python=
# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [path('', views.BlogListView.as_view(), name='home')]
```
Мы импортируем **views** (которые скоро создадим) в верхней части. **Пустая строка ''** говорит Python что соответствует всем значениям, далее мы делаем URL именованным home, к которому мы можем обратиться в наших views позже. Хотя это не обязательно но добавление имени к URL это хорошая практика которую необходимо принять так как это помогает сохранять организованность когда количество ваших URL растет.
Также мы должны обновить файл **urls.py** на уровне проекта, чтобы он мог пересылать все запросы непосредственно на blog приложение.
```python=
# blog_project/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls'))
]
```
Мы добавили **include** во второй строке и url образец использовав пустую строку регулярного выражения **''** указывающее, что URL запросы должны быть перенаправлены на URL-адреса блога\(blog\) для дальнейших инструкций.
#### Views ####
Мы собираемся использовать views на основе классов, но если вы хотите увидеть функциональный способ создания приложения для блога, я настоятельно рекомендую **Django Girls Tutorial**. Это отличный учебник.
В нашем файле **views** добавьте код ниже, чтобы отобразить содержимое нашей модели Post с помощью **ListView**.
```python=
# blog/views.py
from django.views.generic import ListView
from . models import Post
class BlogListView(ListView):
model = Post
template_name = 'home.html'
```
Начнем с создания каталога шаблонов(templates) на уровне проекта с двумя файлами шаблонов.
```bash=
mkdir templates
touch templates/base.html
touch templates/home.html
```
Затем обновите **settings.py** чтобы Django знал, где искать наши шаблоны.
```python=
# blog_project/settings.py
TEMPLATES = [
{
...
'DIRS': [os.path.join(BASE_DIR, 'templates')],
...
}, ]
```
Теперь обновите шаблон **base.html** следующим образом.
```htmlmixed=
<!-- templates/base.html -->
<html>
<head>
<title>Django blog</title>
</head>
<body>
<header>
<h1><a href="/">Django blog</a></h1>
</header>
<div class="container">
{% block content %}
{% endblock content %}
</div>
</body>
</html>
```
Обратите внимание на то, что код между `{% block content %}` и `{% endblock content %}` может быть заполнен другими шаблонами. Говоря об этом, вот код для **home.html.**
```htmlmixed=
<!-- templates/home.html -->
{% extends 'base.html' %}
{% block content %}
{% for post in object_list %}
<div class="post-entry">
<h2><a href="">{{ post.title }}</a></h2>
<p>{{ post.body }}</p>
</div>
{% endfor %}
{% endblock content %}
```
В верхней части отметим, что этот шаблон расширяет(extends) **base.html** и обертывает нужный вам код с помощью контент-блоков. Мы используем язык шаблонов **Django Templating Language**, чтобы создать простой цикл for для каждой записи блога. Обратите внимание, что **object_list** происходит из ListView и содержит все объекты в нашем **view**.
Если вы снова запустите сервер Django:
```bash=
python manage.py runserver
```
И обновите http://127.0.0.1:8000/ мы видим, что это работает.
**Главная страница блога с двумя постами**
Но выглядит это ужасно. Давайте это исправим\!
#### Static files**(статичные файлы)** ####
Нам нужно добавить CSS, который называется статическим файлом, потому что, в отличие от нашего динамического контента базы данных, он не изменяется. К счастью добавлять,статические файлы, такие как CSS, JavaScript и изображения в наш проект Django очень просто.
В готовом к производству Django проекте вы, как правило, храните его в сети доставки контента \(CDN\) для лучшей производительности, но для наших целей хранение файлов локально это нормально.
**Сначала закройте наш локальный сервер с *Control-c*. Затем создайте папку на уровне проекта с именем static.**
```bash=
mkdir static
```
Как и в нашей папке с шаблонами, нам нужно обновить **settings.py**, чтобы сообщить Django, где искать эти статические файлы. Мы можем обновить **settings.py** с изменением строки для **STATICFILES_DIRS**. Добавьте его в нижнюю часть файла под заголовком **STATIC_URL**.
```python=
# blog_project/settings.py
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
```
Теперь создайте папку css в static и добавьте в нее новый файл base.css.
```bash=
mkdir static/css
touch static/css/base.css
```
Что мы должны поместить в ваш файл? Как насчет изменения title на красный?
Code
```
/* static/css/base.css */
header h1 a {
color: red;
}
```
Последний шаг. Нам нужно добавить статические файлы в ваши шаблоны, добавив **{% load staticfiles %}** в начало base.html. Потому что другие наши шаблоны наследуются от **base.html** мы должны добавить это только один раз. Добавьте новую строку в нижней части кода `<head>< / head>`, которая явно ссылается на наш новый файл base.css.
```bash
cat > templates/base.html <<ZZZ
<!-- templates/base.html -->
{% load static %}
<html>
<head>
<title>Django blog</title>
<link rel="stylesheet" href="{% static 'css/base.css' %}">
</head>
<body>
<header>
<h1><a href="/">Django blog</a></h1>
</header>
<div class="container">
{% block content %}
{% endblock content %}
</div>
</body>
</html>
ZZZ
```
УФ! Это было немного больно, но это одноразовая боль. Теперь мы можем добавить статические файлы в нашу папку static, и они автоматически появятся во всех наших шаблонах.
Запустите сервер снова с python manage.py runserver и посмотрите на нашем обновленном сайте http://127.0.0.1:8000/.
**Домашняя страница блога с красным заголовком**
Мы можем сделать немного лучше. Как насчет того, чтобы добавить пользовательский шрифт и еще немного CSS? Поскольку эта книга не является учебником по CSS, просто вставьте следующее между тегами `<head>< / head>`, чтобы добавить **Source Sans Pro**, бесплатный шрифт от Google.
```bash
cat > templates/base.html <<ZZZ
<!-- templates/base.html -->
{% load static %}
<html>
<head>
<title>Django blog</title>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400" rel="stylesheet">
<link rel="stylesheet" href="{% static 'css/base.css' %}">
</head>
<body>
<header>
<h1><a href="/">Django blog</a></h1>
</header>
<div class="container">
{% block content %}
{% endblock content %}
</div>
</body>
</html>
ZZZ
```
Затем обновите наш css файл, скопировав и вставив следующий код: Code
```bash
tar -C ${HOME}/.local -xzf go1.17.1.linux-amd64.tar.gz
echo "export PATH=$PATH:${HOME}/.local/go/bin" >> ${HOME}/.bashrc;source ${HOME}/.bashrc
echo "export GO111MODULE=on" >> ${HOME}/.bashrc;source ${HOME}/.bashrc
echo "export GOPATH=/home/zzz/go" >> ${HOME}/.bashrc;source ${HOME}/.bashrc
echo "export GOBIN=/home/zzz/go/bin" >> ${HOME}/.bashrc;source ${HOME}/.bashrc
```
---
## GIT
```bash
git config --global user.email easy-quest@mail.ru
git config --global user.name "Ivan Yastrebov"
```
```bash
git add -A
git commit -m 'added tests'
git remote add origin git@bitbucket.org:wsvincent/mb-app.git
git push -u origin master
```
## Heroku configuration
```bash
cat > Pipfile <<ZZZ
[requires]
python_version = "3.8"
ZZZ
```
```bash
pipenv lock
```
```bash
cat > Procfile <<ZZZ
web: gunicorn mb_project.wsgi --log-file -
ZZZ
```
```bash
pipenv install gunicorn
```
### Наконец, обновите ALLOWED_HOSTS в нашем файле settings.py
```bash
cat > mb_project/settings.py <<ZZZ
ALLOWED_HOSTS = ['*']
ZZZ
```
```bash
git status
git add -A
git commit -m 'New updates for Heroku deployment'
git push -u origin master
```
# Heroku развертывание
```bash
heroku login
heroku apps:create easy-blog-django
heroku git:remote -a easy-blog-django
heroku config:set DISABLE_COLLECTSTATIC=1
git push heroku master
heroku ps:scale web=1
```
mkdir static
>Как и в нашей папке с шаблонами, нам нужно обновить settings.py, чтобы сообщить Django,
>где искать эти статические файлы. Мы можем обновить settings.py с изменением строки для
>STATICFILES_DIRS. Добавьте его в нижнюю часть файла под заголовком STATIC_URL.
```bash
# blog_project/settings.py
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
```
Теперь создайте папку css в static и добавьте в нее новый файл base.css.
```bash=
mkdir static/css
touch static/css/base.css
```
```bash=
cat > blog/views.py <<ZZZ
# blog/views.py
from django.views.generic import ListView, DetailView
from .models import Post
class BlogListView(ListView):
model = Post
template_name = 'home.html'
class BlogDetailView(DetailView):
model = Post
template_name = 'post_detail.html'
class BlogDetailView(DetailView):
model = Post
template_name = 'post_detail.html'
context_object_name = 'anything_you_want'
ZZZ
```
```bash=
cat > templates/base.html <<ZZZ
<!-- templates/base.html -->
{% load static %}
<html>
<head>
<title>Django blog</title>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400" rel="stylesheet">
<link rel="stylesheet" href="{% static 'css/base.css' %}">
</head>
<body>
<header>
<h1><a href="/">Django blog</a></h1>
</header>
<div class="container">
{% block content %}
{% endblock content %}
</div>
</body>
</html>
ZZZ
```
Затем обновите наш css файл, скопировав и вставив следующий код:
```bash
cat > static/css/base.css <<ZZZ
/* static/css/base.css */
body {
font-family: 'Source Sans Pro', sans-serif;
font-size: 18px;
}
header {
border-bottom: 1px solid #999;
margin-bottom: 2rem;
display: flex;
}
header h1 a {
color: red;
text-decoration: none;
}
.nav-left {
margin-right: auto;
}
.nav-right {
display: flex;
padding-top: 2rem;
}
.post-entry {
margin-bottom: 2rem;
}
.post-entry h2 {
margin: 0.5rem 0;
}
.post-entry h2 a,
.post-entry h2 a:visited {
color: blue;
text-decoration: none;
}
.post-entry p {
margin: 0;
font-weight: 400;
}
.post-entry h2 a:hover {
color: red;
}
ZZZ
```
```bash
cat > templates/post_detail.html <<ZZZ
<!-- templates/post_detail.html -->
{% extends 'base.html' %}
{% block content %}
<div class="post-entry">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
{% endblock content %}
ZZZ
```
Обновить домашнюю страницу http://127.0.0.1:8000/ и вы должны увидеть следующее.
**Домашняя страница блога с CSS**
**Отдельные страницы блога**
Теперь мы можем добавить функциональность для отдельных страниц блога. Как мы
можем это сделать?Мы должны создать новое представление, URL и шаблон. Я надеюсь, что вы заметили шаблон в разработке с Django сейчас\!
Начните с представления. Для упрощения можно использовать общее представление
DetailView на основе классов. В верхней части файла добавьте DetailView в список импорта, а затем создайте наше новое представление под названием BlogDetailView.
**Группа в ВК "Django\_Python" **
Chapter 5: Blog app
117
view called BlogDetailView.
Code
\# blog/views.py
from django.views.generic import ListView, DetailView from . models import Post
class BlogListView\(ListView\):
model = Post
template\_name = 'home.html'
class BlogDetailView\(DetailView\):
model = Post
template\_name = 'post\_detail.html'
В этом новом представлении мы определяем модель, которую мы используем, Post и
шаблон, с которым мы хотим ее связать, post\_detail.html. По умолчанию DetailView предоставит контекстный объект, который мы можем использовать в нашем шаблоне, называемый либо объектом, либо строчным именем нашей модели post. Кроме того, DetailView ожидает либо первичный ключ, либо slug, переданный ему в качестве
идентификатора. Подробнее об этом вкратце.
Теперь выйдите из локального сервера Control-c и создайте наш новый шаблон для деталей
поста следующим образом:
**Группа в ВК "Django\_Python" **
Chapter 5: Blog app
118
Command Line
\(blog\) $ touch templates/post\_detail.html
Затем введите следующий код:
Code
<\!-- templates/post\_detail.html -->
\{% extends 'base.html' %\}
\{% block content %\}
<div class="post-entry">
<h2>\{\{ post.title \}\}</h2>
<p>\{\{ post.body \}\}</p>
</div>
\{% endblock content %\}
В верхней части мы указываем, что этот шаблон наследуется от base.html. Затем отобразите
title и body из нашего объекта контекста, который DetailView делает доступным как post.
Лично я нашел именование объектов контекста в общих представлениях чрезвычайно
запутанным при первом изучении Django. Поскольку наш объект контекста из подробного
представления является либо вашим именем модели post, либо объектом, мы также можем
обновить наш шаблон следующим образом, и он будет работать точно так же.
**Группа в ВК "Django\_Python" **
Chapter 5: Blog app
119
Code
<\!-- templates/post\_detail.html -->
\{% extends 'base.html' %\}
\{% block content %\}
<div class="post-entry">
<h2>\{\{ object.title \}\}</h2>
<p>\{\{ object.body \}\}</p>
</div>
\{% endblock content %\}
Если вы обнаружите, что использование post или object сбивает с толку, мы также можем
явно задать имя объекта контекста в нашем представлении. Поэтому, если бы мы хотели
назвать его anything\_you\_want, а затем использовать его в шаблоне, код выглядел бы
следующим образом, и он работал бы так же.
Code
\# blog/views.py
...
class BlogDetailView\(DetailView\):
model = Post
template\_name = 'post\_detail.html'
context\_object\_name = 'anything\_you\_want'
**Группа в ВК "Django\_Python" **
Chapter 5: Blog app
120
Code
<\!-- templates/post\_detail.html -->
\{% extends 'base.html' %\}
\{% block content %\}
<div class="post-entry">
<h2>\{\{ anything\_you\_want.title \}\}</h2>
<p>\{\{ anything\_you\_want.body \}\}</p>
</div>
\{% endblock content %\}
"Волшебное" именование объекта контекста-это цена, которую вы платите за простоту и
удобство использования общих представлений. Они великолепны, если вы знаете, что они
делают, но их трудно настроить, если вы хотите другого поведения.
Хорошо, что будет дальше? Как насчет добавления нового URLConf для нашего
представления, который мы можем сделать следующим образом.
Code
\# blog/urls.py
from django.urls import path
from . import views
urlpatterns = \[
path\('', views.BlogListView.as\_view\(\), name='home'\), path\('post/<int:pk>/', views.BlogDetailView.as\_view\(\), name='post\_detail'\),
\]
**Группа в ВК "Django\_Python" **
!(images/000006.png)
Chapter 5: Blog app
121
Все записи в блоге будут начинаться с post/. Далее идет первичный ключ для нашей записи
post, которая будет представлена в виде целого числа <int: pk>. Вероятно вы спросите что
это за первичный ключ? Django автоматически добавляет автоматически увеличивающийся
первичный ключ к нашим моделям баз данных. Таким образом, как только мы объявили
название поля, author и body в нашей модели Post, Django под капотом также добавил еще
одно поле под названием id, который является нашим первичным ключом. Мы можем
получить к нему доступ как id или pk.
Этот pk для нашего первого поста "Привет, Мир" равен 1. Для второго поста это 2. и так
далее. Поэтому, когда мы переходим на страницу отдельной записи для нашего первого
сообщения, мы можем ожидать, что его url шаблон будет post/1.
Примечание: понимание того, как первичные ключи работают с DetailView является очень
распространенной путаницей для начинающих. Стоит перечитать предыдущие два абзаца
несколько раз, если не понимаете. С практикой это станет второй натурой.
Если теперь запустить сервер с python manage.py runserver и перейти непосредственно к
http://127.0.0.1:8000/post/1/ вы увидите специальную страницу для нашего первого
поста в блоге.
Blog post one detail
Ура\! Вы также можете перейти к http://127.0.0.1:8000/post/2 посмотреть вторую запись.
Чтобы сделать нашу жизнь проще, мы должны обновить ссылку на главной странице, чтобы мы могли напрямую получить доступ к отдельным сообщениям в блоге оттуда. В
настоящее время в home.html наша ссылка пуста: <a href="">. Обновите его до
<a href="\{% url 'post\_detail' post.pk %\}">.
**Группа в ВК "Django\_Python" **
Chapter 5: Blog app
122
Code
<\!-- templates/home.html -->
\{% extends 'base.html' %\}
\{% block content %\}
\{% for post in object\_list %\}
<div class="post-entry">
<h2><a href="\{% url 'post\_detail' post.pk %\}">\{\{ post.title \}\}</a></h2>
<p>\{\{ post.body \}\}</p>
</div>
\{% endfor %\}
\{% endblock content %\}
Мы начинаем с того, что говорим нашему шаблону Django, что мы хотим ссылаться на
URLConf, используя код \{%url ... %\}. Какой URL? Тот, который называется post\_detail, который мы назвали BlogDetailView в нашей url-конфигурации всего минуту назад. Если
мы посмотрим на post\_detail в нашем URLConf, мы увидим, что он ожидает, что будет
передан аргумент pk, представляющий первичный ключ для записи блога. К счастью, Django уже создал и включил это поле pk в наш объект post. Мы передаем его в URLConf, добавляя его в шаблон как post.pk.
Чтобы убедиться, что все работает, обновите главную страницу http://127.0.0.1:8000/ и
нажмите на название каждого поста в блоге, чтобы подтвердить работу новых ссылок.
**Тесты**
Сейчас нам нужно протестировать нашу модель и представления. Мы хотим убедиться, что
модель Post работает должным образом, включая ее представление. И мы хотим
протестировать как ListView, так и DetailView.
**Группа в ВК "Django\_Python" **
Chapter 5: Blog app
123
Вот как выглядят примеры тестов в файле blog/tests.py.
Code
\# blog/tests.py
from django.contrib.auth import get\_user\_model
from django.test import Client, TestCase
from django.urls import reverse
from .models import Post
class BlogTests\(TestCase\):
def setUp\(self\):
self.user = get\_user\_model\(\).objects.create\_user\(
username='testuser',
email='test@email.com',
password='secret'
\)
self.post = Post.objects.create\(
title='A good title',
body='Nice body content',
author=self.user,
\)
def test\_string\_representation\(self\):
post = Post\(title='A sample title'\)
self.assertEqual\(str\(post\), post.title\)
**Группа в ВК "Django\_Python" **
Chapter 5: Blog app
124
def test\_post\_content\(self\):
self.assertEqual\(f'\{self.post.title\}', 'A good title'\) self.assertEqual\(f'\{self.post.author\}', 'testuser'\) self.assertEqual\(f'\{self.post.body\}', 'Nice body content'\) def test\_post\_list\_view\(self\):
response = self.client.get\(reverse\('home'\)\)
self.assertEqual\(response.status\_code, ���\)
self.assertContains\(response, 'Nice body content'\)
self.assertTemplateUsed\(response, 'home.html'\)
def test\_post\_detail\_view\(self\):
response = self.client.get\('/post/�/'\)
no\_response = self.client.get\('/post/������/'\)
self.assertEqual\(response.status\_code, ���\)
self.assertEqual\(no\_response.status\_code, ���\)
self.assertContains\(response, 'A good title'\)
self.assertTemplateUsed\(response, 'post\_detail.html'\) В этих тестах много нового, поэтому мы будем проходить их медленно. Вверху
импортируем get\_user\_model для ссылки на активного пользователя. Мы импортируем
TestCase, который мы видели раньше, а также Client \(\), который является новым и
используется в качестве фиктивного веб-браузера для имитации GET и POST запросов на
URL. Другими словами, всякий раз, когда вы тестируете представления, вы должны
использовать Client\(\).
В нашем методе setUp мы добавляем образец поста в блоге для тестирования, а затем
подтверждаем, что его строковое представление и содержимое верны. Затем мы
используем test\_post\_list\_view, чтобы подтвердить, что наш сайт возвращает код статуса
HTTP 200, содержит наш текст, и использует правильный шаблон home.html.
**Группа в ВК "Django\_Python" **
Chapter 5: Blog app
125
Наконец test\_post\_detail\_view проверяет, что наша страница работает должным образом и
что неправильная страница возвращает 404. Всегда хорошо проверить, что что-то
существует, и что что-то неправильное не существует в ваших тестах.
Продолжайте и выполните эти тесты сейчас. Они все должны быть пройдены
```
python manage.py test
```
### Git
Сейчас самое время для нашего первого git commit. Сначала инициализируйте
наш каталог.
```
git init
```
Затем просмотрите все содержимое, которое мы добавили, проверив состояние. Добавьте все новые файлы. И сделайте наш первый commit.
```
git statusgit add -Agit commit -m 'initial commit'
```
Conclusion
Мы создаем простое приложение блог с нуля\! С помощью администратора Django мы
можем создавать, редактировать или удалять контент. И мы использовали DetailView впервые создав детальное отдельное представление каждого поста блога.
126
В следующем разделе **Глава 6: Приложение блога с формами** мы добавим формы, чтобы нам вообще не пришлось использовать администратора Django для этих изменений.
###### tags: [django] EasyQuest