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

Django基于模型实现DRY模式自动CRUD功能的最优方案是什么?

实现方案

你遗漏的Django内置特性

Django的通用编辑视图(CreateView/UpdateView)本身支持直接通过fields参数自动生成对应的ModelForm,完全不需要单独为每个模型定义Form类,你可以直接把原来的视图简化为:

from django.views.generic import CreateView
from .models import Author

class AuthorCreateView(CreateView):
    model = Author
    fields = "__all__"
    success_url = "/author/list/" # 也可以用reverse_lazy动态生成

这一步就能直接省掉所有重复的Form类代码。

完整DRY模式实现步骤

要做到类似Admin仅注册模型就能用全套CRUD的效果,你可以自己实现一个轻量的模型注册器,动态生成所有视图和路由,不需要为每个模型单独写视图代码:

步骤1:实现CRUD注册器

新建crud_registry.py文件,内容如下:

from django.urls import path, reverse_lazy
from django.views.generic import ListView, CreateView, UpdateView, DeleteView

class CRUDRegistry:
    def __init__(self):
        self._registry = {}
    
    def register(self, model, crud_options=None):
        # 支持自定义字段、模板、额外参数等配置
        self._registry[model] = crud_options or {}
    
    def get_urls(self):
        urls = []
        for model, options in self._registry.items():
            model_name = model._meta.model_name
            app_label = model._meta.app_label
            url_prefix = f"{app_label}/{model_name}/"
            # 路由命名规则:{app}_{模型}_{操作}
            list_view_name = f"{app_label}_{model_name}_list"

            # 1. 列表视图
            list_view = type(
                f"{model.__name__}ListView",
                (ListView,),
                {
                    "model": model,
                    "context_object_name": f"{model_name}_list",
                    "template_name": options.get("list_template", "generic/crud_list.html"),
                    **options.get("list_kwargs", {})
                }
            )
            urls.append(path(url_prefix, list_view.as_view(), name=list_view_name))

            # 2. 新增视图
            create_view = type(
                f"{model.__name__}CreateView",
                (CreateView,),
                {
                    "model": model,
                    "fields": options.get("fields", "__all__"),
                    "success_url": reverse_lazy(list_view_name),
                    "template_name": options.get("form_template", "generic/crud_form.html"),
                    **options.get("create_kwargs", {})
                }
            )
            urls.append(path(f"{url_prefix}create/", create_view.as_view(), name=f"{app_label}_{model_name}_create"))

            # 3. 编辑视图
            update_view = type(
                f"{model.__name__}UpdateView",
                (UpdateView,),
                {
                    "model": model,
                    "fields": options.get("fields", "__all__"),
                    "success_url": reverse_lazy(list_view_name),
                    "template_name": options.get("form_template", "generic/crud_form.html"),
                    **options.get("update_kwargs", {})
                }
            )
            urls.append(path(f"{url_prefix}<int:pk>/edit/", update_view.as_view(), name=f"{app_label}_{model_name}_update"))

            # 4. 删除视图
            delete_view = type(
                f"{model.__name__}DeleteView",
                (DeleteView,),
                {
                    "model": model,
                    "success_url": reverse_lazy(list_view_name),
                    "template_name": options.get("delete_template", "generic/crud_confirm_delete.html"),
                    **options.get("delete_kwargs", {})
                }
            )
            urls.append(path(f"{url_prefix}<int:pk>/delete/", delete_view.as_view(), name=f"{app_label}_{model_name}_delete"))
        return urls

    @property
    def urls(self):
        return self.get_urls(), "crud", "crud"

# 全局注册器实例
crud_site = CRUDRegistry()

步骤2:注册模型

和Admin的注册逻辑完全一致,在你应用的任意文件(比如crud.py)里注册需要生成CRUD的模型:

from .models import Author, Book
from .crud_registry import crud_site

# 用默认配置注册
crud_site.register(Author)
# 也可以自定义配置,比如指定可编辑字段
crud_site.register(Book, crud_options={"fields": ["title", "author"]})

步骤3:配置路由

在项目根目录的urls.py中引入注册器的路由:

from django.contrib import admin
from django.urls import path, include
from your_app_name.crud_registry import crud_site

urlpatterns = [
    path('admin/', admin.site.urls),
    # 所有自定义CRUD路由都在/crud/前缀下
    path('crud/', include(crud_site.urls)),
]

步骤4:编写通用模板

只需要写3套通用模板,所有模型共用:

  1. templates/generic/crud_list.html:模型列表页,遍历上下文里的对象列表,拼接新增、编辑、删除按钮的跳转地址即可
  2. templates/generic/crud_form.html:新增/编辑表单页,直接渲染form对象即可,支持自定义样式
  3. templates/generic/crud_confirm_delete.html:删除确认页,显示待删除对象信息和确认按钮

自定义扩展说明

如果需要实现类似Admin的权限控制、搜索、过滤、排序等功能,只需要在CRUDRegistry里增加对应的配置项,注册模型时传入参数即可,逻辑和Admin的ModelAdmin配置完全一致。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 15:15:06