django组件-2
中间件-(内置方法)
一、csrf
1.1、csrf说明
- 是什么?
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
-
如何理解?
攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。 如下:其中Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户;
-
CSRF攻击原理
-
攻击防护
-
验证 HTTP Referer 字段
-
在请求地址中添加 token 并验证 (加载请求的路径里,加载请求体中)
在请求体中加上token值进行验证
-
在 HTTP 头中自定义属性并验证
这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中
-
1.2、攻击防护验证
当django开启中间件:django.middleware.csrf.CsrfViewMiddleware时需要配置csrf字段
-
form添加csrf_token
# 在请求地址中添加 token并验证,当开启中间件时,如果没有指定csrf_token时,在访问的时候也会给予提示 # In the template, there is a {% csrf_token %} template tag inside # html页面中修改为 <form action="." method="post"> {% csrf_token %} # 关键字段,django自带的模板 <p>姓名: <input type="text" name="name"></p> <p>密码: <input type="text" name="pwd"></p> <input type="submit" value="提交"> </form> # 在浏览器中显示如下, 类型隐藏, 值只是第一次生效,后续每次访问会自动变更,防止黑客拿到value值进行攻击 <input type="hidden" name="csrfmiddlewaretoken" value="YBZWMYCSOQa4veKrJLPGzTThrJu5XMlrgF9zPjTKEz5MkLd4Y3UvF7PAdqNfUT8s"> # ============================== 源码 ============================== @register.tag def csrf_token(parser, token): return CsrfTokenNode() class CsrfTokenNode(Node): def render(self, context): csrf_token = context.get('csrf_token') if csrf_token: if csrf_token == 'NOTPROVIDED': return format_html("") else: return format_html('<input type="hidden" name="csrfmiddlewaretoken" value="{}">', csrf_token)
-
ajax中添加csrf
# 写法一, 直接取出名称,然后获取值的方式
<script>
$(".btn").on("click", function () {
$.ajax({
url: "。。。。",
type: "post",
data: {
"name": $('[name="name"]').val(),
"pwd": $('[name="pwd"]').val(),
# 关键字段,就这一句
"csrfmiddlewaretoken": $('[name="csrfmiddlewaretoken"]').val(),
},
success: function (data) {
console.log(data)
}
})
})
</script>
# 写法二, 直接取出模板中的值, 需要注意要加单|双引号,否则就是一个变量
"csrfmiddlewaretoken": '{{ csrf_token }}',
1.3、局部使用
from django.views.decorators.csrf import csrf_exempt, csrf_protect
# 局部验证, 需要关闭csrf中间件 csrf_protect
# 局部禁用, 需要开启csrf中间件 csrf_exempt
# 局部禁用使用csrf中间件
@csrf_exempt
def test_csrf(request):
return HttpResponse("OK")
# 局部验证
@csrf_protect
1、先关闭csrf验证, ajax中提交中不加csrfmiddlewaretoken
2、提交时验证就会无法通过, 而其它的post html页面则不受影响
3、可以用到购物中,加购物车不受影响付款时需要提交验证
1.3.1、CBV中使用
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
# django中, CBV 局部禁用无法直接用装饰器在方法直接装饰, CBV的csrf装饰器,只能加载类上(指定方法为dispatch)和dispatch方法上(django的bug)
class Test_csrf(View):
def get(self, request):
return render(request, "test_csrf.html")
@method_decorator(csrf_exempt)
def post(self, request):
return HttpResponse("OK")
# 需要加到 dispatch中, 方法一
class Test_csrf(View):
@method_decorator(csrf_exempt, name="post")
def dispatch(self, request, *args, **kwargs):
ret = super().dispatch(request, *args, **kwargs)
return ret
# 方法二
@method_decorator(csrf_exempt, name="dispatch")
class Test_csrf(View):
def dispatch(self, request, *args, **kwargs):
ret = super().dispatch(request, *args, **kwargs)
return ret
1.3.2、登录认证
import re
from django.shortcuts import render, HttpResponse, redirect, reverse
# Create your views here.
# 登录认证, 登录用户可以继续操作, 其它都必须要先登陆
# from django.middleware.csrf import CsrfViewMiddleware
from django.utils.deprecation import MiddlewareMixin
LOGIN_URL = "/login/login"
"""
1、获取到完整的路径,与正则路径匹配,如果完全符合则下一步
2、获取到session是否登陆, 如果登陆也返回
3、此时第1步,如果在跳转的时候一直匹配不上,则路径会一直进行拼接,此时获取一下next的路径如果有,也直接给它返回了
"""
class LoginCheck(MiddlewareMixin):
def process_request(self, request):
login_url = reverse("auth:login")
next_path = request.get_full_path()
next = request.GET.get("next")
if re.match(next_path, login_url) or request.session.get("is_login") or next:
print("匹配")
return
else:
return redirect("/login/login?next={}".format(next_path))
def process_response(self, request, response):
return response
'''
# url 后加 next跳转
1、login页面获取next路径, 将路径传到 html页面
2、页面获取到之后,拼接到 input中 以name方式在传输到后台
'''
def login(request):
if request.method == "GET":
next_path = request.GET.get("next")
if next_path:
return render(request, "login/login.html", {"next_path": next_path})
return render(request, "login/login.html")
if request.method == "POST":
res = None
name = request.POST.get("name")
pwd = request.POST.get("pwd")
next = request.POST.get("next_path")
if name == "xiong" and pwd == "123":
request.session["is_login"] = True
print(next)
if next:
return redirect(next)
res = "登陆成功"
else:
res = "登陆失败"
return HttpResponse(res)
def buy(request):
return HttpResponse("购物")
二、auth
django内置的用户认证系统, 我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们可以使用auth来可以快速的实现,登录,注销,修改密码功能,django默认使用auth_user表来存储用户数据
2.1、auth初始化
# 一、连接数据库连接
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'xx_auth',
'USER': 'root',
'PASSWORD': '111111',
'HOST': '192.168.1.199',
'PORT': '3306',
}
}
# 二、初始数据库
python manage.py makemigrations
python manage.py migrate
# 三、创建超级用户
python3 manage.py createsuperuser
也就是在auth_user这个表中插入了一条数据(密码是加密的,不能手动插入)
# pbkdf2_sha256$216000$H3....Sm$9jee/1/vma74e....lPVnV8Es=
2.2、常用方法
# 模块导入:from django.contrib import auth
# urls.py
urlpatterns = [
path('xlogin/', views.xlogin), # 两种urls的写法
path("login/", views.Login.as_view()),
]
# html
<form action="." method="post">
{% csrf_token %} # 注意csrf校验
<p>用户: <input type="text" name="username"></p>
<p>密码: <input type="text" name="pwd"></p>
<input type="submit" value="提交">
</form>
2.2.1、登陆类方法
-
authenticate
# 检验用户名与密码 class Login(View): def get(self, request): return render(request, "login.html") def post(self, request): user = request.POST.get("username") pwd = request.POST.get("pwd") res = auth.authenticate(request, username=user, password=pwd) # 相当于在查询:user=models.User.objects.filter(name=name,pwd=pwd).first() # 如果校验通过,会返回一个user对象,通过判断user对象,校验是否验证成功 if res: return HttpResponse("ok") return HttpResponse("登陆失败")
-
登陆成功之后的方法 (login)
if res: # 可以在登陆成功之后,添加 auth.login方法 auth.login(request, res) # 相当于是 request.session["xx"] = "sessionid"
-
登陆成功之后的方法 (request.user)
# 依赖于login, 如果没有登陆,那么它回显的就是 匿名用户 AnonymousUser def test_user(request): request.session.flush() user = request.user print(user.username, user) return HttpResponse("ok") user.is_authenticated # 校验用户是否登陆
-
注销用户(logout)
def logout(request):
auth.logout(request)
# 相当于就是 request.session.flush()
return redirect(reverse("login"))
- 登录认证装饰器
from django.contrib.auth.decorators import login_required
# def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
# redirect_field_name=REDIRECT_FIELD_NAME, url中?后跟的next值,可以被定义为其它
# login_url=None 如果没有登陆要跳转的Url,其实就是判断了 user.is_authenticated
@login_required(login_url="/login/")
def buy(request):
return HttpResponse("购物")
# @login_required(login_url="/login/") 如果想在装饰中省略login_url需要在setting.py中写上
LOGIN_URL='/login/'
2.2.2、创建类方法
- 创建用户
def create_user(request):
# 注意: 不能直接使用create方法,因为auth_user的密码是加过密的
res = User.objects.create_user(username="xiong2", password="123")
# 也可以创建超级用户,但是不建议 create_superuser
res返回的就是创建的用户名称
- 检验密码
def check_user(request):
# 检验当前用户密码是否是它
res = request.user.check_password("123456")
# res = User.check_password("123") # 不能直接这么写,因为检验的是当前的用户
return HttpResponse(res)
- 修改密码
def set_pass(request):
res = request.user.set_password("123")
request.user.save() # 注意,修改完之后,一定要保存
return HttpResponse(res)
- 其它方法
- is_active:禁止登录网站(用户还存在,封号)
- is_staff:是否对网站有管理权限(能不能登录admin)
2.2.3、扩展auth_user表
-
一对一关连表
# models.py中加上关连表即可 from django.db import models from django.contrib.auth.models import User # Create your models here. class Userdetail(models.Model): phone = models.CharField(max_length=128, null=True) age = models.IntegerField(null=True) users = models.OneToOneField(on_delete=models.CASCADE, to=User) # 最后在执行数据迁移 关键字: makemigrations, migrate
-
继承 AbstractUser
from django.db import models from django.contrib.auth.models import User,AbstractUser # User也是继承的这个类, 修改了元类,配置文件中获取AUTH_USER_MODEL # class User(AbstractUser): # class Meta(AbstractUser.Meta): # swappable = 'AUTH_USER_MODEL' class Users(AbstractUser): phone = models.CharField(max_length=128, null=True) age = models.IntegerField(null=True) -在setting中配置: AUTH_USER_MODEL ='app01.UserInfo' -做数据库迁移,以后就没有auth_user这个表了,以后认证组件用的表就是UserInfo #setting中配置: AUTH_USER_MODEL="auth01.Users" # 在做数据库迁移,以后就没有auth_user这个表了,以后认证组件用的表就是Users
2.3、登陆示例-demo
- views.py
from django.shortcuts import render, HttpResponse, redirect, reverse
# Create your views here.
from django.views import View
from django.contrib import auth
# 登陆
class Login(View):
def get(self, request):
return render(request, "demo/login.html")
def post(self, request):
user = request.POST.get("username")
pwd = request.POST.get("pwd")
res = auth.authenticate(request, username=user, password=pwd)
if res:
auth.login(request, res)
return redirect(reverse("demo:demo_login"))
return HttpResponse("登陆失败")
# 修改密码
class Change_pass(View):
def get(self, request):
return render(request, "demo/change_pass.html")
def post(self, request):
old_pwd = request.POST.get("old_pwd")
if request.user.check_password(old_pwd):
new_pwd = request.POST.get("new_pwd")
if new_pwd == request.POST.get("new_pwd2"):
request.user.set_password(new_pwd)
request.user.save()
return redirect(reverse("demo:demo_logout"))
return HttpResponse("原密码或新密码不一致")
# 退出
def logout(request):
auth.logout(request)
return redirect(reverse("demo:demo_login"))
- urls.py
# 主urls.py
from django.urls import path, include
from auth01 import views
urlpatterns = [
path("demo/", include("app02.urls"))
]
# app/urls.py
from django.urls import path, include
from . import views
app_name = "demo"
urlpatterns = [
path("login/", views.Login.as_view(), name="demo_login"),
path("logout/", views.logout, name="demo_logout"),
path("change_pass/", views.Change_pass.as_view(), name="demo_change_pass"),
]
- html
# 登陆页
{% if request.user.is_authenticated %}
<p>用户: {{ request.user }}</p>
<p><a href="{% url 'demo:demo_change_pass' %}">修改密码</a></p>
<p><a href="{% url 'demo:demo_logout' %}">注销用户</a></p>
{% else %}
<form action="." method="post">
{% csrf_token %}
<p>用户: <input type="text" name="username"></p>
<p>密码: <input type="text" name="pwd"></p>
<input type="submit" value="提交">
</form>
{% endif %}
# 修改密码
<form action="{% url 'demo:demo_change_pass' %}" method="post">
{% csrf_token %}
<p>原密码: <input type="text" name="old_pwd"></p>
<p>新密码: <input type="text" name="new_pwd"></p>
<p>确认密码: <input type="text" name="new_pwd2"></p>
<input type="submit" value="提交">
</form>
三、foms组件
3.1、简易使用
1. 前台渲染
打印出错误信息:{{ foo.errors.0 }} 实际就是 foo.属性.errors.0 取第0个
自动渲染模板: {{ myform.as_p }} as_p 直接将模板渲染成p标签的样式
-
方式一, 不推荐使用
# myform是由后端的forms生成的字典 <form action="" method="post" novalidate> {{ myform.as_p }} <input type="submit" value="提交" class="btn btn-success"> </form>
-
方式二, 如果过多的标签维护将会很复杂
<form action="" method="post" novalidate> <p>用户: {{ myform.user }}</p> <p>密码: {{ myform.pwd }}</p> <p>邮箱: {{ myform.email }}</p> <input type="submit" value="提交" class="btn btn-success"> </form>
-
方式三, 推荐使用
{% for foo in myform %} <p>{{ foo.label }} {{ foo }} <span>{{ foo.errors.0 }}</span></p> {% endfor %}
3.1.1、form用法
错误信息: error_messages = {"max_length": "最大值", "min_length": "最小值", "invalid": "格式不正确", "required": "不能为空"}
from django import forms
class User(forms.Form):
user = forms.CharField(
max_length=10,
min_length=3,
label="用户:",
error_messages={"max_length": "最大不能超过10位", "min_length": "低于最小值", "required": "不能为空"},
widget=forms.widgets.TextInput(attrs={"class": "form-control"}))
pwd = forms.CharField(
max_length=10,
min_length=3,
label="密码:",
error_messages={"max_length": "最大不能超过10位", "min_length": "输入密码太短"},
widget=forms.widgets.PasswordInput(attrs={"class": "form-control"}))
email = forms.EmailField(
label="邮箱:",
error_messages={"invalid": "格式不正确 示例:xx@xx.com"},
widget=forms.widgets.EmailInput(attrs={"class": "form-control"}))
3.1.2、视图
# 生成的对象.is_valid 检验数据是否正常
# 生成的对象.cleaned_data 打印数据,字典格式
from django.shortcuts import render, HttpResponse
from app01.userfroms import User
def test(request):
if request.method == "GET":
myform = User()
elif request.method == "POST":
myform = User(request.POST)
if myform.is_valid():
data = myform.cleaned_data
print(data) # {'user': 'test', 'pwd': 'sssss', 'email': '1@qq.com'}
return HttpResponse("ok")
return render(request, 'test.html', {"myform": myform})
3.1.3、钩子函数
# 检验字段
self._clean_fields()
# 检验全局
self._clean_form()
self._post_clean()
# 原码, 程序的最后钩子函数的执行方式,反射并return一个结果
def _clean_fields(self):
# 循环forms中的值
# name = 属性名, field = form.后的属性
for name, field in self.fields.items():
# value_from_datadict() gets the data from the data dictionaries.
# Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields.
if field.disabled:
value = self.get_initial_for_field(field, name)
else:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, FileField):
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
value = field.clean(value)
# 判断字段都正常,那就将结果打包成字典,返回
self.cleaned_data[name] = value
# 钩子函数, 当forms程序运行完成,调用clean_属性名完成最后一次的执行
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
# 如果值都不对那么就抛出异常
except ValidationError as e:
self.add_error(name, e)
3.2、modelform用法
model 只能 “一对一关联的model类名”, 以下看实例
3.2.1、常用字段
model = models.Book # 对应的Model中的类 一对一关联的model类名
fields = "__all__" # 字段,如果是__all__,就是表示列出所有的字段
# fields = ("user","pwd") 也可以只指定某几个字段
exclude = None # 排除的字段
labels = None # 提示信息
help_texts = None # 帮助提示信息
widgets = None # 自定义插件
格式: widgets = { 字段: {属性: "属性"} } 如
"user": forms.widgets.TextInput(attrs={"class": "form-control"}),
error_messages = None # 自定义错误信息
格式: error_messages = {"字段": {错误: "提示"}} 如
"user": {"required": "不允许为空", "invalid": "格式错误"},
myforms = Myforms(request.POST)
if myforms.is_valid(): 当检验成功之后, 可以直接调用.save()方法保存
myforms.save()
3.2.2、示例
- myforms.py
# 导入forms以及models
from django import forms
from app01 import models
class Myforms(forms.ModelForm):
class Meta:
model = models.User
fields = "__all__"
# 只指定某几个字段
# fields = ("user","pwd")
labels = {
"user": "用户名: ",
"pwd": "密码:"
}
# 定义错误信息
error_messages = {
"user": {"required": "不允许为空", "invalid": "格式错误"},
"pwd": {"required": "不允许为空", "invalid": "格式错误"},
}
# 指定格式
widgets = {
"user": forms.widgets.TextInput(attrs={"class": "form-control"}),
"pwd": forms.widgets.PasswordInput(attrs={"class": "form-control"}),
}
- model.py
from django.db import models
# Create your models here.
class User(models.Model):
id = models.AutoField(primary_key=True)
user = models.CharField(max_length=10)
pwd = models.CharField(max_length=16)
def __str__(self):
return self.user
- views.py
def register(request):
myforms = []
if request.method == "GET":
myforms = Myforms()
elif request.method == "POST":
myforms = Myforms(request.POST)
if myforms.is_valid():
myforms.save()
return HttpResponse("注册成功 ")
return render(request, "register.html", {"myforms": myforms})
- html文件
<form action="{% url 'register' %}" method="post" novalidate>
{% for myform in myforms %}
<div class="form-group">
<p>{{ myform.label }} {{ myform }}</p>
<span class="error_size">{{ myform.errors.0 }}</span>
</div>
{% endfor %}
<input type="submit" value="提交">
</form>
- 修改示例
def edit_book(request, pk):
book_obj = models.Book.objects.filter(id=pk).first()
print("我是book_obj", book_obj)
# instance实例
form_obj = BookModelForm(instance=book_obj) # 实例化的form_obj
if request.method == "POST":
# 获取用户提交过来的数据,用request.POST传过来的数据去更新book_obj这本书
form_obj = BookModelForm(request.POST, instance=book_obj)
if form_obj.is_valid():
form_obj.save()
return redirect("/book_list/")
return render(request, "edit_book.html", locals())
3.3、练习
1 写一个注册功能(基本要求,用form表单提交)
2 校验name字段在数据库里有没有,有返回错误信息
3 应用bootstrap的样式
4 password这个input,type是密码形式
5 用forms的错误渲染功能
6 升级要求,用ajax提交数据
7 错误渲染也用dom操作,渲染
8 (可以不写)当用户名失去焦点,直接判断用户名在数据库是否存在
9 昨天的装饰器没写完的,敲一遍
10 没写完的(用dom操作,渲染table),继续写
- models.py
from django.db import models
# Create your models here.
class User(models.Model):
id = models.AutoField(primary_key=True)
user = models.CharField(max_length=10, unique=True)
pwd = models.CharField(max_length=16)
email = models.EmailField()
def __str__(self):
return self.user
3.3.1、1-5
- appfrom.py
```python
# coding:utf-8
#
from app02form import models
from django import forms
from django.core.exceptions import ValidationError
from app02form import models
class Myfrom(forms.Form):
user = forms.CharField(
max_length=12,
label="用户名",
min_length=5,
error_messages={"required": "不能为空"},
widget=forms.widgets.TextInput(attrs={"class": "form-control"}))
pwd = forms.CharField(max_length=12,
min_length=6,
label="密码:",
error_messages={"required": "不能为空"},
widget=forms.widgets.TextInput(attrs={"class": "form-control"}))
email = forms.EmailField(
label="邮箱",
error_messages={"required": "不能为空", "invalid": "格式不正确"},
widget=forms.widgets.TextInput(attrs={"class": "form-control"}))
def clean_user(self):
user = models.User.objects.filter(user=self.cleaned_data["user"]).first()
if user:
raise ValidationError("用户名已存在,请重新输入")
# 注意这里应该传的是 input框中的user, 而不是orm中的user
return self.cleaned_data["user"]
```
-
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {% load static %} <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.css' %}"> <script src="{% static 'jquery-3.3.1.js' %}"></script> <title>a2</title> <style> .error_size { position: absolute; right: 0px; font-size: 12px; color: red; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-5 col-md-offset-3"> <form action="{% url 'myreg' %}" method="post" novalidate> {% for foo in myfrom %} <p>{{ foo.label }} {{ foo }}</p> <span class="error_size">{{ foo.errors.0 }}</span> {% endfor %} <input type="submit" value="提交"> </form> </div> </div> </div> <script> setTimeout(function () { $('.error_size').html("") },3000) </script> </body> </html>
-
views.py
sfrom django.shortcuts import render, HttpResponse from app02form.appfrom import Myfrom from app02form import models # Create your views here. def myregister(request): myfrom = "" if request.method == "GET": myfrom = Myfrom() elif request.method == "POST": myfrom = Myfrom(request.POST) if myfrom.is_valid(): print(myfrom.cleaned_data) models.User.objects.create(**myfrom.cleaned_data) return HttpResponse("注册成功") # print(myfrom.errors.as_data()) return render(request, "a2.html", {"myfrom": myfrom})
错误在后端 直接用error_message = myfrom.errors.get_json_data()
3.3.2、6-10
- appfrom.py
```python
# coding:utf-8
#
from app02form import models
from django import forms
from django.core.exceptions import ValidationError
from app02form import models
class Myfrom(forms.Form):
user = forms.CharField(
max_length=12,
label="用户名",
min_length=5,
error_messages={"required": "不能为空"},
widget=forms.widgets.TextInput(attrs={"class": "form-control"}))
pwd = forms.CharField(max_length=12,
min_length=6,
label="密码:",
error_messages={"required": "不能为空"},
widget=forms.widgets.TextInput(attrs={"class": "form-control"}))
email = forms.EmailField(
label="邮箱",
error_messages={"required": "不能为空", "invalid": "格式不正确"},
widget=forms.widgets.TextInput(attrs={"class": "form-control"}))
# 钩子函数,需要注意的是 这里错误必须要返回 ValidationError, 以及返回的结果是 self.cleaned_data["user"], 而不是过滤之后的user
def clean_user(self):
user = models.User.objects.filter(user=self.cleaned_data["user"]).first()
print(user)
if user:
raise ValidationError("用户名已存在,请重新输入")
return self.cleaned_data["user"]
```
-
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {% load static %} <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.css' %}"> <script src="{% static 'jquery-3.3.1.js' %}"></script> <title>a3_ajax</title> <style> .error_size { position: absolute; right: 0px; font-size: 12px; color: red; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-5 col-md-offset-3"> {% for foo in myfrom %} <div class="form-group"> <p>{{ foo.label }} {{ foo }}</p> <span class="error_size">{{ foo.errors.0 }}</span> </div> {% endfor %} <button class="btn btn-success col-md-5 butn">提交</button> </div> </div> </div> <script> $(".butn").click(function () { var user = $("input[name='user']").val(); var pwd = $("input[name='pwd']").val(); var email = $("input[name='email']").val(); var dic = JSON.stringify({"user": user, "pwd": pwd, "email": email}); $.ajax({ url: "/myajax/", type: "post", contentType: "application/json", data: dic, success: function (data) { # 一般注册成功都是直接跳转到别的页面 if (data.status == 100 ){ alert("注册成功") }else { $.each(data, function (i, item) { # 这里是直接取的ID号 var error_id = $("#id_" + i); error_id.parent().siblings('span').html(item[0].message) }); # 设置超时时长为3秒 setTimeout(function () { $('.error_size').html("") }, 3000); } } }) }) </script> </body> </html>
-
views.py
from django.shortcuts import render, HttpResponse from app02form.appfrom import Myfrom from app02form import models import json from django.http import JsonResponse # 用于接受前端数据传过来之后的数据解析, 将body的数据转换成 字典 def get_data(func): def inner(request, *args, **kwargs): # 重新赋个值,post在前端也是空的状态 request.data = request.POST try: request.data = json.loads(request.body.decode('utf-8')) except Exception as E: print(E) ret = func(request, *args, **kwargs) return ret return inner @get_data def myajax(request): myfrom = "" error_message = "" if request.method == "GET": myfrom = Myfrom() elif request.method == "POST": myfrom = Myfrom(request.data) error_message = myfrom.errors.get_json_data() msg = {"status": 101, "message": None} if myfrom.is_valid(): msg['status'] = 100 msg["message"] = "注册成功" return JsonResponse(msg) return JsonResponse(error_message) return render(request, "a3ajax.html", {"myfrom": myfrom, "error_message": error_message})