Django База [2023]: Права доступа с помощью миксинов в представлениях #19

Django Django База [2023]: Права доступа с помощью миксинов в представлениях #19


В этой статье мы рассмотрим использование миксинов в работе с представлениями Django. Мы реализуем возможность редактировать статью только для авторов статьи и админов сайта.

Стандартные миксины Django

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

В нашем проекте в одном из уроков мы реализовали добавление статьи с помощью представления CreateView. Давайте наше представление дополним, добавив в него миксин LoginRequiredMixin.

blog/views.py
from django.views.generic import CreateView
from django.contrib.auth.mixins import LoginRequiredMixin

from .models import Article, Category


class ArticleCreateView(LoginRequiredMixin, CreateView):
    """
    Представление: создание материалов на сайте
    """
    model = Article
    template_name = 'blog/articles_create.html'
    form_class = ArticleCreateForm
    login_url = 'home'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Добавление статьи на сайт'
        return context

    def form_valid(self, form):
        form.instance.author = self.request.user
        form.save()
        return super().form_valid(form)

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

Давайте проверим в деле, как это работает. Я деавторизуюсь на сайте и попробую добавить статью, перейдя по следующему адресу: http://127.0.0.1:8000/articles/create/. Автоматически, меня перебрасывает на страницу всех статей со статусом HTTP 302.

Django База [2023]: Права доступа с помощью миксинов в представлениях #19
Нельзя добавить статью без авторизации

Рассмотрим миксин уведомления для представления обновления материала. Для этого мы будем использовать встроенный миксин SuccessMessageMixin при отправке формы.

blog/views.py
from django.views.generic import UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin

from .models import Article


class ArticleUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
    """
    Представление: обновления материала на сайте
    """
    model = Article
    template_name = 'blog/articles_update.html'
    context_object_name = 'article'
    form_class = ArticleUpdateForm
    login_url = 'home'
    success_message = 'Материал был успешно обновлен'

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = f'Обновление статьи: {self.object.title}'
        return context
    
    def form_valid(self, form):
        # form.instance.updater = self.request.user
        form.save()
        return super().form_valid(form)

В примере выше мы использовали два миксина, один для неавторизованных пользователей, и один для уведомления при успешном обновлении материала, также этот миксин можно добавить и к представления добавления материала.

Нам необходимо настроить шаблон уведомления, для этого в папке templates я создам папку includes, где размещу HTML файл messages.html со следующим фрагментом кода:

templates/includes/messages.html
{% if messages %}
{% for message in messages %}
    <div class="alert alert-{% if message.tags %}{{ message.tags }}{% endif %} alert-dismissible fade show" role="alert">
        <i class="fas fa-info-circle"></i> {{message}}
        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
    </div>
{% endfor %}
{% endif %}

Шаблон мы используем из Bootstrap, а сами теги из документации по django.contrib.messages

Далее нам необходимо подлкючить компонент messages в main.html, поэтому обновляем содержимое:

templates/main.html
<!DOCTYPE html>
<html lang="ru">
<head>
    {% load static %}
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- INCLUDE CSS -->
    <link href="{% static 'bootstrap/css/bootstrap.min.css' %}" type="text/css" rel="stylesheet">
</head>
<body>
    <div class="container">
        {% include 'header.html' %}
        <div class="row">
            <div class="col-8">
            {% include 'includes/messages.html' %}
            {% block content %}

            {% endblock %}
            {% include 'pagination.html' %}
            </div>
            <div class="col-4">
                {% include 'sidebar.html' %}
            </div>
        </div>
    </div>
<script src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script>
</body>
</html>

Отлично, теперь давайте обновим одну из наших статей и посмотрим как работает миксин уведомления.

Django База [2023]: Права доступа с помощью миксинов в представлениях #19
После обновления статьи получаем успешное сообщение об обновлении

Отлично, все работает.

Пример кастомного миксина

Теперь давайте в нашей папке services, которую мы создали в уроке по обработке кириллицы создадим файл mixins.py, и добавим следующий код:

services/mixins.py
from django.contrib.auth.mixins import AccessMixin
from django.contrib import messages
from django.shortcuts import redirect


class AuthorRequiredMixin(AccessMixin):

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return self.handle_no_permission()
        if request.user.is_authenticated:
            if request.user != self.get_object().author or request.user.is_staff:
                messages.info(request, 'Изменение и удаление статьи доступно только автору')
                return redirect('home')
        return super().dispatch(request, *args, **kwargs)

В примере выше мы создали миксин наследуясь от основного AccessMixin и добавили возможность редактирования статьи только автору. Давайте воспользуемся нашим миксином в представлении удаления, и обновления:

blog/views.py
from django.views.generic import UpdateView, DeleteView
from django.contrib.messages.views import SuccessMessageMixin

from .models import Article
from ..services.mixins import AuthorRequiredMixin


class ArticleUpdateView(AuthorRequiredMixin, SuccessMessageMixin, UpdateView):
    """
    Представление: обновления материала на сайте
    """
    model = Article
    template_name = 'blog/articles_update.html'
    context_object_name = 'article'
    form_class = ArticleUpdateForm
    login_url = 'home'
    success_message = 'Материал был успешно обновлен'

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = f'Обновление статьи: {self.object.title}'
        return context
    
    def form_valid(self, form):
        # form.instance.updater = self.request.user
        form.save()
        return super().form_valid(form)


class ArticleDeleteView(AuthorRequiredMixin, DeleteView):
    """
    Представление: удаления материала
    """
    model = Article
    success_url = reverse_lazy('home')
    context_object_name = 'article'
    template_name = 'blog/articles_delete.html'

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = f'Удаление статьи: {self.object.title}'
        return context

В этом коде я убрал LoginRequiredMixin, так как данная логика уже добавлена в кастомный миксин AuthorRequiredMixin. Осталось проверить на сайте, для этого я зайду как с гостя, так и с другого аккаунта:

Редактирование с гостя:

Django База [2023]: Права доступа с помощью миксинов в представлениях #19
Недоступно

Редактирование с другого пользователя:

Django База [2023]: Права доступа с помощью миксинов в представлениях #19
Недоступно

Редактирование с автора статьи:

Django База [2023]: Права доступа с помощью миксинов в представлениях #19
Доступно

Отлично, миксин работает. Вы можете добавлять, изменять миксины так, как вам хочется.

Более подробная информация о миксинах есть на официальном сайте Django