---
tags: Python Web
source: https://python-scripts.com/create-blog-django
---
# Тема 4. Приложение Блог
https://github.com/roman-yatsenko/django-topics/tree/main/django-blog
## Начальная настройка блога
```shell
pipenv install Django==2.2.16
pipenv shell
django-admin startproject blog_project .
python manage.py startapp blog
```
### `blog_project\settings.py`
```python=33
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog'
]
```
```shell
python manage.py migrate
python manage.py runserver
```
## Создание модели Post для работы с записями блога
### `blog\models.py`
```python=
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
```
```shell
python manage.py makemigrations blog
python manage.py migrate blog
```
## Панель администратора для блога
```shell
python manage.py createsuperuser
python manage.py runserver
```
### `blog\admin.py`
```python=
from django.contrib import admin
from .models import Post
admin.site.register(Post)
```
## BlogListView для отображения записей блога
### `blog\views.py`
```python=
from django.views.generic import ListView
from .models import Post
class BlogListView(ListView):
model = Post
template_name = 'home.html'
```
## Настройка URL маршрутов для блога
```shell
touch blog/urls.py
```
### `blog\urls.py`
```python=
from django.urls import path
from .views import BlogListView
urlpatterns = [
path('', BlogListView.as_view(), name='home'),
]
```
### `blog_project\urls.py`
```python=16
from django.contrib import admin
from django.urls import path, include # новое изменение
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')), # новое изменение
]
```
## Создание шаблона для блога
```shell
mkdir templates
touch templates/base.html
touch templates/home.html
```
### `blog_project\settings.py`
```python=55
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # новое
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
```
### `templates\base.html`
```htmlmixed=
<html>
<head>
<title>Django blog</title>
</head>
<body>
<header>
<h1><a href="{% url 'home' %}">Django blog</a></h1>
</header>
<div>
{% block content %}
{% endblock content %}
</div>
</body>
</html>
```
### `templates\home.html`
```htmlmixed=
{% 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 %}
```
```shell
python manage.py runserver
```
## Подключение статических файлов в Django
```shell
mkdir static
```
### `blog_project\settings.py`
```python=122
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
```
```shell
static/css
static/css/base.css
```
### `static\css\base.css`
```css=
header h1 a {
color: red;
}
```
### `templates\base.html`
```htmlmixed=
{% load static %}
<html>
<head>
<title>Django blog</title>
<link href="{% static 'css/base.css' %}" rel="stylesheet">
</head>
...
```
```shell
python manage.py runserver
```
### `templates\base.html`
```htmlmixed=4
<title>Django blog</title>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400" rel="stylesheet">
...
```
### `static\css\base.css`
```css=
body {
font-family: 'Source Sans Pro', sans-serif;
font-size: 18px;
}
header {
border-bottom: 1px solid #999;
margin-bottom: 2rem;
}
header h1 a {
color: red;
text-decoration: none;
}
.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;
}
```
## Создаем отдельную страницу DetailView для статьи
### `blog\views.py`
```python=
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'
```
### `blog\urls.py`
```python=3
from .views import BlogListView, BlogDetailView # новое изменение
urlpatterns = [
path('post/<int:pk>/', BlogDetailView.as_view(), name='post_detail'), # новое изменение
path('', BlogListView.as_view(), name='home'),
]
```
```shell
touch templates/post_detail.html
```
### `templates\home.html`
```htmlmixed=3
{% 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 %}
```
### `templates\post_detail.html`
```htmlmixed=3
{% extends 'base.html' %}
{% block content %}
<div class="post-entry">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
{% endblock content %}
```
```shell
python manage.py runserver
# http://127.0.0.1:8000/post/1/
```
## TestCase для блога
### `blog\tests.py`
```python=
# blog/tests.py
from django.contrib.auth import get_user_model
from django.test import 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 good title')
self.assertEqual(str(post), post.title)
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, 200)
self.assertContains(response, 'Nice body content')
self.assertTemplateUsed(response, 'home.html')
def test_post_detail_view(self):
response = self.client.get('/post/1/')
no_response = self.client.get('/post/100000/')
self.assertEqual(response.status_code, 200)
self.assertEqual(no_response.status_code, 404)
self.assertContains(response, 'A good title')
self.assertTemplateUsed(response, 'post_detail.html')
```
---
(c) Яценко Р.Н., 2018-2020
[Учебный центр компьютерных технологий "Кит"](http://kit.kh.ua/)
<img src="https://i.imgur.com/Kh901c1.png" style="width: 150px; position: fixed; top: 100px; right: 10px; border: 0; box-shadow: none;">