Объединение DetailView и CreateView в Django 3.2/4.0, как объясняется в документации

Я создаю общий блог и пытаюсь дать пользователям возможность оставлять комментарии прямо на странице статьи. Я пытаюсь реализовать это, комбинируя DetailView с CreateView.


В документации представлено 3 различных решения этой проблемы:

  • FormMixin + DetailView: это ответ, который документация Django не советует , но который советует большинство ответов на SO, которые я смог найти
  • DetailView только + написать метод post: "лучшее решение" согласно Django docs
  • .
  • DetailView + FormView: "альтернативное лучшее решение", и то, которое я пытаюсь реализовать
  • .

"Альтернативное лучшее" решение заключается в создании DetailView для статей и FormView для комментариев, но в документации говорится, что "Этот подход также может быть использован с любыми другими общими представлениями на основе классов", что означает, что DetailView + CreateView должны быть возможны.

Я просмотрел ряд пунктов SO, которые ссылаются на это решение, но я не могу реализовать ни один из них.
  • В этом вопросе SO предлагается смешать DetailView и CreateView. Однако, объяснение в этом ответе неполное.
  • Другой вопрос SO, среди советов использовать FormMixins, имеет такой ответ, который близок, но отличается.
  • Другие вопросы (1, 2 и т.д.) касаются только методов FormMixin и DetailView + post.

Вот моя реализация на данный момент:

models.py:

class Article(models.Model):
    slug = models.SlugField()
    # title, body, author
    def get_absolute_url(self):
        return reverse("article_detail", kwargs={"slug": self.slug})

class Comment(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name="comments", to_field="slug")
    body = models.TextField()
    # ...
    def get_absolute_url(self):
        return reverse("article_detail", kwargs={"slug": self.article.slug})

views.py:

class ArticleDetailView(DetailView):
    model = Article
    template_name = "article_detail.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["form"] = CommentCreateView()
        return context

class CommentCreateView(CreateView):
    """create comment"""

    model = Comment
    fields = ["body"]
    template_name = "comment_create.html"

    def form_valid(self, form):
        self.object = form.save(commit=False)
        self.object.article = Article.objects.filter(
            slug=self.kwargs.get("slug")
        ).first()
        self.object.author = self.request.user
        self.object.save()
        return super().form_valid(form)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super().post(request, *args, **kwargs)

    def get_success_url(self):
        return reverse("article_detail", kwargs={"slug": self.object.article.slug})


class ArticleCommentView(View):
    def get(self, request, *args, **kwargs):
        view = ArticleDetailView.as_view()
        return view(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        view = CommentCreateView.as_view()
        return view(request, *args, **kwargs)

urls.py:

urlpatterns = [
    # ...
    path("article/<slug:slug>", ArticleDetailView.as_view(), name="article_detail"),
    path("comment/new", CommentCreateView.as_view(), name="comment_create"),
]

article_detail.html:

{% extends 'base.html' %}
{% block content %}
    {{ article.title }}
    {{ article.author }}
    {{ article.body }}
    <form method="post" action="{% url 'comment_create'%}">
        {% csrf_token %}
        <textarea name="{{ form.body.name }}">{{ form.body.value|default_if_none:'' }}</textarea>
        </div>
            <button type="submit">
                Post Comment
            </button>
        </div>
    </form>
    <!--  {% for comment in article.comments.all %} ...-->
{% endblock %}
Вернуться на верх