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

如何在Django的CRUD中实现多级联动下拉菜单、表单布局与数据展示?

Django CRUD 实现带联动下拉菜单和数据表格

一、模型定义 (models.py)

首先定义数据库模型,包含所有需要的字段,并为下拉选项设置可选值:

from django.db import models

class DeviceRecord(models.Model):
    # 手机品牌选项
    PHONE_BRAND_CHOICES = [
        ('samsung', 'Samsung'),
        ('redmi', 'Redmi'),
    ]
    # 手机型号选项
    PHONE_MODEL_CHOICES = [
        ('m30', 'M30'),
        ('note10', 'Note10'),
    ]
    # 州选项
    STATE_CHOICES = [
        ('karnataka', 'Karnataka'),
        ('andhra', 'Andhra'),
    ]
    # 城市选项
    CITY_CHOICES = [
        ('bangalore', 'Bangalore'),
        ('tirupathi', 'Tirupathi'),
    ]

    phone_brand = models.CharField(max_length=20, choices=PHONE_BRAND_CHOICES, verbose_name='手机名称')
    phone_model = models.CharField(max_length=20, choices=PHONE_MODEL_CHOICES, verbose_name='手机型号')
    state = models.CharField(max_length=20, choices=STATE_CHOICES, verbose_name='州')
    city = models.CharField(max_length=20, choices=CITY_CHOICES, verbose_name='城市')
    datetime = models.DateTimeField(verbose_name='日期时间')
    company_name = models.CharField(max_length=100, verbose_name='公司名称')

    def __str__(self):
        return f"{self.company_name} - {self.get_phone_brand_display()} {self.get_phone_model_display()}"

二、表单创建 (forms.py)

使用ModelForm快速生成表单,并为日期时间字段设置合适的控件:

from django import forms
from .models import DeviceRecord

class DeviceRecordForm(forms.ModelForm):
    class Meta:
        model = DeviceRecord
        fields = '__all__'
        widgets = {
            'datetime': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
        }
    
    # 为所有字段添加统一样式类
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].widget.attrs.update({'class': 'form-control'})

三、视图实现 (views.py)

使用Django类视图完成CRUD操作:

from django.views.generic import CreateView, ListView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import DeviceRecord
from .forms import DeviceRecordForm

# 创建记录视图
class DeviceCreateView(CreateView):
    model = DeviceRecord
    form_class = DeviceRecordForm
    template_name = 'device_form.html'
    success_url = reverse_lazy('device-list')

# 记录列表视图
class DeviceListView(ListView):
    model = DeviceRecord
    template_name = 'device_list.html'
    context_object_name = 'records'
    paginate_by = 10  # 可选:添加分页功能

# 更新记录视图
class DeviceUpdateView(UpdateView):
    model = DeviceRecord
    form_class = DeviceRecordForm
    template_name = 'device_form.html'
    success_url = reverse_lazy('device-list')

# 删除记录视图
class DeviceDeleteView(DeleteView):
    model = DeviceRecord
    template_name = 'device_confirm_delete.html'
    success_url = reverse_lazy('device-list')

四、URL配置 (urls.py)

将视图映射到URL:

from django.urls import path
from .views import (
    DeviceCreateView,
    DeviceListView,
    DeviceUpdateView,
    DeviceDeleteView,
)

urlpatterns = [
    path('', DeviceListView.as_view(), name='device-list'),  # 列表页作为首页
    path('create/', DeviceCreateView.as_view(), name='device-create'),
    path('<int:pk>/update/', DeviceUpdateView.as_view(), name='device-update'),
    path('<int:pk>/delete/', DeviceDeleteView.as_view(), name='device-delete'),
]

五、模板文件

1. 表单页面 (device_form.html)

实现横向布局(col-6),并添加JavaScript处理联动下拉:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% if form.instance.pk %}编辑{% else %}新增{% endif %}设备记录</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-4">
        <h1>{% if form.instance.pk %}编辑{% else %}新增{% endif %}设备记录</h1>
        <form method="post" class="mt-3">
            {% csrf_token %}
            <!-- 第一行:手机名称、手机型号 -->
            <div class="row mb-3">
                <div class="col-6">
                    <label for="{{ form.phone_brand.id_for_label }}" class="form-label">{{ form.phone_brand.label }}</label>
                    {{ form.phone_brand }}
                </div>
                <div class="col-6">
                    <label for="{{ form.phone_model.id_for_label }}" class="form-label">{{ form.phone_model.label }}</label>
                    {{ form.phone_model }}
                </div>
            </div>
            <!-- 第二行:州、城市 -->
            <div class="row mb-3">
                <div class="col-6">
                    <label for="{{ form.state.id_for_label }}" class="form-label">{{ form.state.label }}</label>
                    {{ form.state }}
                </div>
                <div class="col-6">
                    <label for="{{ form.city.id_for_label }}" class="form-label">{{ form.city.label }}</label>
                    {{ form.city }}
                </div>
            </div>
            <!-- 第三行:日期时间、公司名称 -->
            <div class="row mb-3">
                <div class="col-6">
                    <label for="{{ form.datetime.id_for_label }}" class="form-label">{{ form.datetime.label }}</label>
                    {{ form.datetime }}
                </div>
                <div class="col-6">
                    <label for="{{ form.company_name.id_for_label }}" class="form-label">{{ form.company_name.label }}</label>
                    {{ form.company_name }}
                </div>
            </div>
            <button type="submit" class="btn btn-primary">保存</button>
            <a href="{% url 'device-list' %}" class="btn btn-secondary ms-2">取消</a>
        </form>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        // 手机品牌与型号联动逻辑
        const phoneBrandSelect = document.getElementById('id_phone_brand');
        const phoneModelSelect = document.getElementById('id_phone_model');
        const brandModelMap = {
            'samsung': ['m30'],
            'redmi': ['note10']
        };

        function updatePhoneModels() {
            const selectedBrand = phoneBrandSelect.value;
            const allowedModels = brandModelMap[selectedBrand] || [];
            
            phoneModelSelect.innerHTML = '';
            allowedModels.forEach(modelValue => {
                const option = document.createElement('option');
                option.value = modelValue;
                option.textContent = modelValue === 'm30' ? 'M30' : 'Note10';
                phoneModelSelect.appendChild(option);
            });
            
            if (phoneModelSelect.dataset.initial) {
                phoneModelSelect.value = phoneModelSelect.dataset.initial;
            }
        }

        // 州与城市联动逻辑
        const stateSelect = document.getElementById('id_state');
        const citySelect = document.getElementById('id_city');
        const stateCityMap = {
            'karnataka': ['bangalore'],
            'andhra': ['tirupathi']
        };

        function updateCities() {
            const selectedState = stateSelect.value;
            const allowedCities = stateCityMap[selectedState] || [];
            
            citySelect.innerHTML = '';
            allowedCities.forEach(cityValue => {
                const option = document.createElement('option');
                option.value = cityValue;
                option.textContent = cityValue === 'bangalore' ? 'Bangalore' : 'Tirupathi';
                citySelect.appendChild(option);
            });
            
            if (citySelect.dataset.initial) {
                citySelect.value = citySelect.dataset.initial;
            }
        }

        // 页面加载时初始化
        document.addEventListener('DOMContentLoaded', () => {
            {% if form.instance.pk %}
                phoneModelSelect.dataset.initial = '{{ form.instance.phone_model }}';
                citySelect.dataset.initial = '{{ form.instance.city }}';
            {% endif %}
            
            updatePhoneModels();
            updateCities();
            
            phoneBrandSelect.addEventListener('change', updatePhoneModels);
            stateSelect.addEventListener('change', updateCities);
        });
    </script>
</body>
</html>

2. 数据列表页面 (device_list.html)

展示数据库中的记录,以表格形式呈现:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>设备记录列表</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-4">
        <h1>设备记录列表</h1>
        <a href="{% url 'device-create' %}" class="btn btn-primary mb-3">新增记录</a>
        
        <table class="table table-striped table-hover">
            <thead class="table-dark">
                <tr>
                    <th>手机名称</th>
                    <th>手机型号</th>
                    <th>州</th>
                    <th>城市</th>
                    <th>公司名称</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for record in records %}
                <tr>
                    <td>{{ record.get_phone_brand_display }}</td>
                    <td>{{ record.get_phone_model_display }}</td>
                    <td>{{ record.get_state_display }}</td>
                    <td>{{ record.get_city_display }}</td>
                    <td>{{ record.company_name }}</td>
                    <td>
                        <a href="{% url 'device-update' record.pk %}" class="btn btn-sm btn-warning">编辑</a>
                        <a href="{% url 'device-delete' record.pk %}" class="btn btn-sm btn-danger">删除</a>
                    </td>
                </tr>
                {% empty %}
                <tr>
                    <td colspan="6" class="text-center text-muted">暂无记录</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>

        <!-- 分页控件(可选) -->
        {% if is_paginated %}
        <nav aria-label="Page navigation">
            <ul class="pagination">
                {% if page_obj.has_previous %}
                <li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">上一页</a></li>
                {% endif %}
                <li class="page-item active"><span class="page-link">{{ page_obj.number }}</span></li>
                {% if page_obj.has_next %}
                <li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">下一页</a></li>
                {% endif %}
            </ul>
        </nav>
        {% endif %}
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

3. 删除确认页面 (device_confirm_delete.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>删除记录</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-4">
        <h1>确认删除</h1>
        <p>确定要删除记录:<strong>{{ object.company_name }} - {{ object.get_phone_brand_display }} {{ object.get_phone_model_display }}</strong>吗?</p>
        
        <form method="post">
            {% csrf_token %}
            <button type="submit" class="btn btn-danger">确认删除</button>
            <a href="{% url 'device-list' %}" class="btn btn-secondary ms-2">取消</a>
        </form>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 15:15:30