Django实现带新增选项的交互式下拉列表
解决方案
第一步:调整模型结构
首先需要将route字段从Activity模型中拆分出来,单独创建Route模型,这样才能实现路线的复用和下拉选择功能。修改后的模型代码如下:
from django.db import models from django.contrib.auth.models import User class Person(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) fName = models.CharField(max_length=100) lName = models.CharField(max_length=100) weight = models.FloatField() sex = models.CharField(max_length=10) def __str__(self): return self.lName # 新增Route模型,存储所有路线 class Route(models.Model): name = models.CharField(max_length=100, unique=True) # 全局唯一路线名,若要用户专属可添加person外键 def __str__(self): return self.name class Activity(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) length = models.FloatField() runTime = models.DurationField() route = models.ForeignKey(Route, on_delete=models.CASCADE) # 改为外键关联Route ave = models.DurationField() dateRun = models.DateField() marathon = models.DurationField() def __str__(self): return f"{self.route.name} - {self.dateRun}"
执行数据库迁移命令,更新数据库结构:
python manage.py makemigrations python manage.py migrate
第二步:自定义表单实现“下拉选择+新路线输入”
创建ActivityForm,添加额外的new_route输入框,在表单验证逻辑中处理已有路线选择或新路线创建:
from django import forms from .models import Activity, Route class ActivityForm(forms.ModelForm): # 新增输入框用于录入新路线 new_route = forms.CharField(max_length=100, required=False, label="新路线名称") class Meta: model = Activity fields = ['length', 'runTime', 'route', 'dateRun'] widgets = { 'dateRun': forms.DateInput(attrs={'type': 'date'}), 'runTime': forms.TimeInput(attrs={'type': 'time'}), } def __init__(self, *args, **kwargs): self.person = kwargs.pop('person') # 从视图传入当前用户的Person实例 super().__init__(*args, **kwargs) # 设置路线下拉选项为所有已有路线,若要用户专属路线可改为self.person.routes.all() self.fields['route'].queryset = Route.objects.all() self.fields['route'].empty_label = "选择已有路线或输入新路线" def clean(self): cleaned_data = super().clean() selected_route = cleaned_data.get('route') new_route_name = cleaned_data.get('new_route') # 验证逻辑:必须选择已有路线或输入新路线 if not selected_route and not new_route_name: raise forms.ValidationError("请选择已有路线或输入新路线名称") # 若输入新路线,自动创建Route实例(避免重复) if new_route_name: route, _ = Route.objects.get_or_create(name=new_route_name.strip()) cleaned_data['route'] = route return cleaned_data
第三步:修改视图处理表单提交
在视图中传入当前用户的Person实例,处理表单验证和活动保存,同时自动计算ave和marathon字段:
from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required from .models import Person, Activity from .forms import ActivityForm import datetime @login_required def add_activity(request): person = Person.objects.get(user=request.user) if request.method == 'POST': form = ActivityForm(request.POST, person=person) if form.is_valid(): activity = form.save(commit=False) activity.person = person # 计算平均配速(ave)和全马预计时间(marathon) total_seconds = activity.runTime.total_seconds() if activity.length > 0: ave_seconds = total_seconds / activity.length activity.ave = datetime.timedelta(seconds=ave_seconds) marathon_seconds = ave_seconds * 42.195 activity.marathon = datetime.timedelta(seconds=marathon_seconds) activity.save() return redirect('activity_list') # 替换为你的活动列表页面路由 else: form = ActivityForm(person=person) return render(request, 'add_activity.html', {'form': form})
第四步:模板渲染表单
在模板中渲染表单,可直接使用{{ form.as_p }}快速生成,或自定义布局:
<form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">保存跑步记录</button> </form>
可选:用户专属路线配置
如果希望每个用户只能看到自己创建的路线,修改Route模型添加person外键:
class Route(models.Model): name = models.CharField(max_length=100) person = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='routes') class Meta: unique_together = ('person', 'name') # 同一用户的路线名唯一 def __str__(self): return self.name
同时修改表单的查询集:
self.fields['route'].queryset = self.person.routes.all()
以及新路线创建逻辑:
route, _ = Route.objects.get_or_create(person=self.person, name=new_route_name.strip())
内容的提问来源于stack exchange,提问作者RickRugby
相关产品推荐
相关产品推荐

