如何基于Django模型多字段实现用户输入验证及权限访问控制
Django密钥+手机号验证访问功能修正方案
需求描述
我是编程新手,今年8月开始自学Python和Django,现在要实现一个功能:已认证的Django用户输入与数据库匹配的密钥(key)和手机号后,才能访问指定URL资源。流程是:首页按钮跳转对应视图,展示包含两个输入字段的表单;用户提交数据后验证,匹配成功就跳转到目标URL。以下是我的实现代码,求帮忙解决问题:
原代码
Models.py
from django.db import models from .utils import generate_code class Secret(models.Model): name = models.CharField(max_length=50) key = models.CharField(max_length=15, blank=True, unique=True) phone_number = models.CharField(max_length=13) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) def __str__(self): return self.name def save(self, *args, **kwargs): if self.key == "": self.key = generate_code() return super().save(*args, **kwargs)
Utils.py
import uuid def generate_code(): code = uuid.uuid4() code_mod = str(code).replace('-', '').upper()[:15] return code_mod
Forms.py
from django import forms from .models import Secret class SecretModelForm(forms.ModelForm): class Meta: model = Secret fields = ["phone_number", "key"] widgets = {"key": forms.TextInput(attrs={"placeholder": "Inserisci la chiave segreta.."})}
Views.py
from django.shortcuts import redirect, render from .forms import SecretModelForm from .models import Secret def homepage(request): context = {} return render(request, 'core/homepage.html', context) def access_to_events_by_secret(request): keys = Secret.objects.order_by('key') phone_numbers = Secret.objects.order_by('phone_number') form = SecretModelForm() context = {"form": form, "keys": keys, "phone_numbers": phone_numbers} if request.method == "POST": instance_k = Secret.objects.get(key=request.POST['key']) instance_p_n = Secret.objects.get(phone_number=request.POST['phone_number']) form = SecretModelForm(request.POST, instance_k=instance_k, instance_p_n=instance_p_n) if form.is_valid(): if request.POST.get("key").strip() == instance_k.key and request.POST.get("phone_number").strip() == instance_p_n.phone_number: return redirect("/events/") return render(request, "core/secret_access.html", context)
Secret_access.html
{% extends 'base.html' %} {% load crispy_forms_tags %} {% block head_title %}{{ block.super }} - Secret{% endblock head_title %} {% block content %} <div class="container"> <h1 class="text-light text-center mt-5">Secret Access</h1> <div class="row mt-2 justify-content-center"> <div class="col-lg-5 col-sm-auto d-flex justify-content-center"> <form method="POST" novalidate> {% csrf_token %} {{ form.key }} {{ form.phone_number }} <input type="hidden" name="key" value="{{ key }}" /> <input type="hidden" name="phone_number" value="{{ phone_number }}" /> <div class="container text-center"> <input type="submit" class="btn btn-sm" value="Accedi!"> </div> </form> </div> </div> </div> {% endblock content %}
问题分析与修正代码
核心问题点
- 视图层异常风险:使用
Secret.objects.get()会在数据不存在时抛出DoesNotExist异常,导致页面崩溃;且分开查询key和手机号无法确保属于同一个Secret记录。 - 表单使用错误:这里不需要用
ModelForm(用于创建/更新模型实例),只需普通表单验证输入;ModelForm不支持传入多个instance参数。 - 模板冗余干扰:隐藏input字段无意义,且未传递对应变量,会覆盖用户输入的有效数据。
- 未做登录验证:没有限制只有已认证用户能访问验证页面。
- 无错误反馈:验证失败时未提示用户错误信息。
修正后的代码
1. Forms.py(改用普通表单)
from django import forms class SecretAccessForm(forms.Form): key = forms.CharField( max_length=15, widget=forms.TextInput(attrs={"placeholder": "输入密钥..", "class": "form-control mb-3"}) ) phone_number = forms.CharField( max_length=13, widget=forms.TextInput(attrs={"placeholder": "输入手机号..", "class": "form-control"}) )
2. Views.py(修复验证逻辑+登录验证)
from django.shortcuts import redirect, render from django.contrib.auth.decorators import login_required from django.contrib import messages from .forms import SecretAccessForm from .models import Secret def homepage(request): context = {} return render(request, 'core/homepage.html', context) @login_required # 强制用户必须登录才能访问此视图 def access_to_events_by_secret(request): form = SecretAccessForm() if request.method == "POST": form = SecretAccessForm(request.POST) if form.is_valid(): # 从表单获取清洗后的数据 input_key = form.cleaned_data['key'].strip() input_phone = form.cleaned_data['phone_number'].strip() # 查询同时匹配key和手机号的Secret记录 try: Secret.objects.get(key=input_key, phone_number=input_phone) # 匹配成功,跳转目标页面 return redirect("/events/") except Secret.DoesNotExist: # 匹配失败,给用户错误提示 messages.error(request, "密钥或手机号错误,请重新输入") context = {"form": form} return render(request, "core/secret_access.html", context)
3. Secret_access.html(去掉冗余字段+添加错误提示)
{% extends 'base.html' %} {% load crispy_forms_tags %} {% block head_title %}{{ block.super }} - 密钥验证{% endblock head_title %} {% block content %} <div class="container"> <h1 class="text-light text-center mt-5">密钥验证</h1> <!-- 显示错误提示 --> {% if messages %} {% for message in messages %} <div class="alert alert-danger text-center mt-3">{{ message }}</div> {% endfor %} {% endif %} <div class="row mt-2 justify-content-center"> <div class="col-lg-5 col-sm-auto d-flex justify-content-center"> <form method="POST" novalidate> {% csrf_token %} {{ form.as_p }} <!-- 简化表单渲染,自动应用crispy样式 --> <div class="container text-center"> <input type="submit" class="btn btn-primary btn-sm" value="登录"> </div> </form> </div> </div> </div> {% endblock content %}
4. Models.py和Utils.py(无需修改,保持原逻辑即可)
额外说明
- 确保已在项目中启用
django.contrib.messages应用(默认已启用),用于错误提示。 @login_required装饰器会将未登录用户跳转到登录页面,需在settings.py中配置LOGIN_URL(默认是/accounts/login/)。- 表单渲染使用
{{ form.as_p }}配合crispy_forms,会自动生成带样式的输入框,比手动渲染字段更简洁。
内容的提问来源于stack exchange,提问作者Giovanni
相关产品推荐
相关产品推荐

