Django База [2023]: Права доступа с помощью миксинов в представлениях #19
В этой статье мы рассмотрим использование миксинов в работе с представлениями Django. Мы реализуем возможность редактировать статью только для авторов статьи и админов сайта.
Если вы хотите выразить благодарность автору сайта, статей и курса по Django, вы можете сделать это по ссылке ниже:
Стандартные миксины Django
Для безопасности проекта мы можем использовать встроенные миксины, которые запрещают неавторизованным пользователям добавлять материал. На этот случай в Django существует миксин LoginRequiredMixin, который дает возможность добавлять материалы только после авторизации пользователя на сайте.
В нашем проекте в одном из уроков мы реализовали добавление статьи с помощью представления CreateView. Давайте наше представление дополним, добавив в него миксин LoginRequiredMixin.
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.
Рассмотрим миксин уведомления для представления обновления материала. Для этого мы будем использовать встроенный миксин SuccessMessageMixin при отправке формы.
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 со следующим фрагментом кода:
{% 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, поэтому обновляем содержимое:
<!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>
Отлично, теперь давайте обновим одну из наших статей и посмотрим как работает миксин уведомления.
Отлично, все работает.
Пример кастомного миксина
Теперь давайте в нашей папке 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 и добавили возможность редактирования статьи только автору. Давайте воспользуемся нашим миксином в представлении удаления, и обновления:
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