Share This
Связаться со мной
Крути в низ
Categories
//Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Изучаем основные возможности Django Ninja, Alpine.js и Axios в процессе создания веб-приложения для хранения заметок.

prilozhenie dlja hranenija zametok na django django ninja rest framework i alpinejs 18c615e - Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Рано или поздно любой начинающий Django-разработчик сталкивается с проектом, для которого нужно четкое разделение приложения на бэкенд и фронтенд: в этом случае серверную часть пишут на Django REST Framework (DRF) или FastAPI, а клиентскую – на React, Angular или Vue. Если речь идет о высоконагруженном сайте со множеством интерактивных элементов на стороне клиента – такой подход неизбежен. При этом значительную часть функциональности, которую Django предоставляет по умолчанию, придется реализовать на стороне фронтенда – и это будет гораздо сложнее.

Но если нагрузка на приложение будет умеренной, а идея разделения на фронтенд и бэкенд возникла из-за необходимости реализовать взаимодействие с базой данных без постоянных обновлений страниц – то такой проект целесообразнее воплотить в виде гибридного приложения. Гибридный подход позволяет:

  • максимально использовать «батарейки» Django – в том числе формы, систему аутентификации и авторизации;
  • реализовать асинхронную передачу данных и CRUD без перезагрузки страниц;
  • включить в шаблоны Джанго любые интерактивные JS-элементы.

Обзор проекта

prilozhenie dlja hranenija zametok na django django ninja rest framework i alpinejs 0ce21ca - Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Главная страница Notes – категории заметок

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

  • Фреймворк Django Ninja для API и CRUD.
  • Библиотеку Axios – для HTTP-запросов к бэкенду.
  • Ультралегкий JS-фреймворк Alpine.js и CSS-фреймворк Bootstrap – для фронтенда.

prilozhenie dlja hranenija zametok na django django ninja rest framework i alpinejs f5f6025 - Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Заметки в отдельной категории

Приложение использует один базовый и два гибридных шаблона Django/Alpine.js:

  • base.html – подключает библиотеку Axios, а также фреймворки Alpine.js и Bootstrap.
  • index.html – выводит все категории заметок. Карточки категорий добавляются и удаляются без перезагрузки страницы.
  • detail.html – отображает все заметки в определенной категории. Карточки заметок добавляются и удаляются без перезагрузки, таким же образом происходит обновление статуса В процессе на Сделано.

Весь код для проекта находится в репозитории.

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста» Интересно, перейти к каналу

Бэкенд и API

Для разработки API мы воспользуемся новым фреймворком Django Ninja. Это отличная альтернатива Django REST Framework и FastAPI, причем по синтаксису Django Ninja ближе к последнему. Django Ninja гораздо проще DRF и намного производительнее.

prilozhenie dlja hranenija zametok na django django ninja rest framework i alpinejs 78b2406 - Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Тест производительности Django Ninja

Единственный недостаток Django Ninja – пока что фреймворк не поддерживает представления на основе классов, и код получается довольно объемным (по сравнению с DRF), но в ближайшее время разработчики обещают решить эту задачу.

Начнем работу с создания нового проекта и приложения:

         python -m venv Notesvenv cd notes venvscriptsactivate pip install django pip install django-ninja django-admin startproject config . manage.py startapp notes      

Создадим базу данных и учетную запись админа:

         manage.py migrate manage.py createsuperuser      

Сделаем нужные настройки в файле config/settings.py:

         import os … INSTALLED_APPS = [ … 	'notes', ] … STATIC_URL = '/static/' STATIC_ROOT='/' STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)      

Создадим файл notes/urls.py с содержимым:

notes/urls.py

         from django.urls import path from . import views app_name = 'notes' urlpatterns = [ 	path('', views.home, name='home'),     path('category/<category_id>/', views.category_detail, name='detail'), ]      

Добавим нужные маршруты в config/urls.py:

config/urls.py

         from django.contrib import admin from django.urls import path, include from notes.api import api from django.conf import settings from django.conf.urls.static import static   urlpatterns = [ 	path('admin/', admin.site.urls), 	path("api/", api.urls), 	path('', include('notes.urls', namespace="notes")), ] urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)      

Создадим модели для заметок и категорий:

notes/models.py

         from django.db import models  class Category(models.Model):     title = models.CharField(max_length=100)     description = models.CharField(max_length=300)     created = models.DateTimeField(auto_now_add=True)         class Meta:     	verbose_name_plural = 'Категории'     	ordering = ['created']      def __str__(self):         return self.title  class Note(models.Model):     title = models.CharField(max_length=250)     category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='notes')     created = models.DateTimeField(auto_now_add=True)     completed = models.BooleanField(default=False, blank=True)          class Meta:     	verbose_name_plural = 'Заметки'     	ordering = ['-created']        def __str__(self):         return self.title             

Зарегистрируем модели в admin.py:

admin.py

         from django.contrib import admin from .models import Category, Note   admin.site.register(Category) admin.site.register(Note)      

Файл notes/views.py должен выглядеть так:

notes/views.py

         from django.shortcuts import render, get_object_or_404  from .models import Category, Note  def home(request):     return render(request, 'index.html', {         'categories': Category.objects.all()     })   def category_detail(request, category_id):     category = get_object_or_404(Category, id=category_id)     return render(request, 'detail.html', {         'category': category     })      

Теперь нужно подготовить и выполнить миграции:

         manage.py makemigrations manage.py migrate      

После выполнения миграций можно приступать к разработке API и CRUD-операций. Для этого нужно создать два файла – notes/schemas.py и notes/api.py. Сначала займемся схемами – они в Django Ninja выполняют те же самые функции, что и сериализатор в Django REST Framework, то есть определяют, какие именно данные поступают в базу и какие запрашиваются. Обратите внимание на разницу в наборах данных между схемами NoteIn, NoteOut, NoteUpd, CategoryIn, CategoryOut:

notes/schemas.py

         from ninja import Schema, ModelSchema from datetime import date from .models import Note   class CategoryIn(Schema):     title: str     description: str  class CategoryOut(Schema):     id: int     title: str     description: str         created: date     class NoteIn(ModelSchema):     class Config:         model = Note         model_fields = ['title', 'category']  class NoteUpd(ModelSchema):     class Config:         model = Note         model_fields = ['id', 'completed']  class NoteOut(ModelSchema):     class Config:         model = Note         model_fields = ['id','title', 'category', 'created', 'completed']      

Вся функциональность API описана в одном файле – notes/api.py:

notes/api.py

         from datetime import date from typing import List from ninja import NinjaAPI, Schema from django.shortcuts import get_object_or_404 from .models import Note, Category from .schemas import NoteIn, NoteOut, NoteUpd, CategoryIn, CategoryOut   api = NinjaAPI()   @api.post("/notes", tags=['Заметки']) def create_note(request, payload: NoteIn):     data = payload.dict()     category = Category.objects.get(id=data['category'])     del data['category']     note = Note.objects.create(category=category, **data)     return {"id": note.id}  @api.post("/category", tags=['Категории']) def create_category(request, payload: CategoryIn):     category = Category.objects.create(**payload.dict())     return {"id":category.id}       @api.get("/notes/{note_id}", response=NoteOut, tags=['Заметки']) def get_note(request, note_id: int):     note = get_object_or_404(Note, id=note_id)     return note  @api.get("/category/{category_id}", response=CategoryOut, tags=['Категории']) def get_category(request, category_id: int):     category = get_object_or_404(Category, id=category_id)     return category  @api.get("/category", response=List[CategoryOut], tags=['Категории']) def list_categories(request):     categories = Category.objects.all()     return categories     @api.get("/notes", response=List[NoteOut], tags=['Заметки']) def list_notes(request):     notes = Note.objects.all()     return notes   @api.patch("/notes/{note_id}", tags=['Заметки']) def update_note(request, note_id: int, payload: NoteUpd):     note = get_object_or_404(Note, id=note_id)     for attr, value in payload.dict().items():         setattr(note, attr, value)     note.save()     return {"success": True}  @api.put("/category/{category_id}", tags=['Категории']) def update_category(request, category_id: int, payload: CategoryIn):     note = get_object_or_404(Category, id=category_id)     for attr, value in payload.dict().items():         setattr(note, attr, value)     category.save()     return {"success": True}  @api.delete("/notes/{note_id}", tags=['Заметки']) def delete_note(request, note_id: int):     note = get_object_or_404(Note, id=note_id)     note.delete()     return {"success": True}  @api.delete("/category/{category_id}", tags=['Категории']) def delete_category(request, category_id: int):     category = get_object_or_404(Category, id=category_id)     category.delete()     return {"success": True}      

Теперь можно запустить сервер и протестировать работу API:

         manage.py runserver     

Перейдите по ссылке http://localhost:8000/api/docs – это адрес Django Ninja API:

prilozhenie dlja hranenija zametok na django django ninja rest framework i alpinejs 0d3bc37 - Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Веб-интерфейс Django Ninja API

Создавать новые категории и заметки можно прямо на этой странице. Выберите операцию POST в разделе Категории, нажмите кнопку Try it out, введите название и описание категории:

prilozhenie dlja hranenija zametok na django django ninja rest framework i alpinejs 3789a35 - Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Добавление в базу новой категории

Кликните на Execute – готово, первая категория добавлена в базу данных:

prilozhenie dlja hranenija zametok na django django ninja rest framework i alpinejs 59ab7f5 - Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Сервер сообщает об успешном добавлении категории

HTTP-запросы к бэкенду

За обработку запросов к бэкенду отвечает библиотека Axios, подключенная в шаблоне base.html. Axios – это альтернатива fetch с более дружественным синтаксисом. Код HTTP-запросов расположен в конце шаблонов index.html и detail.html. Создайте папку notes/templates и поместите туда все три шаблона. Кроме того, создайте папку static на одном уровне с notes и config, и сохраните в ней файл CSS-стилей.

Перейдите на главную страницу приложения http://localhost:8000/ и протестируйте работу API и Axios – теперь карточки категорий и заметки можно добавлять с фронтенда:

prilozhenie dlja hranenija zametok na django django ninja rest framework i alpinejs 8adc772 - Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Добавление карточек происходит без перезагрузки страницы

prilozhenie dlja hranenija zametok na django django ninja rest framework i alpinejs 53aba18 - Приложение для хранения заметок на Django, Django Ninja REST Framework и Alpine.js

Статус заметки изменяется одним кликом без перезагрузки

Фронтенд

Помимо API и Axios в добавлении элементов без перезагрузки страницы участвует фреймворк Alpine.js. Синтаксис Alpine.js очень похож на Vue.js – но, в отличие от Vue, Alpine не конфликтует с тегами Django и не требует заключения кода в теги {% verbatim %} {% endverbatim %}. По функциональности Alpine максимально близок к jQuery, поэтому фреймворк уже окрестили «современным jQuery».

Синтаксис Alpine.js во многом напоминает синтаксис стандартного шаблонизатора Django, и разобраться в нем (в отличие от ванильного JavaScript) не составит никакого труда. Еще одно огромное преимущество Alpine.js заключается в том, что его не нужно никуда устанавливать и запускать на отдельном локальном сервере (и, следовательно, не придется использовать CORS).

Более того, Alpine.js без проблем может получить данные от шаблонизатора Django. Обратите внимание на эти фрагменты в index.html, где в шаблоне мирно уживаются запрос Alpine и получение данных из бэкенда Django:

         <div x-data="getCategories()">    <h3 class="text-center mt-5" style="color:#777">все категории заметок пользователя <span class="fw-bold">{{ request.user.username }}</span></h3>     <form id="category-form">     	{% csrf_token %} 	</form> ... const getCategories = () => { 	return {     	newCategory: '',     	newDescription: '',     	categories: [         	{% for category in categories %}         	{ 'title': '{{ category.title }}', 'id': '{{ category.id }}', 'description': '{{ category.description }}' },         	{% endfor %}         ]     } };      

В конце шаблонов index.html и detail.html библиотека Axios обеспечивает обработку запросов к Django Ninja API. При создании новой категории Axios принимает от Alpine название и описание (title, description) и передает API запрос POST:

         const addCategory = async (title, description) => { 	try { 	const res = await axios.post('/api/category',     	{ title, description },     	{ headers: { 'X-CSRFToken': csrftoken }}     	); 	location.reload();   	} catch (e) {     	console.error(e);   	} };      

Для удаления категории Axios передает бэкенду соответствующий ID – categoryId:

         const removeCategory = async categoryId => { 	try { 	const res = await axios.delete('/api/category/' + categoryId,     	{ headers: { 'X-CSRFToken': csrftoken }}     	); 	location.reload();   	} catch (e) {     	console.error(e);   	} };      

Подведем итоги

Фреймворк Django Ninja и библиотека Alpine.js появились совсем недавно, но уже успели произвести фурор среди разработчиков: скорость, гибкость, простота синтаксиса и бесшовная интеграция делают их идеальным выбором для гибридных Django-проектов с умеренной нагрузкой. Приложение Notes, несмотря на простоту, позволяет быстро изучить все основные возможности Django Ninja и Alpine.js, разобраться в механизме взаимодействия API и Axios, и приступить к разработке более сложных проектов. Напоминаем, что весь код для Notes можно взять в репозитории.

***

Материалы по теме

  • 🐍🚀 Django с нуля. Часть 1: пишем многопользовательский блог для клуба любителей задач Python
  • 🐍🚀 Создаем рекрутинговый портал на Django: часть 1
  • 🔩 Полный фуллстек: пишем сайт на Django, Vue и GraphQL

  • 25 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close