如何在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
相关产品推荐
相关产品推荐

