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套通用模板,所有模型共用:
templates/generic/crud_list.html:模型列表页,遍历上下文里的对象列表,拼接新增、编辑、删除按钮的跳转地址即可templates/generic/crud_form.html:新增/编辑表单页,直接渲染form对象即可,支持自定义样式templates/generic/crud_confirm_delete.html:删除确认页,显示待删除对象信息和确认按钮
自定义扩展说明
如果需要实现类似Admin的权限控制、搜索、过滤、排序等功能,只需要在CRUDRegistry里增加对应的配置项,注册模型时传入参数即可,逻辑和Admin的ModelAdmin配置完全一致。
内容的提问来源于stack exchange,提问作者treecon
相关产品推荐
相关产品推荐

