如何在Django中实现联动下拉表单:选中训练后匹配对应练习数量的字段校验
实现方案
以下为可直接落地的完整修改步骤:
1. 调整模型(models.py)
模型层的校验规则为静态配置,无法随关联数据动态调整最大值,因此仅配置最小值校验,最大值校验放到表单层动态实现:
from django.db import models from django.contrib.auth.models import User from django.core.validators import MinValueValidator class Routine(models.Model): """Shows a routine""" workout = models.ForeignKey(Workout, on_delete=models.CASCADE) owner = models.ForeignKey(User, on_delete=models.CASCADE) num_of_exercises = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)]) def __str__(self): return 'this is a routine'
2. 修改表单(forms.py)
把动作数量字段加入表单,同时新增clean方法做服务端二次校验,避免前端校验规则被恶意绕过:
from django import forms from .models import Workout, Exercise, Routine class RoutineForm(forms.ModelForm): """creates a form for a routine""" def __init__(self, *args, user=None, **kwargs): super().__init__(*args, **kwargs) self.fields['workout'].queryset = Workout.objects.filter(owner=user) def clean(self): cleaned_data = super().clean() workout = cleaned_data.get('workout') input_num = cleaned_data.get('num_of_exercises') if workout and input_num: exercise_total = workout.exercise_set.count() if input_num > exercise_total: raise forms.ValidationError(f"所选训练计划仅包含{exercise_total}个动作,输入值不可超过该上限") return cleaned_data class Meta: model = Routine fields = ['workout', 'num_of_exercises'] labels = {'workout': '',}
3. 新增查询接口(views.py)
新增接口用于前端查询指定训练计划的动作总数,同时做权限校验,避免用户越权查询他人的训练计划:
from django.shortcuts import render, redirect from django.http import JsonResponse from django.contrib.auth.decorators import login_required from .models import Workout, Exercise from .forms import RoutineForm @login_required def routine(request): """display a dropdown of all workouts for a user""" if request.method != 'POST': form = RoutineForm(user=request.user) else: form = RoutineForm(data=request.POST, user=request.user) if form.is_valid(): form.instance.owner = request.user new_routine = form.save() return redirect('index') context = {'form':form} return render(request, 'routine_workouts_app/routine.html', context) # 新增的查询接口 @login_required def get_exercise_count(request): workout_id = request.GET.get('workout_id') if not workout_id: return JsonResponse({'count': 0}) # 校验训练计划所有权 workout = Workout.objects.filter(id=workout_id, owner=request.user).first() if not workout: return JsonResponse({'count': 0}) return JsonResponse({'count': workout.exercise_set.count()})
之后需要在应用的urls.py中新增对应路由:
path('get-exercise-count/', views.get_exercise_count, name='get_exercise_count'),
4. 模板添加联动逻辑(routine.html)
在模板中添加JS逻辑,监听训练计划下拉框的变动,动态更新整数字段的最大值限制:
{% extends 'routine_workouts_app/base.html' %} {% block content %} <p>this is the home page for creating routines</p> <form action="{% url 'routine_workouts_app:routine' %}" method="POST"> {% csrf_token %} {{ form.as_p }} <button name='submit'>create routine</button> </form> <script> // 确保项目已集成jQuery后使用以下逻辑 $(function(){ const numInput = $('input[name="num_of_exercises"]'); // 监听训练计划下拉框变化 $('select[name="workout"]').change(function(){ const workoutId = $(this).val(); if(!workoutId){ numInput.attr('max', 0).val(0); return; } // 请求获取对应训练计划的动作总数 $.get("{% url 'routine_workouts_app:get_exercise_count' %}", {workout_id: workoutId}, function(res){ const maxNum = res.count; numInput.attr('max', maxNum); // 若当前输入值超过上限则自动重置为上限值 if(parseInt(numInput.val()) > maxNum){ numInput.val(maxNum); } numInput.attr('placeholder', `最大可填${maxNum}`); }) }) }) </script> {% endblock content %}
以上修改完成后即可实现需求:选中训练计划后整数字段的最大值会自动同步为该训练计划的动作总数,同时前后端双重校验保证数据合法性。
内容的提问来源于stack exchange,提问作者Kyle F.
相关产品推荐
相关产品推荐

