You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中如何用类视图实现点击按钮切换BooleanField值

如何用Django类视图实现Task状态一键切换

我正在做一个TodoList练习项目,models.py代码如下:

class Tag(models.Model):
    name = models.CharField(max_length=60)

    def __str__(self):
        return self.name


class Task(models.Model):
    TASK_STATUS_CHOICES = [(False, 'Not done'), (True, 'Done')]
    content = models.CharField(max_length=250)
    created = models.DateTimeField(auto_now_add=True)
    deadline = models.DateTimeField(
        default=timezone.now,
        null=True,
        blank=True,
        help_text="Enter date and time in '2023-05-13 06:31' format"
    )
    is_completed = models.BooleanField(
        default=False,
        choices=TASK_STATUS_CHOICES
    )
    tags = models.ManyToManyField(Tag, related_name="tasks")

    class Meta:
        ordering = ["is_completed", "-created"]

    def __str__(self):
        return self.content

此前我用函数视图实现了点击按钮切换is_completed字段值的功能,代码可正常运行:

原函数视图实现

views.py

def task_complete_or_undo(request, pk):
    task = Task.objects.get(id=pk)
    task.is_completed = not task.is_completed
    task.save()
    return redirect("/")

urls.py

path(
        "task/<int:pk>/task-status",
        task_complete_or_undo,
        name="task-status"
    ),

index.html

{% if task.is_completed %}
        <span style="color: green">{{ task.get_is_completed_display }}</span>
        <a href="{% url 'todo_list_app:task-status' pk=task.id %}" class="btn btn-secondary btn-sm link-to-page float-right">
          Undo
        </a>
      {% else %}
        <span style="color: red">{{ task.get_is_completed_display }}</span>
        <a href="{% url 'todo_list_app:task-status' pk=task.id %}" class="btn btn-success btn-sm link-to-page float-right">
          Complete
        </a>
      {% endif %}

现在需要改用类视图重写,但尝试两种方式都遇到问题:

  1. 自定义View类时,仅实现post方法,点击按钮返回“Method Not Allowed”错误(此外代码里还写错了字段名,把is_completed写成了is_done):
class TaskStatusUpdateView(View):

    @staticmethod
    def post(request, pk):
        task = Task.objects.get(id=pk)
        task.is_done = not task.is_done
        task.save()
        return redirect("todo_list_app:index")
  1. 改用UpdateView时,点击按钮会跳转到更新表单,需要手动提交,不符合一键切换的需求(同样存在字段名错误):
class TaskStatusUpdateView(generic.UpdateView):
    model = Task
    fields = ["is_completed"]

    @staticmethod
    def update(request, pk):
        task = Task.objects.get(id=pk)
        task.is_done = not task.is_done
        task.save()
        return redirect("todo_list_app:index")

解决方案

方案一:自定义View类(支持GET/POST请求)

方式1:兼容原GET请求(快速适配)

修正字段名错误,实现get方法匹配前端a标签的GET请求:

from django.views import View
from django.shortcuts import get_object_or_404, redirect
from .models import Task

class TaskStatusUpdateView(View):
    def get(self, request, pk):
        # 用get_object_or_404替代get,避免任务不存在时抛出异常
        task = get_object_or_404(Task, id=pk)
        task.is_completed = not task.is_completed
        task.save()
        return redirect("todo_list_app:index")

路由配置只需把视图换成类视图的as_view()方法:

path(
    "task/<int:pk>/task-status",
    TaskStatusUpdateView.as_view(),
    name="task-status"
),

前端代码无需修改,沿用原a标签即可。

方式2:遵循HTTP规范用POST请求(推荐)

根据HTTP规范,修改数据的操作应使用POST请求,避免误触或爬虫触发。调整前端为表单提交:

{% if task.is_completed %}
    <span style="color: green">{{ task.get_is_completed_display }}</span>
    <form action="{% url 'todo_list_app:task-status' pk=task.id %}" method="POST" class="float-right">
        {% csrf_token %}
        <button type="submit" class="btn btn-secondary btn-sm">Undo</button>
    </form>
{% else %}
    <span style="color: red">{{ task.get_is_completed_display }}</span>
    <form action="{% url 'todo_list_app:task-status' pk=task.id %}" method="POST" class="float-right">
        {% csrf_token %}
        <button type="submit" class="btn btn-success btn-sm">Complete</button>
    </form>
{% endif %}

对应的视图类改为处理POST请求:

class TaskStatusUpdateView(View):
    def post(self, request, pk):
        task = get_object_or_404(Task, id=pk)
        task.is_completed = not task.is_completed
        task.save()
        return redirect("todo_list_app:index")

方案二:用UpdateView实现

UpdateView默认会渲染表单,要实现一键切换,需重写方法跳过表单渲染逻辑:

from django.views.generic.edit import UpdateView
from .models import Task
from django.urls import reverse_lazy

class TaskStatusUpdateView(UpdateView):
    model = Task
    fields = ['is_completed']
    success_url = reverse_lazy("todo_list_app:index")

    # 处理POST请求(配合前端表单)
    def form_valid(self, form):
        # 直接切换状态,忽略表单提交的值
        self.object.is_completed = not self.object.is_completed
        self.object.save()
        return super().form_valid(form)

    # 若要支持GET请求(配合原a标签),重写get方法
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.object.is_completed = not self.object.is_completed
        self.object.save()
        return redirect(self.success_url)

路由配置同样使用as_view()方法即可。


内容的提问来源于stack exchange,提问作者Maksym Maksimov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 02:52:04