如何在Django模板中显示Serializer验证错误?
解决DRF注册页面验证错误不显示的问题
一、修正视图层(views.py)的问题
你的post方法存在两个关键问题:
- 使用
raise_exception=True会直接抛出异常,DRF会返回400响应,而不会将错误保留在序列化器中供模板渲染。 - 验证失败时的
redirect("register")没有加return,且重定向会丢失序列化器中的错误信息,应该直接渲染模板并传递带错误的序列化器。
修改后的views.py代码:
class RegisterView(generics.CreateAPIView): serializer_class = PersonSerializer renderer_classes = [TemplateHTMLRenderer] template_name = "register.html" def get(self, request: Union[Request, HttpRequest]) -> Union[render, redirect]: if "username" in request.session: return redirect("home") serializer = PersonSerializer() return render(request, self.template_name, {"serializer": serializer}) def post(self, request: Union[Request, HttpRequest]) -> Union[redirect, render]: serializer = PersonSerializer(data=request.data) # 去掉raise_exception=True,手动处理验证结果 if serializer.is_valid(): serializer.save() # 必须调用save()执行create方法创建用户 return redirect("login") # 验证失败,将带错误的序列化器传回模板 return render(request, self.template_name, {"serializer": serializer})
二、修正序列化器(serializers.py)的问题
- 添加
confirmpassword字段:因为Person模型中没有这个字段,需要显式在序列化器中定义它,否则request.data中的confirmpassword不会被序列化器处理,validate方法也无法获取到该值。 - 将全局错误改为字段关联错误:原来的
validate方法抛出的是全局错误,改为字段级错误或字典形式的错误,让模板能对应到具体字段显示错误。
修改后的serializers.py代码:
from rest_framework import serializers from .models import Person from typing import Union class PersonSerializer(serializers.ModelSerializer): GENDER_CHOICES = [ ("male", "Male"), ("female", "Female"), ("other", "Other"), ] gender = serializers.ChoiceField(choices=GENDER_CHOICES) # 显式添加确认密码字段,仅用于写入(不返回) confirmpassword = serializers.CharField(write_only=True) class Meta: model = Person fields = "__all__" # 密码字段设置为仅写入 extra_kwargs = {"password": {"write_only": True}} def create(self, validated_data): # 移除confirmpassword,模型不需要这个字段 validated_data.pop("confirmpassword") gender = validated_data.pop("gender") validated_data["gender"] = gender.lower() return Person.objects.create(**validated_data) def validate_username(self, value): # 单独验证用户名唯一性 if Person.objects.filter(username=value).exists(): raise serializers.ValidationError('Username already exists') return value def validate_phone(self, value): # 单独验证手机号格式 if len(value) != 10: raise serializers.ValidationError("Phone number must be 10 digits") return value def validate(self, data): # 验证密码与确认密码匹配 password = data.get("password") confirmpassword = data.get("confirmpassword") if password != confirmpassword: # 将错误关联到confirmpassword字段 raise serializers.ValidationError({'confirmpassword': 'Passwords do not match'}) return data
三、模板(register.html)渲染错误并添加CSS样式
在模板中通过serializer.errors获取错误信息,为每个字段显示对应的错误,同时添加CSS样式美化提示。
示例模板代码:
<!DOCTYPE html> <html> <head> <title>Register</title> <style> /* 错误信息样式 */ .error-message { color: #dc3545; font-size: 0.875rem; margin-top: 0.25rem; } /* 表单字段容器 */ .form-group { margin-bottom: 1rem; } /* 错误字段的输入框样式 */ .form-control.error { border-color: #dc3545; box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); } </style> </head> <body> <h2>Register</h2> <!-- 显示全局错误(如果有) --> {% if serializer.non_field_errors %} <div class="error-message"> {{ serializer.non_field_errors.0 }} </div> {% endif %} <form method="post"> {% csrf_token %} {% for field in serializer %} <div class="form-group"> {{ field.label_tag }} {{ field }} {% if field.errors %} {% for error in field.errors %} <div class="error-message">{{ error }}</div> {% endfor %} {% endif %} </div> {% endfor %} <button type="submit">Register</button> </form> </body> </html>
关键说明
- 字段级验证函数(如
validate_username)抛出的错误会自动关联到对应字段,模板中通过field.errors获取。 - 密码匹配的错误关联到
confirmpassword字段,确保提示显示在确认密码输入框下方。 - 使用DRF序列化器的字段渲染(
{{ field }})会自动生成合适的HTML输入元素,同时保留错误状态。
内容的提问来源于stack exchange,提问作者Sahil
相关产品推荐
相关产品推荐

