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

在已实现JWT视图的Django中如何创建用户注册登录表单?

在Django中实现JWT认证后,如何创建用户输入凭证的表单?

我正在学习用Django结合pyJWT实现用户身份验证,已经通过Postman测试完成了核心功能——用MySQL存储数据,密码哈希处理、Token生成都正常。现在卡在创建用户输入凭证的表单这一步,不知道怎么推进。

以下是现有代码:

Views.py

class RegisterView(APIView):
    def post(self, request):
        serializer = UserSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)
    
class LoginView(APIView):
    def post(self, request):
        email = request.data['email']
        password = request.data['password']

        user = User.objects.filter(email=email).first()

        if user is None:
            raise AuthenticationFailed('User not found')

        if not user.check_password(password):
            raise AuthenticationFailed('Incorrect password')

        payload = {
            'id': user.id,
            'exp': datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=60),
            'iat': datetime.datetime.now(datetime.timezone.utc)
        }

        token = jwt.encode(payload, 'secret', algorithm='HS256')

        response = Response()
        response.set_cookie(key='jwt', value=token, httponly=True)
        response.data = {"jwt": token}
        return response
    

class UserView(APIView):
    def get(self, request):
        token = request.COOKIES.get('jwt')

        if not token:
            raise AuthenticationFailed('Unathenticated!')
        try:
            payload = jwt.decode(token, 'secret', algorithms=['HS256'])
        except jwt.ExpiredSignatureError:
            raise AuthenticationFailed('Uathenticated')
        
        user = User.objects.filter(id=payload['id']).first()
        serializer = UserSerializer(user)
        return Response(serializer.data)
    

class LogoutView(APIView):
    def post(self, request):
        response = Response()
        response.delete_cookie('jwt')
        response.data = {'message': 'Success'}
        return response

Serializers.py

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'email', 'password', 'first_name', 'last_name', 'username']
        extra_kwargs = {"password": {"write_only": True}}

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

Models.py

class User(AbstractUser):
    email = models.CharField(max_length=255, unique=True)
    password = models.CharField(max_length=255)
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)
    username = models.CharField(max_length=255)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

解决方案:两种表单实现方式

方式1:Django原生服务端渲染表单

适合传统服务端渲染的场景,复用现有业务逻辑:

  1. 创建表单文件 forms.py
from django import forms
from .models import User

class RegisterForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput)
    confirm_password = forms.CharField(widget=forms.PasswordInput)

    class Meta:
        model = User
        fields = ['email', 'username', 'first_name', 'last_name', 'password']

    def clean(self):
        cleaned_data = super().clean()
        password = cleaned_data.get('password')
        confirm_password = cleaned_data.get('confirm_password')
        if password != confirm_password:
            raise forms.ValidationError("密码不匹配")
        return cleaned_data

class LoginForm(forms.Form):
    email = forms.EmailField()
    password = forms.CharField(widget=forms.PasswordInput)
  1. 添加表单处理视图
    在views.py中新增处理页面渲染和表单提交的视图:
from django.shortcuts import render, redirect
from .forms import RegisterForm, LoginForm

# 注册页面
def register_page(request):
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid():
            # 复用现有Serializer逻辑创建用户
            serializer = UserSerializer(data=form.cleaned_data)
            serializer.is_valid(raise_exception=True)
            serializer.save()
            return redirect('login_page')
    else:
        form = RegisterForm()
    return render(request, 'register.html', {'form': form})

# 登录页面
def login_page(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            email = form.cleaned_data['email']
            password = form.cleaned_data['password']
            # 复用现有LoginView的验证逻辑
            user = User.objects.filter(email=email).first()
            if user and user.check_password(password):
                payload = {
                    'id': user.id,
                    'exp': datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=60),
                    'iat': datetime.datetime.now(datetime.timezone.utc)
                }
                token = jwt.encode(payload, 'secret', algorithm='HS256')
                response = redirect('user_profile')
                response.set_cookie(key='jwt', value=token, httponly=True)
                return response
            else:
                form.add_error(None, '邮箱或密码错误')
    else:
        form = LoginForm()
    return render(request, 'login.html', {'form': form})
  1. 创建模板文件
    在templates目录下创建register.html和login.html:
    register.html示例:
<!DOCTYPE html>
<html>
<head>
    <title>用户注册</title>
</head>
<body>
    <h2>注册账号</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">完成注册</button>
    </form>
</body>
</html>

方式2:前后端分离的前端表单

如果是前后端分离架构,直接通过AJAX调用已有的API接口:

<!DOCTYPE html>
<html>
<head>
    <title>用户登录</title>
</head>
<body>
    <h2>登录</h2>
    <form id="loginForm">
        <div>
            <label>邮箱:</label>
            <input type="email" name="email" required>
        </div>
        <div>
            <label>密码:</label>
            <input type="password" name="password" required>
        </div>
        <button type="submit">登录</button>
    </form>

    <script>
        document.getElementById('loginForm').addEventListener('submit', async (e) => {
            e.preventDefault();
            const formData = new FormData(e.target);
            const data = Object.fromEntries(formData);
            
            try {
                const response = await fetch('/api/login/', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify(data),
                    credentials: 'include' // 携带Cookie保存Token
                });
                const result = await response.json();
                
                if (response.ok) {
                    alert('登录成功');
                    window.location.href = '/api/user/';
                } else {
                    alert(result.detail || '登录失败');
                }
            } catch (err) {
                alert('请求出错,请稍后重试');
            }
        });
    </script>
</body>
</html>

注册表单逻辑类似,只需将请求地址改为/api/register/即可。


内容的提问来源于stack exchange,提问作者mdm1e

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:17:45