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

基于DRY原则复用Django多Model的CRUD实现方案咨询

Reduce Django CRUD Code Duplication for Multiple Models (Custom Theme-Friendly)

Hey there! I’ve been in exactly this situation before—copy-pasting CRUD code for every model gets tedious fast, especially when you need to stick to a custom theme without relying on Django Admin. Let’s break down actionable ways to eliminate repetition across all your components:

1. Reusable View Base Classes

Django’s class-based views (CBVs) are perfect for this. Create generic base CRUD views that encapsulate all your custom logic (like theme-specific template paths, context data, permission checks, or breadcrumbs), then have each model’s views inherit from these bases.

Example Base Views:

# views/base.py
from django.views.generic import (
    ListView, CreateView, UpdateView, DeleteView
)
from django.urls import reverse_lazy

class BaseListView(ListView):
    # Common list view config
    template_name = "crud/generic_list.html"
    paginate_by = 20

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Add custom theme context (e.g., active sidebar menu)
        context["active_menu"] = self.model._meta.verbose_name_plural.lower()
        return context

class BaseCreateView(CreateView):
    template_name = "crud/generic_form.html"

    def get_success_url(self):
        return reverse_lazy(f"{self.model._meta.model_name}-list")

# Repeat similar base classes for UpdateView/DeleteView

Then Use for Specific Models:

# views/book.py
from .base import BaseListView, BaseCreateView
from .models import Book
from .forms import BookForm

class BookListView(BaseListView):
    model = Book

class BookCreateView(BaseCreateView):
    model = Book
    form_class = BookForm

2. Generic Form Mixins or Factories

Avoid writing a full ModelForm for every model by creating a base mixin that adds common form behavior (like custom CSS classes, field exclusions, or validation).

Example Form Mixin:

# forms/base.py
from django.forms import ModelForm

class BaseModelFormMixin:
    # Common form attributes
    error_css_class = "is-invalid"
    required_css_class = "is-required"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Add custom classes to all fields
        for field in self.fields.values():
            field.widget.attrs["class"] = "form-control"

# Specific model form
class BookForm(BaseModelFormMixin, ModelForm):
    class Meta:
        model = Book
        fields = "__all__"  # Or specify fields as needed

For even less repetition, you could write a form factory function that dynamically generates ModelForms with your base config:

def create_base_model_form(model, fields="__all__"):
    class BaseModelForm(BaseModelFormMixin, ModelForm):
        class Meta:
            model = model
            fields = fields
    return BaseModelForm

# Usage:
BookForm = create_base_model_form(Book)

3. Dynamic URL Routing

Instead of writing separate URL patterns for each model, create a reusable function that generates CRUD routes for any model and its views.

Example URL Factory:

# urls/crud.py
from django.urls import path

def generate_crud_urls(model, view_classes):
    """
    view_classes should be a dict like:
    {"list": BookListView, "create": BookCreateView, ...}
    """
    model_name = model._meta.model_name
    urlpatterns = [
        path("", view_classes["list"].as_view(), name=f"{model_name}-list"),
        path("create/", view_classes["create"].as_view(), name=f"{model_name}-create"),
        path("<int:pk>/", view_classes["detail"].as_view(), name=f"{model_name}-detail"),
        path("<int:pk>/update/", view_classes["update"].as_view(), name=f"{model_name}-update"),
        path("<int:pk>/delete/", view_classes["delete"].as_view(), name=f"{model_name}-delete"),
    ]
    return urlpatterns

Use in Your Main URLs:

# urls.py
from django.urls import include, path
from .views.book import BookListView, BookCreateView, BookDetailView, BookUpdateView, BookDeleteView
from .models import Book
from .urls.crud import generate_crud_urls

urlpatterns = [
    # Other routes...
    path("books/", include(generate_crud_urls(Book, {
        "list": BookListView,
        "create": BookCreateView,
        "detail": BookDetailView,
        "update": BookUpdateView,
        "delete": BookDeleteView
    }))),
    # Repeat for other models
]

4. Reusable Template Components

Leverage Django’s template inheritance and include tags to create generic CRUD templates that work with any model, while letting you override specific parts for custom themes.

Example Generic List Template (crud/generic_list.html):

{% extends "base.html" %}
{% load crud_tags %}

{% block content %}
<div class="card">
    <div class="card-header">
        <h1>{{ model_verbose_name_plural|title }}</h1>
        <a href="{% url model_name|add:'-create' %}" class="btn btn-primary">Add New</a>
    </div>
    <div class="card-body">
        <table class="table table-striped">
            <thead>
                <tr>
                    <!-- Dynamically render table headers from model fields -->
                    {% for field in list_fields %}
                    <th>{{ field|title }}</th>
                    {% endfor %}
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody>
                {% for object in object_list %}
                <tr>
                    {% for field in list_fields %}
                    <td>{{ object|get_attr:field }}</td>
                    {% endfor %}
                    <td>
                        <a href="{% url model_name|add:'-detail' object.pk %}" class="btn btn-sm btn-info">View</a>
                        <a href="{% url model_name|add:'-update' object.pk %}" class="btn btn-sm btn-warning">Edit</a>
                        <a href="{% url model_name|add:'-delete' object.pk %}" class="btn btn-sm btn-danger">Delete</a>
                    </td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</div>
{% endblock %}

Tip: Add a Custom Template Filter for Field Access

To make dynamic field rendering work, create a filter to get an object’s attribute by name:

# templatetags/crud_tags.py
from django import template

register = template.Library()

@register.filter
def get_attr(obj, attr):
    return getattr(obj, attr)

Then in your base view, pass the list of fields to display:

class BaseListView(ListView):
    # ...
    list_fields = ["id", "name"]  # Default fields; override in child views

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["model_name"] = self.model._meta.model_name
        context["model_verbose_name_plural"] = self.model._meta.verbose_name_plural
        context["list_fields"] = self.list_fields
        return context

Bonus: Advanced Reusability

  • Mixins: Create mixins for cross-cutting concerns like LoginRequiredMixin, PermissionRequiredMixin, or a SearchFilterMixin to add search functionality to any list view.
  • Context Processors: Add global theme-related data (like sidebar menus) to all templates via a context processor, so you don’t have to repeat it in every view.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:15:51