Django嵌套函数功能正常但验证器触发时同时显示错误与成功消息问题
问题根源分析
你遇到的问题核心在于:工具函数add_flight_function中的重定向操作并不会终止视图函数add_new_flight的执行流程。当验证失败时,工具函数虽然添加了错误消息并重定向,但它只是自己返回了一个HttpResponseRedirect对象,而视图函数里调用add_flight_function()后,并没有处理这个返回值,而是直接继续执行messages.success(...),然后又发起了一次重定向。这就导致两个消息(错误+成功)都被添加到了消息队列中,最终在页面上一起显示。
而且工具函数里的一些数据库操作(比如FunctionType.objects.get)如果出错,会直接抛出异常,被视图里的except捕获,添加"Failed to Add New Flight"的错误消息,但这和验证器的错误消息是不同场景的问题,需要区分处理。
解决方案
我们需要让工具函数向视图函数反馈执行结果,然后由视图函数来决定后续的消息添加和重定向逻辑。具体修改如下:
1. 修改utils.py中的add_flight_function
让函数返回一个布尔值,表示航班是否成功添加;同时,把验证失败时的重定向逻辑移到视图函数中,工具函数只负责业务逻辑和错误消息的添加:
def add_flight_function(request): try: aircraft_maintenance_type = AircraftMaintenanceType.objects.first() except: aircraft_maintenance_type = None aircraft = request.POST.get('aircraft') adep = request.POST.get('adep') ades = request.POST.get('ades') date = request.POST.get('date') atd = request.POST.get('atd') ata = request.POST.get('ata') function_type_id = request.POST.get('function_type') # 处理FunctionType获取异常,避免直接抛出 try: function_type_obj = FunctionType.objects.get(id=function_type_id) except FunctionType.DoesNotExist: messages.error(request, "Invalid function type selected") return False operational_condition = request.POST.get('operational_condition') student = request.POST.get('student') instructor = request.POST.get('instructor') night = request.POST.get('night') note = request.POST.get('note') # 处理Aircraft获取异常 try: aircraft_obj = Aircraft.objects.get(id=aircraft) aircraft_type = aircraft_obj.aircraft_type.abbreviation except Aircraft.DoesNotExist: messages.error(request, "Invalid aircraft selected") return False # 转速表验证逻辑 if aircraft_maintenance_type and aircraft_maintenance_type.is_tacho: atd_tacho = request.POST.get('atd_tacho') ata_tacho = request.POST.get('ata_tacho') if not atd_tacho or not ata_tacho: messages.error(request, "Tacho values are required") return False try: if float(ata_tacho) <= float(atd_tacho): messages.error(request, "ATA Tacho cannot be lower than ATD Tacho") return False except ValueError: messages.error(request, "Tacho values must be numbers") return False # 通用航班验证 is_valid_flight, error_message = flight_validator(request, ata, atd, function_type_obj.name, instructor) if not is_valid_flight: messages.error(request, error_message) return False # 开始创建航班记录 try: log_entry = LogEntry.objects.create( aircraft_id=aircraft, adep_id=adep, ades_id=ades, date=date, atd=atd, ata=ata, function_type_id=function_type_obj.id, student_id=student, instructor_id=instructor, operational_condition_id=operational_condition, note=note ) if night == 'on': log_entry.night_flight = True if function_type_obj.name == 'Single Pilot': log_entry.solo_flight = True if aircraft_type == 'SIM': log_entry.sim_flight = True elif aircraft_type == 'MEP': log_entry.mep_flight = True log_entry.save() # 维护时间记录逻辑 if not aircraft_maintenance_type or not aircraft_maintenance_type.is_tacho: try: ata_obj = parse(ata) atd_obj = parse(atd) if (ata_obj - atd_obj).total_seconds() > 600: # 10分钟=600秒 maintenance_eet = (ata_obj - atd_obj) - timedelta(minutes=10) else: maintenance_eet = ata_obj - atd_obj AircraftFlightTime.objects.create( aircraft_id=aircraft, log_entry=log_entry, maintenance_eet=maintenance_eet ) except Exception as e: messages.error(request, f"Error calculating maintenance time: {str(e)}") log_entry.delete() # 回滚已创建的日志 return False else: try: ata_obj = float(ata_tacho) atd_obj = float(atd_tacho) maint_eet_obj = (ata_obj - atd_obj) * 60 maintenance_eet = timedelta(minutes=maint_eet_obj) AircraftFlightTime.objects.create( aircraft_id=aircraft, log_entry=log_entry, maintenance_eet=maintenance_eet, atd_tacho=atd_tacho, ata_tacho=ata_tacho ) except ValueError: messages.error(request, "Invalid tacho values for maintenance") log_entry.delete() return False except Exception as e: messages.error(request, f"Error saving flight data: {str(e)}") return False return True
2. 修改views.py中的add_new_flight视图
根据工具函数的返回值来决定添加成功还是错误消息,并统一处理重定向:
def add_new_flight(request): aircrafts = Aircraft.objects.all() aerodromes = Aerodrome.objects.filter(is_base=False) base_aerodrome = Aerodrome.objects.get(is_base=True) function_type = FunctionType.objects.all() students = Student.objects.filter(is_active=True).order_by('last_name') instructors = Instructor.objects.filter(is_active=True).order_by('last_name') operational_conditions = OperationalCondition.objects.all() today = dt.today().date() try: aircraft_maintenance_type = AircraftMaintenanceType.objects.first() except: aircraft_maintenance_type = None context = { 'aircrafts': aircrafts, 'aerodromes': aerodromes, 'function_type': function_type, 'students': students, 'instructors': instructors, 'today': today, 'operational_conditions': operational_conditions, 'base_aerodrome': base_aerodrome, 'aircraft_maintenance_type': aircraft_maintenance_type, } if request.method == 'POST': success = add_flight_function(request) if success: messages.success(request, "New Flight Inserted Successfully") return HttpResponseRedirect(reverse('flight:flight_list')) else: # 工具函数已经添加了具体的错误消息,这里不需要再添加通用错误 return HttpResponseRedirect(request.META.get('HTTP_REFERER') or reverse('flight:add_new_flight')) return render(request, 'flight/add_new_flight.html', context)
关键修改点说明
- 工具函数返回执行状态:通过返回
True/False,让视图函数明确知道航班是否成功添加,避免了之前不管结果都添加成功消息的问题。 - 异常本地化处理:在工具函数中处理数据库查询、数据转换的异常,返回具体的错误消息,而不是让异常冒泡到视图的通用
except块,这样错误提示更精准。 - 移除工具函数中的重定向:把重定向逻辑统一交给视图函数处理,避免多次重定向导致的消息队列混乱。
- 数据回滚:在创建日志记录后,如果后续的维护时间记录保存失败,会删除已创建的日志,保证数据一致性。
这样修改后,当验证失败时,只会显示对应的错误消息,不会再出现成功消息;只有当所有流程都执行成功时,才会显示成功提示并跳转到航班列表页。
内容的提问来源于stack exchange,提问作者Giorgio Scarso
相关产品推荐
相关产品推荐

