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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 17:57:24