--- 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;">