python-django-06-django组件-2

浅谈CSRF

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攻击原理
    在这里插入图片描述

  • 攻击防护

    1. 验证 HTTP Referer 字段

    2. 在请求地址中添加 token 并验证 (加载请求的路径里,加载请求体中)

      ​ 在请求体中加上token值进行验证

    3. 在 HTTP 头中自定义属性并验证

      ​ 这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中

1.2、攻击防护验证

当django开启中间件:django.middleware.csrf.CsrfViewMiddleware时需要配置csrf字段

  1. 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)
    
  2. 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、登陆类方法
  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("登陆失败")
    
  2. 登陆成功之后的方法 (login)

    if res:	# 可以在登陆成功之后,添加 auth.login方法
        auth.login(request, res)
        # 相当于是 request.session["xx"] = "sessionid"
    
  3. 登陆成功之后的方法 (request.user)

    # 依赖于login, 如果没有登陆,那么它回显的就是 匿名用户 AnonymousUser
    def test_user(request):
        request.session.flush()
        user = request.user
        print(user.username, user)
        return HttpResponse("ok")
    
    user.is_authenticated   # 校验用户是否登陆
    
  4. 注销用户(logout)

def logout(request):
    auth.logout(request)
    # 相当于就是 request.session.flush()
    return redirect(reverse("login"))
  1. 登录认证装饰器

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、创建类方法
  1. 创建用户
def create_user(request):
    # 注意:  不能直接使用create方法,因为auth_user的密码是加过密的
    res = User.objects.create_user(username="xiong2", password="123")
	# 也可以创建超级用户,但是不建议 create_superuser
    res返回的就是创建的用户名称
  1. 检验密码
def check_user(request):
    # 检验当前用户密码是否是它
    res = request.user.check_password("123456")
    # res = User.check_password("123")	# 不能直接这么写,因为检验的是当前的用户
    return HttpResponse(res)
  1. 修改密码
def set_pass(request):
    res = request.user.set_password("123")
    request.user.save()		# 注意,修改完之后,一定要保存
    return HttpResponse(res)
  1. 其它方法
- is_active:禁止登录网站(用户还存在,封号)
- is_staff:是否对网站有管理权限(能不能登录admin)
2.2.3、扩展auth_user表
  1. 一对一关连表

    # 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
    
  2. 继承 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标签的样式
  1. 方式一, 不推荐使用

    # myform是由后端的forms生成的字典
    
    <form action="" method="post" novalidate>
        {{ myform.as_p }}
        <input type="submit" value="提交" class="btn btn-success">
    </form>
    
  2. 方式二, 如果过多的标签维护将会很复杂

    <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>
    
  3. 方式三, 推荐使用

    {% 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、示例
  1. 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"}),
        }
  1. 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
  1. 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})
  1. 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>
  1. 修改示例
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})
    
Python-Django-Vue项目实战是一种常见的全栈发模式,结合了PythonDjango框架和Vue.js前端框架。下面是一个简单的介绍: Python-Django是一个强大的Web开发框架,它使用Python语言编写,提供了一系列的工具和库来简化Web应用程序的开发过程。Django具有高度的可扩展性和灵活性,可以帮助开发者快速构建功能丰富的Web应用。 Vue.js是一个流行的JavaScript前端框架,它专注于构建用户界面。Vue.js具有简单易学的语法和强大的功能,可以帮助开发者构建交互性强、响应迅速的前端应用。 在Python-Django-Vue项目实战中,通常会将Django作为后端框架来处理数据逻辑和业务逻辑,而Vue.js则负责前端页面的展示和用户交互。通过这种方式,可以实现前后端分离,提高开发效率和代码可维护性。 具体的项目实战可以包括以下内容: 1. 构建Django后端:使用Django框架创建后端应用程序,包括定义数据模型、编写视图函数、配置URL路由等。 2. 开发Vue前端:使用Vue.js框架创建前端应用程序,包括设计页面布局、编写组件、处理用户交互等。 3. 数据交互:通过RESTful API或GraphQL等方式,实现前后端数据的交互和通信。 4. 用户认证和权限管理:实现用户注册、登录、权限验证等功能,确保系统的安全性。 5. 数据库操作:使用Django的ORM(对象关系映射)来进行数据库操作,包括增删改查等。 6. 页面美化和响应式设计:使用CSS和Vue.js的样式绑定功能,实现页面的美化和响应式设计。 7. 部署和发布:将项目部署到服务器上,并进行性能优化和安全加固。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值