Django веб-фреймворк: практическое руководство по созданию сайтов

Раздел: Django -> Веб-фреймворки

Основные этапы разработки веб-приложения на Django

Рассмотрим процесс создания простого блога на Django. Сначала выполняется установка фреймворка и создание проекта.

Наиболее распространенный подход включает следующие шаги:

  1. Установка Django и создание виртуального окружения.
  2. Создание проекта командой django-admin startproject mysite.
  3. Создание приложения python manage.py startapp blog.
  4. Определение модели Post в models.py.
  5. Регистрация приложения в settings.py и выполнение миграций.
  6. Создание представлений (views) и маршрутов (urls).
  7. Создание шаблонов для отображения.
  8. Запуск сервера разработки.

Пример модели Post:


from django.db import models
from django.utils import timezone

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    published_date = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.title
  

Django разработка веб приложений на python (разработка веб-приложений на django)

Далее в файле views.py создаются функции для отображения списка и детальной страницы:


from django.shortcuts import render, get_object_or_404
from .models import Post

def post_list(request):
    posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
    return render(request, 'blog/post_list.html', {'posts': posts})

def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/post_detail.html', {'post': post})
  

Fast api python (fastapi для python)

Маршруты в urls.py приложения:


from django.urls import path
from . import views

urlpatterns = [
    path('', views.post_list, name='post_list'),
    path('post/<int:pk>/', views.post_detail, name='post_detail'),
]
  

веб библиотека python (веб-библиотека python (фреймворк))

Шаблоны используют переменные из контекста. Пример post_list.html:


<ul>
{% for post in posts %}
  <li>
    <a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a>
    <p>{{ post.published_date }}</p>
  </li>
{% endfor %}
</ul>
  

Flask на языке python (flask на python)

Типичные ошибки:

  • Забыли добавить приложение в INSTALLED_APPS - получается ошибка django.core.exceptions.ImproperlyConfigured.
  • Не выполнили миграции - модель не создается в базе.
  • Ошибка в имени шаблона - TemplateDoesNotExist.
  • Неправильный импорт - ImportError при обращении к модели.

Как сделать код представлений более кратким и переиспользуемым?

Вместо функций можно использовать встроенные class-based views (CBV). Для списка - ListView, для деталей - DetailView.


from django.views.generic import ListView, DetailView
from .models import Post

class PostListView(ListView):
    model = Post
    template_name = 'blog/post_list.html'
    context_object_name = 'posts'
    queryset = Post.objects.filter(published_date__lte=timezone.now()).order_by('-published_date')

class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post_detail.html'
  

Python manage py runserver (запуск сервера разработки django через manage.py runserver)

Маршруты обновляются:


from django.urls import path
from .views import PostListView, PostDetailView

urlpatterns = [
    path('', PostListView.as_view(), name='post_list'),
    path('post/<int:pk>/', PostDetailView.as_view(), name='post_detail'),
]
  

Проблемы CBV: легко забыть указать template_name или context_object_name, что приведет к использованию значений по умолчанию. Также требуется импортировать timezone для фильтрации.

Как создать REST API для внешних клиентов?

Для построения API используется Django REST Framework. Установка: pip install djangorestframework. Затем создаются сериализаторы и viewsets.


# serializers.py
from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'title', 'content', 'published_date']
  

# views.py (rest)
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
  

Маршруты подключаются через DefaultRouter:


# urls.py (основной)
from rest_framework.routers import DefaultRouter
from blog.views import PostViewSet

router = DefaultRouter()
router.register(r'posts', PostViewSet)

urlpatterns += router.urls
  

После запуска сервера API доступно по адресам /posts/ (GET, POST) и /posts/{id}/ (GET, PUT, DELETE).

Типичная ошибка: отсутствие разрешений может привести к тому, что любой пользователь сможет изменять данные. Рекомендуется добавить permission_classes.

Как вынести часто используемые запросы из представлений?

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


from django.db import models
from django.utils import timezone

class PublishedManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(published_date__lte=timezone.now())

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    published_date = models.DateTimeField(default=timezone.now)
    
    objects = models.Manager()  # стандартный
    published = PublishedManager()  # кастомный
  

Теперь Post.published.all() возвращает только опубликованные посты. Это упрощает представления и повышает читаемость.

Проблема: если забыть вернуть стандартный менеджер objects, то все запросы через Post.objects будут использовать кастомный менеджер, что может нарушить функциональность.

Расширенный пример: поиск и пагинация в блоге

Реализуем функциональность поиска по заголовкам и содержанию постов, а также постраничную навигацию.

Модель

Используем ту же модель Post, что и выше.

Представление с пагинацией и поиском

Пример

from django.shortcuts import render
from django.core.paginator import Paginator
from django.db.models import Q
from .models import Post

def post_list(request):
    queryset = Post.published.all()
    query = request.GET.get('q')
    if query:
        queryset = queryset.filter(
            Q(title__icontains=query) | Q(content__icontains=query)
        )
    paginator = Paginator(queryset, 5)  # 5 постов на странице
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    return render(request, 'blog/post_list.html', {'page_obj': page_obj, 'query': query})

Шаблон с формой поиска и пагинацией

Пример

<form method='get' action='.'>
  <input type='text' name='q' value='{{ query }}' placeholder='Поиск...'>
  <button type='submit'>Найти</button>
</form>

<ul>
{% for post in page_obj %}
  <li>
    <a href='{% url 'post_detail' pk=post.pk %}'>{{ post.title }}</a>
    <p>{{ post.published_date|date:'d.m.Y' }}</p>
  </li>
{% endfor %}
</ul>

<div class='pagination'>
  {% if page_obj.has_previous %}
    <a href='?page={{ page_obj.previous_page_number }}&q={{ query }}'>Предыдущая</a>
  {% endif %}
  <span>Страница {{ page_obj.number }} из {{ page_obj.paginator.num_pages }}</span>
  {% if page_obj.has_next %}
    <a href='?page={{ page_obj.next_page_number }}&q={{ query }}'>Следующая</a>
  {% endif %}
</div>

Результат

При переходе на страницу отображаются первые 5 постов. При вводе поискового запроса в строку и нажатии 'Найти' список фильтруется. Пагинация сохраняет переданный запрос через GET-параметр q. Если постов больше 5, появляются ссылки на предыдущую и следующую страницы.

Пример использования: посетитель ищет 'Python' - выводятся посты, содержащие это слово в заголовке или тексте. При этом номер страницы в URL меняется, а поисковый запрос остается.

Разработка веб-приложений на Django - comments

En
Django разработка веб приложений на python (python)