Работа с UpdateView. Обновление записей пользователями
Редактирование статей
Пользователи умеют добавлять статьи. Теперь дадим им возможность исправлять свои материалы и будем запоминать, кто последним обновил статью — поле updater в модели уже есть.
Форма
Форма редактирования почти совпадает с формой добавления — унаследуем её и добавим поле «Закреплено»:
class ArticleUpdateForm(ArticleCreateForm):
class Meta(ArticleCreateForm.Meta):
fields = ArticleCreateForm.Meta.fields + ("fixed",)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["fixed"].widget.attrs.update({"class": "form-check-input"})
class Meta(ArticleCreateForm.Meta)— наследуемmodelиfields, чтобы не повторяться;- у чекбокса свой класс Bootstrap
form-check-input(а неform-control, который проставил родитель).
UpdateView
from django.views.generic import UpdateView
from .forms import ArticleUpdateForm
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
template_name = "blog/articles_update.html"
context_object_name = "article"
form_class = ArticleUpdateForm
login_url = "home"
def get_context_data(self, **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
return super().form_valid(form)
Как работает UpdateView:
get_object()находит статью поslug(илиpk) из URL.- GET — форма с
instance=self.object: поля заполнены текущими значениями. - POST — форма с данными и тем же
instance; при успехеform.save()обновляет запись, а не создаёт новую. - Редирект на
get_success_url()→ по умолчаниюget_absolute_url()статьи.
Маршрут
urlpatterns = [
path("", ArticleListView.as_view(), name="home"),
path("articles/create/", ArticleCreateView.as_view(), name="articles_create"),
path("articles/<str:slug>/update/", ArticleUpdateView.as_view(), name="articles_update"),
path("articles/<str:slug>/", ArticleDetailView.as_view(), name="articles_detail"),
path("category/<str:slug>/", ArticleByCategoryListView.as_view(), name="articles_by_category"),
]
Шаблон
templates/blog/articles_update.html — тот же, что и для создания, только с другой кнопкой. Чтобы не дублировать разметку, вынесите форму во фрагмент includes/article_form.html и подключайте его в обоих шаблонах:
{% extends "main.html" %}
{% block content %}
<h1 class="h4">{{ title }}</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% include "includes/article_form.html" %}
<button type="submit" class="btn btn-primary">Сохранить изменения</button>
</form>
{% endblock %}
На детальной странице — кнопка для автора:
{% if request.user == article.author %}
<a href="{% url 'articles_update' article.slug %}" class="btn btn-sm btn-outline-primary">Редактировать</a>
{% endif %}
И вывод того, кто обновил:
{% if article.updater %}
<small class="text-muted">Обновил: {{ article.updater.username }}, {{ article.time_update|date:"d.m.Y H:i" }}</small>
{% endif %}
Скрыть кнопку — не значит защитить
Пока любой вошедший пользователь может открыть /articles/чужая-статья/update/ вручную и изменить чужую статью. Кнопка лишь спрятана. Настоящую проверку прав сделаем в следующем уроке с помощью миксинов.
Итог
UpdateView=get_object()+ форма сinstance+save()поверх существующей записи.- Форму редактирования удобно наследовать от формы создания, расширяя
Meta.fields. updaterпроставляем вform_valid().- Права на редактирование нужно проверять во view, а не только в шаблоне.
Практика решено 0 из 5
Напишите ArticleUpdateForm, наследующую ArticleCreateForm. Её Meta должна наследовать
ArticleCreateForm.Meta и добавлять к полям родителя поле fixed.
class ArticleUpdateForm(ArticleCreateForm):
class Meta(ArticleCreateForm.Meta):
fields = ArticleCreateForm.Meta.fields + ("fixed",)
Напишите ArticleUpdateView(LoginRequiredMixin, UpdateView): модель Article, шаблон blog/articles_update.html,
context_object_name = "article", форма ArticleUpdateForm. В form_valid запишите в
form.instance.updater текущего пользователя и верните super().form_valid(form).
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
template_name = "blog/articles_update.html"
context_object_name = "article"
form_class = ArticleUpdateForm
def form_valid(self, form):
form.instance.updater = self.request.user
return super().form_valid(form)
Как UpdateView находит редактируемую статью по адресу articles/<str:slug>/update/?
В шаблоне детальной страницы покажите ссылку «Редактировать» только автору статьи:
{% if request.user == article.author %} и ссылка на {% url 'articles_update' article.slug %}.
{% if request.user == article.author %}
<a href="{% url 'articles_update' article.slug %}">Редактировать</a>
{% endif %}
Чем UpdateView отличается от CreateView?
Войдите, чтобы отмечать пройденные уроки.