Creating a View with an update and delete button in Django

I am creating a question-and-answer app, I have some views that I want to combine into a single view. I want to combine the QuestionDetailView, DeleteQuestionView and AnswerFormView into AnswerView.

The problem is that anytime I click on submit to submit an answer to a question, it deletes the question instead.

I have read that you can’t have two post requests in a single view, please I would appreciate it if anyone could give me a help on this.
Here is my views and detail_view.html template.

views.py

class QuestionDetailView(DetailView):
    model = Question
    context_object_name = "question"
    template_name = "qa/question_detail.html"

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

class QuestionDeleteView(PermissionRequiredMixin, DeleteView):
    permission_required = "qa.can_answer"

    model = Question
    template_name = "qa/question_detail.html"
    success_url = reverse_lazy("unanswered_questions")


class AnswerFormView(PermissionRequiredMixin, SingleObjectMixin, FormView):
    permission_required = "qa.can_answer"
    model = Question
    form_class = AnswerForm
    template_name = "qa/question_detail.html"

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.get_form()
        if form.is_valid():
            answer = form.save(commit=False)
            answer.question = self.object
            answer.save()
            return super().form_valid(form)

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


class AnswerView(View):
    template_name = "qa/question_detail.html"

    def get(self, request, *args, **kwargs):
        view = QuestionDetailView.as_view()
        return view(request, *args, **kwargs)

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

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

detail_view.html

{% extends "qa/base_generic.html" %} {% block content %}
<div class="container">
  <h1 class="display-4">{{ question.title }}</h1>
  <p class="lead">{{ question.content }}</p>

  {% if question.answer %}
  <h2 class="mt-4">Answer</h2>
  <div class="card my-2">
    <div class="card-body">
      <p class="card-text">{{ answer.content }}</p>
      <p class="card-text">
        <small class="text-muted"
          >Posted on: {{ answer.created_at|date:"F d, Y H:i" }}</small
        >
      </p>
      <p class="card-text">
        <small class="text-muted"
          >Last Updated: {{ answer.updated_at|date:"F d, Y H:i" }}</small
        >
      </p>
    </div>
  </div>
  {% else %}
  <p class="mt-4">No Answer Available</p>
  {% if perms.qa.can_answer %}
  <form method="post" class="my-4">
    {% csrf_token %} {{ form.as_p }}
    <button class="btn btn-primary" type="submit">Submit Answer</button>
  </form>

  <form method="post">
    {% csrf_token %}
    <button type="submit" class="btn btn-danger">Delete</button>
  </form>
  {% endif %} {% endif %}
</div>
{% endblock %}

Hi adejumoridwan,

I guess you can try setting your delete form method as “delete”

<form method="delete">
    {% csrf_token %}
    <button type="submit" class="btn btn-danger">Delete</button>
  </form>

and rename the second “post” view method to “delete”

def delete(self, request, *args, **kwargs):
        view = QuestionDeleteView.as_view()
        return view(request, *args, **kwargs)

I would not encourage you to use a single view for handling all actions (using 3 different views is cleaner), but if this is really what what you want, …

First, when defining post method twice on the same class, the second definition overrides the first one, so this is roughly equivalent to only having the second declaration of post method ; hence the deletion behaviour observed whatever button you click to submit form.

If you want to handle the different actions in the same post function, you have to distinguish the button used for validating form. For that, you must specify a name and value attributes to your submit buttons. E.g.

<button name="action" value="answer" ...>

<button name="action" value="delete" ...>

Then, your post method could be something like

    def post(self, request, *args, **kwargs):
            if request.POST.get("action") == "answer":
                view = AnswerFormView.as_view()
                return view(request, *args, **kwargs)
            elif request.POST.get("action") == "delete":
                view = QuestionDeleteView.as_view()
                return view(request, *args, **kwargs)
            else:
                Some error handling

I tried the above solutions but none of them worked.
This is the solution i came up with.

Add an action button to the delete button in question_detail.html

  <form
    method="post"
    action="{% url 'delete_question' question.question_id question.slug %}"
  >
    {% csrf_token %}
    <button type="submit" class="btn btn-danger">Delete</button>
  </form>

All views remain the same, but i updated the AnswerView keeping only the form submission post for answering a question

class AnswerView(View):
    template_name = "qa/question_detail.html"

    def get(self, request, *args, **kwargs):
        view = QuestionDetailView.as_view()
        return view(request, *args, **kwargs)

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

And i added the following into my app URLs

    path(
        "delete_question/<int:question_id>/<slug:slug>/",
        QuestionDeleteView.as_view(),
        name="delete_question",
    ),

All is working fine, thanks everyone for your help

@antoinehumbert can you please explain what you mean by not using a single view to handle all actions…
Thanks.

Having a single view handling all actions was what you proposed in your question, with the AnswerView.

What you finally implemented, with a dedicated view for the deletion handling is the correct way of doing things