Я расскажу о там, как создать простой блог на фреймворке Django. Статья будет полезна новичкам в сфере Веб-программирования для того, чтобы разобраться в принципе работы и сути django. И так, поехали!
Проект написан с использованием:
Python 3.6.3 — высокоуровневый язык программирования.
Django 2.0.2 — фреймворк для веб-приложений.
Visual Code — текстовый редактор c поддержкой плагинов
Перед началом работы вам необходимо установить Python для дальнейшей работы с django. Для этого переходим на официальный сайт, скачиваем дистрибутив и устанавливаем его (как обычное приложение), поставив галочку напротив «Add Python to the PATH» (Добавить Python к системной переменной PATH). Если все сделано верно, то прописав в консоли (WIN + R > cmd) python, вы увидите следующее:
Для выхода пропишите exit().
Django установим с помощью менеджера пакетов Python — pip:
1 |
pip install django |
Для начала создадим папку для проекта в любом удобном месте и назовем ее «djangoProject». Далее Скопируем адрес этой папки и откроем консоль (WIN + R > cmd).
В консоли перейдем в созданную ранее папку. В моем случае папка находится на другом диске, поэтому сначала следует перейти на него — «d:»
1 |
cd <путь до папки> |
Создадим django проект с произвольным именем «myBlog»
1 |
django-admin startproject myBlog |
Перейдем в созданную папку и откроем ее в каком-либо текстовом редакторе. Я использую Visual code и запускаю его с помощью команды «code .»
1 2 |
cd myBlog code . |
Ненадолго отложим редактор и запустим стандартный проект django (при выполнении команд с обращением к файлу manage.py, например, запуск сервера вы должны находится в непосредственно в папке с этим файлом. В данном случае это D:\djangoProject\myBlog)
1 |
python manage.py runserver |
Откроем страницу в браузере по адресу https://127.0.0.1:8000/
Ура! Все работает. Теперь создадим аккаунт администратора, с помощью которого вы сможете добавлять, редактировать и удалять статьи (это конечно не все возможности админки django, но пока их будет достаточно).
Завершим работу запущенного сервера:
1 |
Ctrl + C |
Для начала необходимо поработать с миграциями. Миграции в django используются для переноса изменений в моделях на структуру базы данных. Модели в свою очередь отражают данные в виде полей в таблице базы данных. В нашем случае в базу данных необходимо занести таблицы для хранения данных админа.
1 |
python manage.py migrate |
Cоздадим суперюзера
1 |
python manage.py createsuperuser |
Нам будет предложено ввести логин, e-mail почту и пароль.
Логин для удобства введем admin, почту можно не вводить и пароль (мин. 8 символов). Будьте внимательны, вводимые символы пароля в консоли никак не отображаются (в целях безопасности).
Попробуем залогиниться в админ панели. Для этого запустим сервер:
1 |
python manage.py runserver |
Зайдем на страницу по адресу 127.0.0.1:8000/admin. Вводим логин и пароль от админки и жмем «Log in».
В итоге откроется админ панель:
Все работает, стартовый проект создан и создан аккаунт администратора. Сейчас перейдем к созданию Веб-приложения в django. В нашем случае Веб-приложение — это блог, который имеет свой собственный функционал (вывод всех статей и вывод отдельной).
Для этого завершим работу сервера (Ctrl + C) и пропишем следующую команду:
1 |
django-admin startapp blog |
Где «blog» — это произвольное название приложения. В итоге в директории djangoProject/myBlog появился новый каталог с именем «blog» и некоторыми .py файлами.
Далее нужно дать понять Django, что нужно использовать в работе наше новое приложение «blog». Для этого в файле settings.py с расположением djangoProject/myBlog/myBlog/settings.py изменяем настройку INSTALLED_APPS следующим образом:
1 2 3 4 5 6 7 8 9 |
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', ] |
Поработаем с обработкой url-запросов. Нам необходимо, чтобы при запросе 127.0.0.1:8000 открывалась главная страница со списком всех статей.
Отредактируем файл djangoProject/myBlog/myBlog/urls.py следующим образом:
1 2 3 4 5 6 7 |
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('', include('blog.urls')), path('admin/', admin.site.urls), ] |
Если говорить простым языком, то Django сравнивает url-запрос с данными в urlpatterns. Т.е. открыв страницу с адресом 127.0.0.1:8000/ django выполнит строку path(», include(‘blog.urls’)). На основе которой будет «читать» файл по адресу djangoProject/myBlog/blog/urls.py. Создадим и отредактируем этот файл:
1 2 3 4 5 6 7 |
from django.urls import path from .import views urlpatterns = [ path('', views.home, name='home'), ] |
Здесь идет переход «чтения» в файл views.py (в этой же папке), в котором используется функция home
Файл djangoProject/myBlog/blog/views.py
1 2 3 4 5 6 |
from django.http import HttpResponse from django.shortcuts import render def home(request): return render(request, "partial/home.html") |
С помощью функции render выполняем шаблон home.html. Для этого создаем в папке djangoProject/myBlog новую с именем «templates». В этой папке будут храниться все требуемые шаблоны страниц (главная, шаблон подробного просмотра статьи).
И редактируем в файле djangoProject/myBlog/myBlog/settings.py настройку TEMPLATES:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> {% block head %} <title>Title</title> {% endblock %} </head> <body> {% block body %} {% endblock %} </body> </html> |
Это базовый шаблон, который будет дополнятся другими.
Создадим в папке djangoProject/myBlog/templates новую папку partial, в которой и будут находится .html файлы, расширяющие базовый.
В папке partial создадим файл home.html:
1 2 3 4 5 6 7 8 9 10 |
{% extends 'base.html' %} {% block head %} <title>Главная</title> <meta name="description" content="Главная страница блога"/> <meta name="keywords" content="блог"/> {% endblock %} {% block body %} <h1>Главная</h1> {% endblock %} |
При обращении к файлу home.html содержимое блоков head и body в файле base.html заменится содержимым файла home.html. такая система организации шаблонов добавляет динамику и в разы упрощает работу.
Теперь запустив сервер откроется не стандартная страница django, а наш файл base.html, расширенный файлом partial/home.html
1 |
python manage.py runserver |
Точно таким же способом добавим страницу для просмотра отдельной статьи, которую назовем partial/single.html:
Отредактируем файл djangoProject/myBlog/blog/urls.py, где <int:id> — указывает, что на этом месте может располагаться число, которое будет считываться в файле djangoProject/myBlog/blog/views в функции single. Это число будет использоваться как номер определенной статьи.
1 2 3 4 5 6 7 8 |
from django.urls import path from .import views urlpatterns = [ path('', views.home, name='home'), path('<int:id>/', views.single, name='single'), ] |
В файле djangoProject/myBlog/blog/views.py добавим новую функцию для вывода отдельной статьи
1 2 3 4 5 6 7 8 9 |
from django.http import HttpResponse from django.shortcuts import render def home(request): return render(request, "partial/home.html") def single(request, id=None): return render(request, "partial/single.html") |
Создаем шаблон для вывода отдельной статьи djangoProject/myBlog/templates/partial/single.html
1 2 3 4 5 6 7 8 9 10 |
{% extends 'base.html' %} {% block head %} <title>Странца статьи</title> <meta name="description" content="Главная страница блога"/> <meta name="keywords" content="блог"/> {% endblock %} {% block body %} <h1>Странца статьи</h1> {% endblock %} |
Теперь после запуска сервера и переходе по ссылке 127.0.0.1:8000/323 откроется нужная страница.
Создадим таблицу в базе данных с помощью моделей. Каждая статья будет содержать следующие поля:
Файл djangoProject\myBlog\blog\models.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
from django.db import models # Create your models here. class Post(models.Model): title = models.CharField(max_length=120) description = models.TextField(default='Описание') keywords = models.CharField(max_length=120, default='Ключевые слова') image = models.FileField(null=True, blank=True) content = models.TextField() visible = models.BooleanField(default=1) updated = models.DateTimeField(auto_now=True, auto_now_add=False) timestamp = models.DateTimeField(auto_now=False, auto_now_add=True) def __unicode__(self): return self.title def __str__(self): return self.title def get_absolute_url(self): return "/%s/" %(self.id) class Meta: ordering = ["-id", "-timestamp"] |
Для отображения нашей модели статьи в админ панели отредактируем файл djangoProject\myBlog\blog\admin.py, где list_display — поля, отображающиеся в списке статей; list_display_links — поля, являющиеся ссылками для подробного просмотра полей статьи; list_editable — поля, доступные для редактирования сразу из просмотра списка всех статей; list_filter — фильтры в правой части страницы; search_fields — поля по которым осуществляется поиск.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
from django.contrib import admin # Register your models here. from .models import Post class PostModelAdmin(admin.ModelAdmin): list_display = ["id" ,"title", "updated", "timestamp"] list_display_links = ["id", "updated"] list_editable = ["title"] list_filter = ["updated", "timestamp"] search_fields = ["title", "content"] class Meta: model = Post admin.site.register(Post, PostModelAdmin) |
Укажем папку для сохранения загружаемых изображений (поле image) и паку для хранения статических файлов (например, css файлы, js срипты). Для этого добавим в конце файла djangoProject\myBlog\settings.py следующие 3 строчки
1 2 3 4 5 |
MEDIA_URL = '/media/' STATIC_ROOT = os.path.join(BASE_DIR, "static") MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "media") |
И создадим эти папки со следующим расположением: djangoProject/media и djangoProject/static
Помимо сохранения изображений через админ панель необходимо корректно их загружать в шаблон. Для этого отредактируем файл djangoProject\myBlog\views.py
1 2 3 4 5 6 7 8 9 10 11 |
from django.contrib import admin from django.urls import path, include from django.conf import settings from django.conf.urls.static import static urlpatterns = [ path('', include('blog.urls')), path('admin/', admin.site.urls), ] urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) |
Запускаем сервер
1 |
python manage.py runserver |
Заходим в админ панель — 127.0.0.1:8000/admin > переходим в Posts > add Post (в верхнем правом углу). Заполняем текстовые поля, загружаем изображение и оставляем галочку напротив visible (т.е. не скрывать статью). Нажимаем SAVE. Статья добавлена, изображение сохранилось в папку djangoProject/media
Настало время для вывода статей в шаблон home, т.е. на главную страницу.
Изменяем файл djangoProject/myBlog/blog/views.py. Подключаем модули для работы с пагинацией (постраничный вывод новостей) и модель Post. В функции home получаем все записи из таблицы Post и делим их по 4 на каждую страницу. Словарь context содержит ключи и значения, которые будут далее выводиться в шаблон.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
from django.http import HttpResponse from django.shortcuts import render from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from blog.models import Post def home(request): postList = Post.objects.filter(visible='1') paginator = Paginator(postList, 4) page = request.GET.get('page') querysetGoods = paginator.get_page(page) context = { "postList": postList, "title": "Главная страница блога", "desc": "Описание для главной страницы", "key": "ключевые, слова", } return render(request, "partial/home.html", context) def single(request, id=None): return render(request, "partial/single.html") |
Файл djangoProject\myBlog\templates\partial\home.html
С помощью тега truncatechars, обрежем строку до 70 первых символов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
{% extends 'base.html' %} {% block head %} <title>Главная</title> <meta name="description" content="{{ desc }}"/> <meta name="keywords" content="{{ key }}"/> {% endblock %} {% block body %} <h1>Главная</h1> {% if not posts %} <b>Статьи не найдены</b> {% else %} {% for obj in posts %} <a href="{{ obj.get_absolute_url }}"><b>{{ obj.title }}</b></a> <img src="{{ obj.image.url }}" alt=""> <p>{{ obj.content|truncatechars:70 }}</p> <span>{{ obj.timestamp }}</span> <br><br><br> {% endfor %} {% endif %} {% if posts.has_previous %} <a href="?page={{ posts.previous_page_number }}">Назад</a> {% endif %} <span>{{ posts.number }} / {{ posts.num_pages }}</span> {% if posts.has_next %} <a href="?page={{ posts.next_page_number }}" >Вперед</a> {% endif %} {% endblock %} |
В базовый шаблон djangoProject\myBlog\templates\base.html добавим css файл, который следует создать с расположением djangoProject\static\css\style.css
1 2 3 4 5 |
img { width: 400px; height: auto; display: block; } |
Файл djangoProject\myBlog\templates\base.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> {% load static %} <link href="{% static 'css/style.css' %}" rel="stylesheet"> {% block head %} <title>Title</title> {% endblock %} </head> <body> {% block body %} {% endblock %} </body> </html> |
Для проверки пагинации добавим еще несколько статей.
Все работает! При переходе на следующую страницу изменяется параметр «page», указывающий на номер текущей страницы.
Займемся выводом отдельной статьи (подробной просмотр). Роутинг (в файле djangoProject\myBlog\blog\urls.py) уже настроен, осталось добавить выводимую информацию, в зависимости от id в адресной строке. Отредактируем файл djangoProject\myBlog\blog\views.py, в котором создадим новую функцию «single». Обратите внимание, в начале файле была подключена вспомогательная функция «get_object_or_404», возвращающая необходимую статью (по id) или же исключение 404, если пользователь введет несуществующий id.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
from django.http import HttpResponse from django.shortcuts import render, get_object_or_404 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from blog.models import Post def home(request): postList = Post.objects.filter(visible='1') paginator = Paginator(postList, 2) page = request.GET.get('page') posts = paginator.get_page(page) context = { "posts": posts, "title": "Главная страница блога", "desc": "Описание для главной страницы", "key": "ключевые, слова", } return render(request, "partial/home.html", context) def single(request, id=None): post = get_object_or_404(Post, id=id) context = { "post": post, } return render(request, "partial/single.html", context) |
И файл djangoProject\myBlog\templates\partial\single.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{% extends 'base.html' %} {% block head %} <title>{{ post.title }}</title> <meta name="description" content="{{ post.description }}"/> <meta name="keywords" content="{{ post.keywords }}"/> {% endblock %} {% block body %} {% if post.visible %} <h1>{{ post.title }}</h1> <img src="{{ post.image.url }}" alt=""> <p>{{ post.content }}</p> {% else %} <p>Страница не найдена</p> {% endif %} {% endblock %} |
Наконец готов простой блог на django фреймворке. Итак, что в итоге: