Работа с UpdateView. Обновление записей пользователями

⏱ 25 мин · 🧩 заданий: 5

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

Пользователи умеют добавлять статьи. Теперь дадим им возможность исправлять свои материалы и будем запоминать, кто последним обновил статью — поле 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:

  1. get_object() находит статью по slug (или pk) из URL.
  2. GET — форма с instance=self.object: поля заполнены текущими значениями.
  3. POST — форма с данными и тем же instance; при успехе form.save() обновляет запись, а не создаёт новую.
  4. Редирект на 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

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Написать код

Напишите ArticleUpdateForm, наследующую ArticleCreateForm. Её Meta должна наследовать ArticleCreateForm.Meta и добавлять к полям родителя поле fixed.

Задание 2 Написать код

Напишите ArticleUpdateView(LoginRequiredMixin, UpdateView): модель Article, шаблон blog/articles_update.html, context_object_name = "article", форма ArticleUpdateForm. В form_valid запишите в form.instance.updater текущего пользователя и верните super().form_valid(form).

Задание 3 Один вариант ответа

Как UpdateView находит редактируемую статью по адресу articles/<str:slug>/update/?

Задание 4 Написать код

В шаблоне детальной страницы покажите ссылку «Редактировать» только автору статьи: {% if request.user == article.author %} и ссылка на {% url 'articles_update' article.slug %}.

Задание 5 Несколько вариантов

Чем UpdateView отличается от CreateView?

Войдите, чтобы отмечать пройденные уроки.