Django-приложение для ведения личного дневника

В этом руководстве мы разработаем и создадим личный дневник, в котором пользователи смогут: 

  • добавлять новые записи; 
  • просматривать их; 
  • редактировать и удалять конкретные записи. 

UI и UX

Перед созданием любого приложения следует уделить внимание UI (пользовательскому интерфейсу) и UX (пользовательскому опыту): продумать навигацию и внешний вид. Не усложняйте UI. Изобразите конечный результат в общих чертах. Так вы ускорите и упростите процесс разработки. Например, наше приложение для ведения личного дневника схематично выглядит так:

При открытии приложения на домашней странице отображаются все записи, а новые добавляются одним нажатием на кнопку.

Предварительные требования 

Все, что нужно  —  базовые знания Python и Django, а также навыки работы с инструментом командной строки. 

Создание личного дневника посредством Django 

Прежде всего обзаведемся виртуальной средой. Она нужна для установки зависимостей проекта. Если вы не знакомы с этим инструментом, то за разъяснениями можно обратиться к документации Python. 

mkdir JOURNAL

cd journal/

python3 -m venv myenv

Активируем виртуальную среду: 
source myenv/bin/activate

Создаем проект Django:
django-admin startproject journalapp

Устанавливаем Django:
python3 -m pip install Django

База данных 

Django поставляется с легковесной базой данных SQLite, которая отлично подходит для простых проектов. Открываем файл settings.py, содержащий конфигурации БД для SQLite. 

DATABASES = {    
    'default': {       
        'ENGINE': 'django.db.backends.sqlite3',       
        'NAME': BASE_DIR / 'db.sqlite3',    }
}

Создание суперпользователя 

С помощью команды cd переходим в journalapp и создаем суперпользователя: 

cd journalapp
python3 manage.py createsuperuser

Запуск сервера 

Запускаем сервер разработки, чтобы начать работу приложения: 

python3 manage.py runserver

Переходим по адресу localhost:8000 и видим отлично работающее приложение: 

Для регистрации в качестве администратора перемещаемся на localhost: admin и входим в систему с учетными данными суперпользователя: 

Создание приложения для ведения дневника 

В Django для разграничения различных компонентов целого приложения создаются отдельные приложения. Особенно рекомендуется это делать по мере его роста. Например, если бы вы разрабатывали систему управления больницей, то у вас бы появились приложения для докторов, пациентов и т.д. Данный проект предполагает создание только одного приложения с именем journal.

В терминале нажимаем на ctrl+C и прекращаем работу сервера. Создаем приложение journal с помощью следующей команды: 

python3 manage.py startapp journal

В результате появляется каталог journal с файлами: 

Переходим в settings.py и добавляем приложение journal в список INSTALLED_APPS, как показано ниже:  

# settings.py

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'journal',
]

Так Django узнает о существовании journal. Как видно, он уже содержит такие встроенные приложения, как:

  • auth;
  • admin;
  • contenttypes
  • sessions и т.д. 

Создание моделей 

Django использует модели для представления объектов. Под объектами понимаются данные, которые подлежат хранению в приложении, например книги, фильмы, сведения о людях и т.д. В свою очередь, модели обладают атрибутами. Например, у человека может быть имя, возраст и т.д. 

Поскольку дневник предполагает название, описание и даты, создаем в приложении journal модель Model под названием Journal с атрибутами: 

  • title (название); 
  • description (описание); 
  • date_created (дата). 

Открываем journal/models.py и добавляем модель Journal.

from django.db import models
from django.urls import reverse

# В этой части создаем модели. 
class Journal(models.Model):
    title = models.CharField(max_length=100, blank = False)
    description = models.TextField()
    date_created = models.DateTimeField(auto_now_add= True)

    class Meta:
        ordering = ['date_created']
    def __str__(self):
        return self. title

Миграции  —  команда makemigrations

На текущий момент у нас есть база данных, которая пока ничего не знает о моделях. Сообщим о них с помощью миграций, которые реализуют внесенные в модели изменения. За каждым изменением обязательно следует выполнение миграций. Наш случай не исключение. 

python3 manage.py makemigrations

Ниже представлен результат: 

Migrations for ‘journal’:
journal/migrations/0001_initial.py

После выполнения команды makemigrations получаем файл миграций с инструкциями по созданию схемы базы данных. Следующий этап предполагает создание таблиц в БД. Для этой цели потребуется команда migrate

python3 manage.py migrate

В терминале отображается следующий результат: 

Operations to perform:
Apply all migrations: admin, auth, contenttypes, journal, sessions
Running migrations:
Applying contenttypes.0001_initial… OK
Applying auth.0001_initial… OK
Applying admin.0001_initial… OK
Applying admin.0002_logentry_remove_auto_add… OK
Applying admin.0003_logentry_add_action_flag_choices… OK
Applying contenttypes.0002_remove_content_type_name… OK
Applying auth.0002_alter_permission_name_max_length… OK
Applying auth.0003_alter_user_email_max_length… OK
Applying auth.0004_alter_user_username_opts… OK
Applying auth.0005_alter_user_last_login_null… OK
Applying auth.0006_require_contenttypes_0002… OK
Applying auth.0007_alter_validators_add_error_messages… OK
Applying auth.0008_alter_user_username_max_length… OK
Applying auth.0009_alter_user_last_name_max_length… OK
Applying auth.0010_alter_group_name_max_length… OK
Applying auth.0011_update_proxy_permissions… OK
Applying auth.0012_alter_user_first_name_max_length… OK
Applying journal.0001_initial… OK
Applying sessions.0001_initial… OK

Реализация функциональности CRUD (Create, Read, Update, Delete)

Работа по настройке базы данных и определению моделей завершена. Переходим к созданию, чтению, обновлению и удалению записей. 

Регистрация модели Journal 

Самый простой способ внести записи в базу данных  —  зарегистрировать модель Journal с помощью Django admin. Для этого открываем файл journal/admin.py и добавляем код: 

from django.contrib import admin

from .models import Journal

# В этой части регистрируем модели. 
admin.site.register(Journal)

Запускаем сервер разработки, переходим по адресу localhost:8000/admin и добавляем несколько записей в дневник, обеспечивая себя данными для последующей работы. 

Общие представления на основе классов 

Разработка веб-приложений сопряжена с монотонной рутиной многократного повторения шаблонов. Django облегчает эту участь, предлагая общие представления для их абстрагирования. Как следствие, укоряется процесс написания стандартных представлений. 

Общие представления на основе классов обеспечивают быструю реализацию операций создания, извлечения, обновления и удаления. 

В Django представления применяются для отображения шаблонов в браузере. К числу самых распространенных из них относятся: 

  • ListView;
  • DetailView;
  • CreateView;
  • UpdateView;
  • DeleteView

Сначала отобразим список всех записей в таблице Journal. Открываем файл journal/views.py, импортируем модель Journal из файла model.py и ListView из django.views.generic в верхней части файла. 

# views.py

from .models import Journal

from django.views.generic import ListView

Создаем JournalListView, который будет получать все записи Journal в базе данных и передавать информацию в шаблон для отображения. 

from django.shortcuts import render
from .models import Journal
from django.views.generic import ListView

# В этой части создаем представления.

class JournalListView(ListView):
model = Journal
template_name = "journal/journal_list.html"

Здесь мы определяем JournalListView для модели Journal и явно указываем представлению на шаблон для использования. Если же он там не определен, то Django логически выведет его из имени объекта. 

Создаем шаблон templates/journal/journal_list.html. Django автоматически начнет искать шаблоны в директории templates, структура которой выглядит так: 

journal     

  templates      

      journal        

        journal_list.html

Открываем templates/journal/journal_list.html и добавляем код: 

{% block content %}
<h2>ALL JOURNALS</h2>
<a href=""><button class="btn" type = "submit">Add a New Journal</button></a>
{% for journal in journal_list
%}
<ul>
<li>{{ journal.title }}</li>
<li>{{ journal.description |truncatechars:120 }}</li>
<li>{{ journal.date_created }}</li>
</ul>
{% endfor %}
{% endblock %}

Здесь мы проводим рендеринг в зависимости от контекста. Он содержит переменную journal_list, которая включает все объекты Journal. Django автоматически заполняет контекст, применяя строчную версию имени класса модели. Для лучшего понимания кода можно также задать имя переменной контекста при определении представления. Обновляем класс JournalListView следующим образом: 

class JournalListView(ListView):    

   model = Journal    

   template_name = "journal/journal_list.html"    

   context_object_name = "journal_list"

Привязываем представление к URL-адресам:

# journal/urls.py
from django.urls import path
from . import views

urlpatterns = [
path('journal/list', views.JournalListView.as_view(), name="journallist"),
]

Эти URL-адреса не заработают до тех пор, пока вы их не свяжете с urlpatterns проекта. Открываем journalapp/urls.py и включаем их в список urlpatterns

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
path('admin/', admin.site.urls),
path("", include('journal.urls')),
]

Переходим по адресу http://127.0.0.1:8000/journal/list и видим, что страница пополнилась несколькими имеющимися записями: 

django.contrib.humanize —  это набор шаблонных фильтров Django для представления данных в удобочитаемом виде. Например, сделаем так, что у первой вышеприведенной записи вместо формата Jan. 10, 2021, 3:59 p.m. будет отображаться количество месяцев (9 months) и недель (1 week ago) со дня ее добавления. Для активации данных фильтров включаем django.contrib.humanize в настройки INSTALLED_APPS

# journalapp/settings.py

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'journal',
'django.contrib.humanize',
]

После этого используем {% load humanize %} в шаблоне journal_list

{% load humanize %}
{% block content %}
<h2>ALL JOURNALS</h2>
<a href=""><button class="btn" type = "submit">Add a New Journal</button></a>
{% for journal in journal_list
%}
<ul>
<li>{{ journal.title }}</li>
<li>{{ journal.description |truncatechars:120 }}</li>
<li>{{ journal.date_created|naturaltime }}</li>
</ul>
{% endfor %}
{% endblock %}

Создание базового шаблона 

Django допускает применение базового шаблона, содержащего все общие элементы сайта и устанавливающего блоки, которые могут переопределяться дочерними шаблонами. 

Приступаем к определению шаблона. Создаем /templates/journal /base.html и добавляем код: 

<!DOCTYPE html>
<html lang="en">
<head>
{% block title %}
<title>MY Journal</title>
{% endblock %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-sm-12 ">
{% block content %}
<!-- Journal list page and other pages will fill this space -->
{% endblock %}
</div>
</div>
</div>
</body>
</html>

В данном случае страница со списком записей заполнит блок контента, определенного в базовом шаблоне. 

Вносим изменения в файл templates/journal/journal_list.html, как показано ниже: 

{% extends "journal/base.html" %}
{% load humanize %}
{% block content %}
<h2>ALL JOURNALS</h2>
<a href=" "><button class="btn btn-primary" type = "submit">Add a New Journal</button></a>
<div>
{% for journal in journal_list %}
<div class="list-group">
<a href=" " class="list-group-item list-group-item-action flex-column align-items-start">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{ journal.title }}</h5>
<small>{{ journal.date_created|naturaltime }}</small>
</div>
<p class="mb-1">{{ journal.description| truncatechars:120}}</p>
</a>
</div>
{% endfor %}
{% endblock %}
</div>

Обновляем страницу с записями: 

Как видно, применив Bootstrap для стилизации, мы придали странице более привлекательный вид и представили дату в удобном для чтения формате.  

Создание новой записи 

Синтаксис создания новых записей практически аналогичен способу их отображения, за исключением ряда ключевых отличий. В данном случае мы работаем с классом CreateView и предоставляем свойство fields.

Начнем с импорта CreateView из django.views.generic.edit в верхней части:

from django.views.generic import ListView,CreateView

Далее создаем представление класса и добавляем необходимые свойства: 

  • model;
  • template_name
  • fields.
class JournalCreateView(CreateView):    
    model = Journal
    template_name = 'journal/create_journal_form.html'
    fields = ['title', 'description']
    success_url = reverse_lazy('journallist')

Создаем шаблон journal/create_journal_form.html и дописываем код: 

{% extends "journal/base.html" %}
{% block content %}
<h2>Create a New Journal</h2>
<form method="post">
<div class="form-group">
{% csrf_token %} {{ form.as_p }}
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
{% endblock %}

Открываем journal/urls.py и обновляем путь для создания новой записи:

urlpatterns = [
path('journal/list', views.JournalListView.as_view(), name="journallist"),
path('journal/journalcreate', views.JournalCreateView.as_view(), name="createjournal"),
]

Смотрим, работает ли форма для создания новой записи. Для этого переходим по адресу http://127.0.0.1:8000/journal/journalcreate

Добавляем новую запись и нажимаем кнопку отправки. Это действие перенаправляет на страницу с записями дневника, что становится возможным благодаря success_url, определенному в представлении. 

Обновление и удаление записей 

UpdateView и DeleteView предназначены соответственно для обновления и удаления записей. Открываем journal/views.py и добавляем требуемые функции и их свойства. Файл journal/views.py должен выглядеть так: 

from django.shortcuts import render
from models import Journal
from django.views.generic import ListView, CreateView, UpdateView,DeleteView
from django.urls import reverse_lazy

# Создаем представления в этой части кода.

class JournalListView(ListView):
model = Journal
template_name = 'journal/journal_list.html'
context_object_name = 'journal_list'

class JournalCreateView(CreateView):
model = Journal
template_name = 'journal/create_journal_form.html'
fields = ['title', 'description']
success_url = reverse_lazy('journallist')

class JournalUpdateView(UpdateView):
model = Journal
template_name = 'journal/update_journal_form.html'
fields = ['title', 'description']

class JournalDeleteView(DeleteView):
model = Journal
template_name = 'journal/delete_journal_form.html'
success_url = reverse_lazy('journallist')

Обновление urls

Для удаления или обновления экземпляра в вышеуказанном коде к пути прикрепляется первичный ключ. 

В файле urls.py прописываем пути следующим образом: 

urlpatterns = [
path('journal/list', views.JournalListView.as_view(), name="journallist"),
path('journal/journalcreate', views.JournalCreateView.as_view(), name="createjournal"),
path('journal/journalupdate/<pk>', views.JournalUpdateView.as_view(), name="updatejournal"),
path('journal/journaldelete/<pk>', views.JournalDeleteView.as_view(), name="deletejournal"),
]

Django предпримет поиск первичного ключа и отобразит конкретную запись. 

Создание шаблонов обновления и удаления записей 

update_journal_form.html

<!-- journal/update_journal_form.html -->
{% extends "journal/base.html" %}
{% block content %}
<h2>Update Journal</h2>
<form method="post">
<div class="form-group">
{% csrf_token %} {{ form.as_p }}
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
<a href="{% url 'deletejournal' journal.id %}"><button class="btn btn-danger" >Delete</button></a>
{% endblock %}

delete_journal_form.html

<!-- journal/delete_journal_form.html -->
{% extends "journal/base.html" %}
{% block content %}
<h2>Delete Journal</h2>
<form method="post">
<div>
{% csrf_token %}
<p>Are you sure you want to delete "{{ object }}"?</p>
<input type="submit" value="Confirm" />
</div>
</form>
{% endblock %}

Как видно, переменная объекта используется для доступа к конкретным деталям записи.

При нажатии на заданную запись вы перенаправляетесь на страницу обновления, где можете ее отредактировать или удалить. Страница теперь выглядит таким образом: 

При намерении удалить запись вы будете перенаправлены на нижеуказанную страницу: 

Заключение 

В данном руководстве был представлен исчерпывающий материал о функциональности CRUD в Django. Надеюсь, он вам понравился. В целом Django весьма щедр на предоставляемые возможности.

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Esther Vaati: Build a Personal Journal With Django and Python

Предыдущая статьяУспешный релиз ПО: распространенные ошибки перед запуском продукта
Следующая статьяКак стать разработчиком React в 2022 году?