DjangoAndRestFramework

第1章

1.自己开发web框架

  • web种类:

    • 第一种,帮你把下面所有的事都做了。

      • Tornado:做了所有的事。

    • 第二种,不办你做socket的事,再的都帮你做了。

      • wsgiref:做socket服务端。

      • Django:做剩下的事。

    • 第三种,只帮你判断文件的选择,再的都不管。

      • flask:只做判断文件的选择

    • 如果要写大的网站就用Django,因为他有很多的功能。

1.1 服务端

import socket
​
def f1(request):
    f = open("index - 副本.html","rb")
    data = f.read()
    f.close()
    
    return data
def f2(request):
    f = open("aricle.txt","r",encoding='utf-8')
    data = f.read()
    f.close()
    import time
    ctime = time.time()
    data = data.replace("用户名",str(int(ctime)),11)
    #replace找到字符并替换,第三个参数是替换的最大数
    return bytes(data,encoding="utf-8")
​
def f3(request):
    import pymysql
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="优惠记录")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("select user_id,name,telephone_number from user_table")
    user_list = cursor.fetchall()
    cursor.close()
    conn.close()
    
    content_list = []
    for row in user_list:
        tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row["user_id"],row["name"],row["telephone_number"])
        content_list.append(tp)
    content = "".join(content_list)
​
    f = open("aricle.txt","r",encoding="utf-8")
    template = f.read()
    f.close()
​
    data = template.replace("用户名",content)
    return bytes(data,encoding="utf-8")
​
def f4(request):
    import pymysql
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="优惠记录")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("select user_id,name,telephone_number from user_table")
    user_list = cursor.fetchall()
    cursor.close()
    conn.close()
    
     f = open("aricle.txt","r",encoding="utf-8")
    data = f.read()
    f.close()
    #这是个渲染模版的模块需要下载
    from jinja2 import Template
    template = Template(data)
    data = template.render(asd=user_list)#渲染
    #对应要在模版中写相应的代码
    """
    {% for row in asd %}
        <tr>
            <td>{{row.user_id}}</td>
            <td>{{row.name}}</td>
            <td>{{row.telephone_number}}</td>
        </tr>
    {% endfor%}
    
    {{user}}这样写就会把数据替换了
    """
​
fouters = [("/xxx",f1),("/ooo",f2),("/hhh",f3),("/sss",f4)]
​
def run():
    sock = socket.socket()
    sock.bind(("127.0.0.1",8080))
    sock.listen(5)
    while True:
        conn,addr = sock.accept()
        data = conn.recv(8090)
        data = str(data,encoding="utf-8")#转换为字符
        #bytes("asdf",encoding="utf-8")#转换为字节
        print(bool(data))
        if bool(data):
            headers,bodys = data.split("\r\n\r\n")
            temp_list = headers.split("\r\n")
            print(temp_list)
            method,url,protocal = temp_list[0].split(" ")
            conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
            routers = fouters
            func_name = None
            for item in routers:
                if item[0] == url:
                    func_name = item[1]
                    break
            
            if func_name:
                response = func_name(data)
            else:
                response = b"404"
            conn.send(response)
            conn.close()
        
run()
​
#服务器得到的数据,这是文件头
#如果在127.0.0.1:8080后面加上/index?p=123他会直接在文件头中
#'GET/index?p=123 HTTP/1.1
#是/index数据会到文件体重
'GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
​
#这是文件尾 中间会有\r\n\r\n
'

1.2 html文件

<!--(index.html)-->
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>用户登录</h1>
        <form>
            <p><input type="text" placeholder="用户名" /></p>
            <p><input type="password" placeholder="密码" /></p>
        </form>
    </body>
</html>
<!--aricle.html-->
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <form>
            <p>用户名</p>
            <p>密码"</p>
        </form>
    </body>
</html>

1.3 http协议

  • http称为超文本传输协议,它是无状态、无连接的只会在你访问的时候链接将数据传输完后迅速断开。

2.Django框架

2.1 下载Django和创建网站

  • pip3 install django#下载
    ​
    django-admin startproject mysite#创建网站,需要加路径django_admin不会给你自动加路径。mysite是文件夹名称
    cd mysite#进入网站文件夹
    python manage.py runserver 127.0.0.1:8080#运行并设置端口监听,后面如果不加的话默认:127.0.0.1:8000

2.2 文件目录

  • mysite

    • mysite

      • settings.py #Django配置文件

        TEMPLATES = [
            {
                "DIRS":[os.path.join(BASE_DIR,"template")]#BASE_DIR代表最外层的mysite文件夹,对应的字符串中是要找到文件夹名
            }
        ]
        ​
        MIDDLEWARE = [
            'django.middleware.security.SecurityMiddleware',
            'django.contrib.sessions.middleware.SessionMiddleware',
            'django.middleware.common.CommonMiddleware',
            #'django.middleware.csrf.CsrfViewMiddleware',
            'django.contrib.auth.middleware.AuthenticationMiddleware',
            'django.contrib.messages.middleware.MessageMiddleware',
            'django.middleware.clickjacking.XFrameOptionsMiddleware',
        ]#在这个中把'django.middleware.csrf.CsrfViewMiddleware',注释掉
        ​
        STATIC_URL = '/static/'#找到这个,在这个的下面写
        ​
        STATICFILES_DIRS =os.path.join(BASE_DIR,"static"),#这是配置静态文件的
        ​
        #文件名后必须加“,”不加的话会报错

         

      • url.py #路由系统 :url的对应函数

        from django.contrib import admin
        from django.urls import path
        ​
        from django.shortcuts import HttpResponse,render,redirect#这个模块
        def login(request):
            """
            处理用户请求,并返回内容
            :param request:用户请求相关的所有信息(对象)
            :retrun:
            """
            #render也需要导入,需要在settings中配置一下不然找不到你的文件夹,配置好后写入文件名称就可以了。
            #return HttpResponse("login")#如果你要输出什么需要使用模块
            #method判断它是什么形式
            #数据提交方式
                #你用get提交数据request.get是可以拿到数据的,request.post是拿不到数据的,你用post提交数据时request.post是可以拿到数据的,用.request.get也是可以的。因为get是将数据追在url尾部的,而post是将数据放到请求体中的,但是也可以追加到url后边。后边会做示例。
            if request.method == "GET":
                return render(request,"login.html")
            else:
                #用户POST提交时的数据(请求体)
                u=request.POST.get("user")
                p=request.POST.get("pwd")
                if u == "root" and p == "123123":
                  #redirect是跳转页面,需要导入
                    #return redirect("http://www.oldboyedu.com")
                    return redirect("/index/")#如果想要转到自己的网页,可以直接写网页名,不用加ip和端口
                else:
                  return render(request,"login.html",{"msg":"登录失败"})#这是对应的在这个里面加了它,模版里面也需要加相应的东西如:“{{msg}}”
        def index(request):
            return render(request,"index.html",{"name":"学习","users":["你好","hello world!"],"user_dict":{"k1":"v1","k2":"v2"},
                                               "user_list_dict":[{
                                                   "id":1,"name":"alex"},{"id":2,"name":"alex2"}]})
        urlpatterns = [
              path('admin/', admin.site.urls),
            #path(r"^名字/",函数名), #添加名和函数的关系
              path(r"login/",login),
              path(r"index/",index),
        ]
  • wsgi.py #用于定义Django用socket,wsgirgf(用于个人测试),uwsgi

  • manage.py#对当前Django程序所有操作可以基于 Python manage.py runserver

    • 自定的文件夹

      • static

      • template

        <!--login-->
        <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title></title>
            <link rel="stylesheet" href="/static/commons.css"><!--导入文件时需要配置,文件的前缀需要改为“static”不然找不到-->
          </head>
            <body>
                <h1>用户登录</h1>
                <!--以post形式访问login-->
                <!--method设置形式,action是提交login-->
                <form method="POST" action="/login/">
                    <input type="text" name="user" />
                    <input type="password" name="pwd" />
                    <input type="submit" value="登录"/>
                    {{msg}}
                </form>
        </body>
        </html>
        <!--index-->
        <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title></title>
          </head>
            <body>
                <h1>模板标记学习</h1>
                <p>{{name}}</p>
                <p>{{users.0}}</p>
                <p>{{users.1}}</p>
                <!--users返回的是列表,.0/.1是索引-->
                <p>{{user_dict.k1}}</p>
                <p>{{user_dict.k2}}</p>
                <!--user返回的是字典,.k1/.k2是用建取值-->
                <h3>循环</h3>
                <ul>
                    {% for item in users %}
                        <li>{{item}}</li>
                    {% endfor %}
                </ul>
                <table border="1">
                    {% for row in user_list_dict %}
                    <tr>
                        <td>{{row.id}}</td>
                        <td>{{row.name}}</td>
                        <td>
                            <a>编辑</a> | <a href="/del/?nid={{row.id}}">删除</a>
                        </td>
                    </tr>
                    {% endfor %}
                </table>
        </body>
        </html>

第2章

1.概述

  • 接受请求并返回

    • 普通返回有响应头和响应体组成。客户端接收到响应后直接显示。

    • 重定向返回只有响应头其中更新时,可以通过"LOCSTION:”http://www.baidu.com“",来指定更新到的页面。客户端接收到响应后再次发出一次请求(指定请求)。

  • 渲染

    {%for i in 值%}    //循环
        {{语句组}}
    {%endfor%}
    ​
    {%if 条件%}        //选择
        {{语句组}}
    {%else%}
        {{语句组}}
    {%endif%}
    ​
    {{值.索引数}}      //索引
    ​
    {{值}}            //取值
  • Ajax(jQuery)

    //提交并不更新网页
    $.jajx({
        url:""    //提交的路劲
        type:"GET/POST"     //提交的方式
        data:{k1:"v1"}      //提交的数据
        success:function(arg){  //返回的数据
    ​
        }
    })
  • js阻止默认事件的发生

    <a href = "www.baidu.com" onclick="return modelEdit();">编辑</a>
    <script>
        function modelEdit(){
            alert(1234)
            return False;   //true是执行默认事件,反之不执行
        }
    </script>
    //jQuery也可以阻止默认事件发生
    <a href = "www.baidu.com" id="addModal">编辑</a>
    <script src = "jQuery文件夹加文件名"></script>
    <script>
        //框架加载完成后自动触发
        $(function(){
            //找到addModal标签点击后触发,click点击触发
            $("#addModal").click(function(){
                alert(123);
                return false;
            })
        })
        
    </script>
  • 获取当前标签

    <a onclick=" modelEdit(this);">编辑</a>
    <script>
        function modelEdit(ths){
             //parent() 找到当前标签的父标签
            //prevAll() 找到父标签上面所有的标签
             //现在这就是点击了事件的当前标签
            $(ths).parent().prevAll();
            
        }
    </script>
  • Http请求生命周期

    • 请求头中提取url,在进行路由匹配,匹配成功执行函数(模版+数据渲染)然后返回用户(响应头加响应体)

  • 渲染

    • 渲染是在后台执行的,使用Django的替换在script中需要加定界符。

  •  

2.mysite

2.1app01

views.py

from django.shortcuts import render,redirect,HttpResponse
from utils import sqlheper
import pymysql
import json
def classes(request):
​
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("select id,class_name from class")
    class_list = cursor.fetchall()
    cursor.close()
    conn.close()
​
    return render(request,"classes.html",{"classes":class_list})
    
def add_class(request):
    if request.method == "GET":
        print(request.method)
        return render(request,"add_class.html")
    else:
        print(request.POST)
        v = request.POST.get("title")
        if len(v)>0:
            conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
            cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
            cursor.execute("insert into class(class_name) values(%s)",[v,])
            conn.commit()
            cursor.close()
            conn.close()
            return redirect("/classes")
        else:
            return render(request,"add_class.html",{"mag":"不能为空"})
        
def del_class(request):
    nid = request.GET.get("nid")
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("delete from class where id =%s",[nid,])
    conn.commit()
    cursor.close()
    conn.close()
    return redirect("/classes")
​
def edit_class(request):
    if request.method == "GET":
        edit = request.GET.get("nid")
        conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,class_name from class where id =%s",[edit,])
        result = cursor.fetchone()
        cursor.close()
        conn.close()
        return render(request,"edit_class.html",{"edit_class":result})
    else:
        id = request.GET.get("nid")
        title = request.POST.get("edit")
        conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("update class set class_name = %s where id =%s",[title,id])
        conn.commit()
        cursor.close()
        conn.close()
        return redirect("/classes")
​
​
def teacher(request):
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("""select teacher.id,teacher.teacher_name,class.class_name from teacher 
        left join teacher_class_id on teacher.id = teacher_class_id.teacher_id 
        left join class on teacher_class_id.class_id = class.id """)
    teacher_list = cursor.fetchall()
    cursor.close()
    conn.close()
    result = {}
    for i in teacher_list:
        tid = i["id"]
        if tid in result:
            result[tid]["class_name"].append(i["class_name"])
        else:
            result[tid]={"id":i["id"],"teacher_name":i["teacher_name"],"class_name":[i["class_name"]]}
​
            
​
    return render(request,"teacher.html",{"teacher":result.values()})
    
def add_teacher(request):
    if request.method == "GET":
        class_list = sqlheper.get_list("select id,class_name from class ",[])
        return render(request,"add_teacher.html",{"class_list":class_list})
    else:
        title = request.POST.get("title")
        #返回值是添加属性的自增列的对应id
        teacher_id=sqlheper.create("insert into teacher(teacher_name)values(%s)",[title,])
        class_id = request.POST.getlist("class_ids")
        #request.POST.getlist('xx')
        #如果取多选的东西就用这个,取同一个名字下的多个值。
        #多次链接,多次提交
        #for cls_id in class_ids:
        #    sqlheper.modify("insert into teacher_class_id(teacher_id,class_id)values(%s,%s)",[teacher_id,cls_id,])
        #一次链接,多次提交
        #obj = sqlheper.SqlHelper()
        #for cls_id in class_ids:
        #    obj.modify("insert into teacher_class_id(teacher_id,class_id)values(%s,%s)",[teacher_id,cls_id,])
        #obj.close()
        #一次链接,一次提交
        data_list = [(teacher_id,cls_id) for cls_id in class_id]
        obj = sqlheper.SqlHeper()
        obj.multiple_modify("insert into teacher_class_id(teacher_id,class_id)values(%s,%s)",data_list)
        obj.close()
        return redirect("/teacher")
        
def del_teacher(request):
    nid = request.GET.get("nid")
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("delete from teacher where id =%s",[nid,])
    conn.commit()
    cursor.close()
    conn.close()
    return redirect("/teacher")
​
def edit_teacher(request):
    if request.method == "GET":
        nid = request.GET.get("nid")
        obj = sqlheper.SqlHeper()
        teacher_info=obj.get_one("select id,teacher_name from teacher where id =%s",[nid,])
        class_id_list = obj.get_list("select class_id from teacher_class_id where teacher_id =%s",[nid,])
        class_list = obj.get_list("select id, class_name from class",[])
        obj.close()
        classs = []
        for i in class_id_list:
            classs.append(i["class_id"])
        return render(request,"edit_teacher.html",{"teacher_info":teacher_info,"class_id_list":classs,"class_list":class_list})
    else:
        teacher_id = request.GET.get("nid")
        name = request.POST.get("edit")
        class_ids = request.POST.getlist("class_ids")
        obj = sqlheper.SqlHeper()
        obj.modify("update teacher set teacher_name = %s where id = %s",[name,teacher_id])
        obj.modify("delete from teacher_class_id where teacher_id = %s",[teacher_id])
        data_list = [(teacher_id,cls_id) for cls_id in class_ids]
        obj = sqlheper.SqlHeper()
        obj.multiple_modify("insert into teacher_class_id(teacher_id,class_id)values(%s,%s)",data_list)
        obj.close()
        return redirect("/teacher")
​
def students(request):
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("select students.id,students.students_name,class.id as class_id,class.class_name from students left join class on students.class_id = class.id")
    students_list = cursor.fetchall()
    cursor.close()
    conn.close()
​
    class_list = sqlheper.get_list("select id,class_name from class",[])
    return render(request,"students.html",{"students_list":students_list,"class_list":class_list})
​
def add_students(request):
    if request.method == "GET":
        conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,class_name from class")
        class_list = cursor.fetchall()
        cursor.close()
        conn.close()
​
        return render(request,"add_students.html",{"class_list":class_list})
    else:
        name = request.POST.get("title")
        class_id = request.POST.get("class_id")
        print(name,class_id)
        conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("insert into students(students_name,class_id) values(%s,%s)",[name,class_id])
        conn.commit()
        cursor.close()
        conn.close()
        return redirect("/students")
​
def edit_students(request):
    if request.method == "GET":
        id = request.GET.get("nid")
        students_list = sqlheper.get_one("select id, students_name,class_id from students where id = %s",id)
        class_list = sqlheper.get_list("select id,class_name from class ",[])
        return render(request,"edit_students.html", {"class_list":class_list,"students_list":students_list})
    else:
        id = request.GET.get("nid")
        name = request.POST.get("title")
        class_id = request.POST.get("class_id")
        sqlheper.modify("update students set students_name = %s,class_id = %s where id = %s",[name,class_id,id])
        return redirect("/students")
​
def del_students(request):
    nid = request.GET.get("nid")
    sqlheper.modify("delete from students where id =%s",[nid,])
    return redirect("/students")
​
​
# ############################## 对话框 ################################
​
def modal_add_class (request):
    title = request.POST.get("title")
    if len(title)>0:
        sqlheper.modify("insert into class(class_name) values(%s)",[title,])
        return HttpResponse("ok")
    else:
        return HttpResponse("不能为空")
​
def modal_edit_class (request):
    ret = {"status":True,"message":None}
    try:
        cid = request.POST.get("cid")
        title = request.POST.get("title")
        sqlheper.modify("update class set class_name = %s where id = %s",[title,cid,])
    except Exception as e:
        ret["status"] = False
        ret["message"] = "异常处理"
​
    return HttpResponse(json.dumps(ret))
​
def modal_add_student(request):
    ret = {"status":True,"message":None}
    try:
        class_id = request.POST.get("classId")
        name = request.POST.get("name")
        sqlheper.modify("insert into students(students_name,class_id) values(%s,%s)",[name,class_id,])
    except Exception as e:
        ret["status"] = False
        ret["message"] = str(e)
    return HttpResponse(json.dumps(ret))
​
def modal_edit_student(request):
    ret = {"status":True,"message":None}
    try:
        class_id = request.POST.get("classId")
        student_id = request.POST.get("studentsId")
        name = request.POST.get("name")
        print(class_id,student_id,name)
        sqlheper.modify("update students set students_name = %s,class_id=%s  where id=%s",[name,class_id,student_id,])
    except Exception as e:
        ret["status"] = False
        ret["message"] = str(e)
    return HttpResponse(json.dumps(ret))
​
def btn_modal_add_teacher(request):
    obj = sqlheper.SqlHeper()
    class_list = obj.get_list("select id,class_name from class",[])
    obj.close()
    import time
    time.sleep(5)
    return HttpResponse(json.dumps(class_list))
def modal_add_teacher(request):
    ret = {"status":True,"message":None}
    try:
        name = request.POST.get("name")
        teacher_id=sqlheper.create("insert into teacher(teacher_name)values(%s)",[name,])
        class_id = request.POST.getlist("classIds")
        data_list = [(teacher_id,cls_id) for cls_id in class_id]
        obj = sqlheper.SqlHeper()
        obj.multiple_modify("insert into teacher_class_id(teacher_id,class_id)values(%s,%s)",data_list)
        obj.close()
    except Exception as e:
        ret["status"] = False
        ret["message"] = str(e)
    return HttpResponse(json.dumps(ret))
    
    
​

2.2utils

sqlheper.py

import pymysql
​
def get_list(sql,args):
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute(sql,args)
    result = cursor.fetchall()
    cursor.close()
    conn.close()
    return result
    
def get_one(sql,args):
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute(sql,args)
    result = cursor.fetchone()
    cursor.close()
    conn.close()
    return result
​
def modify(sql,args):
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute(sql,args)
    conn.commit()
    cursor.close()
    conn.close()
    
def create(sql,args):
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute(sql,args)
    conn.commit()
    last_row_id = cursor.lastrowid
    cursor.close()
    conn.close()
    return last_row_id
    
class SqlHeper():
    def __init__(self):
        self.connect()
​
    def connect(self):
        self.conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
        self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
    
    def get_list(self,sql,args):
        self.cursor.execute(sql,args)
        result = self.cursor.fetchall()
        return result
        
    def get_one(self,sql,args):
        self.cursor.execute(sql,args)
        result = self.cursor.fetchone()
        return result
    
    def modify(self,sql,args):
        self.cursor.execute(sql,args)
        self.conn.commit()
    def multiple_modify(self,sql,args):
        #sql = insert into bd(id,name)values(1,"alex"),args=[(1,"alex"),(2,"xxx")]
        #self.cursor.executemany(sql,args) 
        self.cursor.executemany(sql,args)
        self.conn.commit()
    
    def create(self,sql,args):
        self.cursor.execute(sql,args)
        self.conn.commit()
        return self.cursor.lastrowid #拿到添加后的对应自增id值
        
    def close(self):
        self.cursor.close()
        self.conn.close()

2.3mysite

settings.py

from pathlib import Path
import os
​
​
​
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    #'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
​
ROOT_URLCONF = 'mysite.urls'
​
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,"templates")],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
​
STATIC_URL = '/static/'
​
STATICFILES_DIRS =os.path.join(BASE_DIR,"static"),
​

urls.py

"""mysite URL Configuration
​
The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from app01 import views
    
urlpatterns = [
    path('admin/', admin.site.urls),
    path(r"classes/",views.classes),
    path(r"add_class/",views.add_class),
    path(r"del_class/",views.del_class),
    path(r"edit_class/",views.edit_class),
    path(r"teacher/",views.teacher),
    path(r"add_teacher/",views.add_teacher),
    path(r"del_teacher/",views.del_teacher),
    path(r"edit_teacher/",views.edit_teacher),
    path(r"students/",views.students),
    path(r"add_students/",views.add_students),
    path(r"edit_students/",views.edit_students),
    path(r"del_students/",views.del_students),
    path(r"modal_add_class/",views.modal_add_class),
    path(r"modal_edit_class/",views.modal_edit_class),
    path(r"modal_add_student/",views.modal_add_student),
    path(r"modal_edit_student/",views.modal_edit_student),
    path(r"modal_add_teacher/",views.modal_add_teacher),
    path(r"btn_modal_add_teacher/",views.btn_modal_add_teacher),
]
​
​

2.4static

jquery-3.6.0.min.js

2.4.1plugins

2.4.2images

2.5templates

add_class.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
	<h1>添加班级</h1>
	<form method="POST" action="/add_class/">
		<p>班级名称:<input type="text" name="title"/></p>
		<input type="submit" value="提交">{{mag}}	
	</form>
</body>
</html>

classes.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
	<link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1-dist/css/bootstrap.css">
	<link rel="stylesheet" href="/static/plugins/font-awesome-4.7.0/css/font-awesome.css">
    <style>
        .hide{
            display: none;
        }
        .shadow{
            position:fixed;
            left:0;
            top:0;
            right:0;
            bottom:0;
            background-color:black;
            opacity:0.4;
            z-index:999;   
        }
        .modal{
            z-index:1000;
            position:fixed;
            left:50%;
            top:50%;
            height:300px;
            width:400px;
            background-color:white;
            margin-left:-200px;
            margin-top:-150px;
        }
    </style>
</head>
<body>
	<div style="margin:10px;">
		<h1>班级列表</h1>
		<div>
			<a class="btn btn-primary" href = "/add_class/">添加</a>
			<a class="btn btn-info" onclick = "ShowModal();">对话框添加 </a>
		</div>
		<table class="table">
			<thead>
				<tr>
				<th>ID</th>
				<th>班级名称</th>
				<th>功能</th>
				</tr>
		</thead>
	</div>
	<tbody>
			{% for row in classes %}
				<tr>
					<td>{{row.id}}</td>
					<td>{{row.class_name}}</td>
					<td>
						<a href = "/edit_class/?nid={{row.id}}">编辑</a>
						<a onclick="modelEdit(this);">对话框编辑</a>
						<a href = "/del_class/?nid={{row.id}}">删除</a>
					</td>
				</tr>
			{% endfor %}
	</tbody>
	</table>
	<div id="shadow" class="shadow hide"></div>
	<div id="modal" class="modal hide">
		<p>
			<input id="title" type="text" name="title" />
		</p>
		<input type="button" value="提交" onclick="AjaxSend();"/><span id="errormsg"></span>
		<input type="button" value="取消" onclick="cancleModal();"/>
	</div>
	<!--############-->
	<div id="editModal" class="modal hide">
		<p>
			<input id="editid" type="text" name="id" style="display:none;"/>
			<input id="editTitle" type="text" name="title"/>
		</p>
		<input type="button" value="提交" onclick="editAjaxadd();"/><span id="editerrormsg"></span>
		<input type="button" value="取消" onclick="cancleModel();"/>
	</div>
	
	<script src="/static/jquery-3.6.0.min.js"></script>
	<script>
		function ShowModal(){
		
			document.getElementById("shadow").classList.remove("hide");
			document.getElementById("modal").classList.remove("hide");
		}
		function cancleModal(){
		
			document.getElementById("shadow").classList.add("hide");
			document.getElementById("modal").classList.add("hide");
		}
		
		function AjaxSend(){
			$.ajax({
				url:"/modal_add_class/",   //将数据通过post提交到这里
				type:"POST",    //以什么方式提交
				data:{"title":$("#title").val()},
				success:function(data){
					//当服务端处理完成后,返回数据时,该函数自动调用
					//data=服务端返回的值
					if(data == "ok"){
						location.href="/classes/";
					}else{
						$("#errormsg").text(data);
					}
					
				},
			})
		}
		function modelEdit(ths){
			document.getElementById("shadow").classList.remove("hide");
			document.getElementById("editModal").classList.remove("hide");
			//parent() 找到当前标签的父标签
			//prevAll() 找到父标签上面所有的标签
			var row = $(ths).parent().prevAll();
			$("#editTitle").val($(row[0]).text());    //id 查找并把之前查找的赋值给,刚查找的标签
			$("#editid").val($(row[1]).text());    //id 查找并把之前查找的赋值给,刚查找的标签
		}
		
		function cancleModel(){
		
			document.getElementById("shadow").classList.add("hide");
			document.getElementById("editModal").classList.add("hide");
		}
		function editAjaxadd(){
			$.ajax({
				url:"/modal_edit_class/",   //将数据通过post提交到这里
				type:"POST",    //以什么方式提交
				data:{"title":$("#editTitle").val(),"cid":$("#editid").val()},
				success:function(arg){
					//当服务端处理完成后,返回数据时,该函数自动调用
					//data=服务端返回的值
					//JSON.parse(字符串)    是将字符串转为对象
					//JSON.stringify(对象)     是将对象转为字符串
					//python中的字典在ajax称为ajax对象如果条调用它“对象.建”
					arg = JSON.parse(arg);
					if(arg.status){
						location.reload();   //如果是当前页面这样写直接刷新
					}else{
						alert(arg.message);
					}
				},
			})
		}
	</script>
</body>
</html>

edit_class.html

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title></title>
	</head>
	<body>
		<h1>编辑班级</h1>
		<form method="POST" action="/edit_class/?nid={{edit_class.id}}">
			<p>修改名称:<input type="text" name="edit" value="{{edit_class.class_name}}" /></p>
			<input type="submit" value="提交">	
		</form>
	</body>
		
</html>

teacher.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
	<style>
		.hide{
			display:none;
		}
		.shadow{
			/*fixed是相对于窗口固定的*/
			position:fixed;
			left:0;
			top:0;
			right:0;
			bottom:0;
			background-color:black;
			opacity:0.4;
			z-index:999;
		}
		.loading{
			position:fixed;
			top:50%;
			left:50%;
			width:600px;
			height:600px;
			margin-left:-300px;
			margin-top:-300px;
			background-image:url("/static/images/loader.gif");
		}
		.add-Modal{
			position:fixed;
			top:50%;
			left:50%;
			width:400px;
			height:300px;
			z-index:1000;
			margin-left:-200px;
			margin-top:-200px;
			background-color:white;
		}
	
	</style>
</head>
<body>
    <h1>老师列表</h1>
    <div>
	<a href = "/add_teacher/">添加</a>
	<a id = "btn_add_teacher">对话框添加</a>
    </div>
    <table border="1">
		<thead>
			<tr>
			<th>ID</th>
			<th>老师名称</th>
			<th>班级名称</th>
			<th>功能</th>
			</tr>
		</thead>
		<tbody>
			{% for row in teacher %}
				<tr>
					<td>{{row.id}}</td>
					<td>{{row.teacher_name}}</td>
					<td>
					{%for i in row.class_name%}
						<span>{{i}}</span>
					{%endfor%}
						</td>
					<td>
						<a href = "/edit_teacher/?nid={{row.id}}">编辑</a>
						<a href = "/del_teacher/?nid={{row.id}}">删除</a>
					</td>
				</tr>
			{% endfor %}
		</tbody>
    </table>
	<div id="shadow" class="shadow hide"></div>
	<div id="loading" class="loading hide"></div>
	<div id="addModal" class="add-Modal hide">
		<p>
			<!--placeholder在输入框内部显示-->
			姓名:<input id="className" type="text" name="name" placeholder="姓名">
		</p>
		<p>
			班级:
			<select id="classIds" name="classId" multiple size="10"></select>
		</p>
		<input id="btnAdd" type="button" value="添加">
		<input id="cancleModel" type="button" value="取消">
	</div>
	<script src="/static/jquery-3.6.0.min.js"></script>
	<script>
		$(function(){
			btn_add_teacher();
			btnAdd();
		})
		function btn_add_teacher(){
			$("#btn_add_teacher").click(function(){
				$("#shadow,#loading").removeClass("hide");
			
				$.ajax({
					url:"/btn_modal_add_teacher/",
					type:"GET",
					dataType:"JSON",
					success:function(arg){
						//each是循环,i是索引,row是值
						$.each(arg,function(i,row){
							//document.createElement是创建标签
							var tag = document.createElement("option");
							//给标签添加内容
							tag.innerHTML = row.class_name;
							//给标签添加属性
							tag.setAttribute("value",row.id);
							//把标签添加到某个位置
							//判断一个值是否在数组中用【12,23,34】indexOf("23")返回索引1,不在返回-1
							$("#classIds").append(tag);
							$("#loading").addClass("hide");
							$("#addModal").removeClass("hide");
						 
						})
					}
				
				})
				
			})
		}
		function btnAdd(){
			$("#btnAdd").click(function(){
				$.ajax({
					url:"/modal_add_teacher/",
					type:"POST",
					data:{"name":$("#className").val(),"classIds":$("#classIds").val()},
					traditional:true, //当提交的数据中有列表时,会自动处理而加上它就不会处理;
					dataType:"JSON",
					success:function(arg){
						if(arg.status){
							location.reload();   //如果是当前页面这样写直接刷新
						}else{
							$("#addError").text(arg.message);
						}
					}
				
				})
			})
		}
			
	</script>
</body>
</html>

all_teacher.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
	<h1>老师班级</h1>
	<form method="POST" action="/add_teacher/">
		<p>老师名称:<input type="text" name="title"/></p>
		<input type="submit" value="提交">	
	</form>
</body>
</html>

edit_teacher.html

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title></title>
	</head>
	<body>
		<h1>编辑班级</h1>
		<form method="POST" action="/edit_teacher/?nid={{edit_teacher.id}}">
			<p>修改名称:<input type="text" name="edit" value="{{edit_teacher.teacher_name}}" /></p>
			<input type="submit" value="提交">	
		</form>
	</body>
		
</html>

students.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
	<style>
		.hide{
			display:none;
		}
		.shadow{
			/*fixed是相对于窗口固定的*/
			position:fixed;
			left:0;
			top:0;
			right:0;
			bottom:0;
			background-color:black;
			opacity:0.4;
			z-index:999;
		}
		
		.add-Modal{
			position:fixed;
			top:50%;
			left:50%;
			width:400px;
			height:300px;
			z-index:1000;
			margin-left:-200px;
			margin-top:-200px;
			background-color:white;
		}
	
	</style>
</head>
<body>
    <h1>学生列表</h1>
    <div>
	<a href = "/add_students/">添加</a>
	<a id="add-Modal">对话框添加</a>
    </div>
    <table border="1">
		<thead>
			<tr>
			<th>ID</th>
			<th>学生姓名</th>
			<th>班级名称</th>
			<th>功能</th>
			</tr>
		</thead>
		<tbody>
			{% for row in students_list %}
				<tr>
					<td>{{row.id}}</td>
					<td>{{row.students_name}}</td>
					<td clsid={{row.class_id}}>{{row.class_name}}</td>
					<td>
						<a href = "/edit_students/?nid={{row.id}}">编辑</a>
						<a class="btn-edit">对话框编辑</a>
						<a href = "/del_students/?nid={{row.id}}">删除</a>
					</td>
				</tr>
			{% endfor %}
		</tbody>
    </table>
	<div id="shadow" class="shadow hide"></div>
	<div id="addModal" class="add-Modal hide">
		<p>
			<!--placeholder在输入框内部显示-->
			姓名:<input id="className" type="text" name="name" placeholder="姓名">
		</p>
		<p>
			班级:
			<select id="classId" name="classId">
				{%for i in class_list%}
					<option value="{{i.id}}">{{i.class_name}}</option>
				{%endfor%}
			</select>
		</p>
		<input id="btnAdd" type="button" value="添加">
		<input id="cancleModel" type="button" value="取消">
		
		<span id="addError" style="color:red;"></span>
	</div>
	<!--#####################-->
	<div id="editModal" class="add-Modal hide">
		<p>
			<!--placeholder在输入框内部显示-->
			姓名:<input id="studentsName" type="text" name="name" >
			<input id="studentsId" class="hide" type="text" name="name" >
			
		</p>
		<p>
			班级:
			<select id="editclassId" name="classId">
				{%for i in class_list%}
					<option value="{{i.id}}">{{i.class_name}}</option>
				{%endfor%}
			</select>
		</p>
		<input id="btnedit" type="button" value="添加">
		<input id="cancleModel" type="button" value="取消">
		
		<span id="editError" style="color:red;"></span>
	</div>
	
	<script src="/static/jquery-3.6.0.min.js"></script>
	<script>
		$(function(){
			$("#className").val($("#students_name").val())
			
			$("#add-Modal").click(function(){
				$("#shadow,#addModal").removeClass("hide");
			})
			
			$("#cancleModel").click(function(){
			
				$("#shadow,#addModal").addClass("hide");
			})
			
			$("#btnAdd").click(function(){
			
				$.ajax({
					url:"/modal_add_student/",
					type:"POST",
					data:{"name":$("#className").val(),"classId":$("#classId").val()},
					success:function(arg){
						arg = JSON.parse(arg);
						if(arg.status){
							location.reload();   //如果是当前页面这样写直接刷新
						}else{
							$("#addError").text(arg.message);
						}
					}
				
				})
			
			})
			
			$(".btn-edit").click(function(){
				$("#shadow,#editModal").removeClass("hide");
				/*1.$(this)就代表当前标签*/
				var tds = $(this).parent().prevAll();
				var studentId = $(tds[2]).text();
				var studentName = $(tds[1]).text();
				var classId = $(tds[0]).attr("clsid");
				/*attr获取标签自定的属性值*/
				console.log(studentId,studentName,classId);
				$("#studentsName").val(studentName);
				$("#editclassId").val(classId);
				$("#studentsId").val(studentId);
				
			})
			$("#btnedit").click(function(){
			
				$.ajax({
					url:"/modal_edit_student/",
					type:"POST",
					data:{"studentsId":$("#studentsId").val(),"name":$("#studentsName").val(),"classId":$("#editclassId").val()},
					dataType:"JSON",  //它可以在内部直接转为对象
					success:function(arg){
						//arg = JSON.parse(arg);
						if(arg.status){
							location.reload();   //如果是当前页面这样写直接刷新
						}else{
							$("#addError").text(arg.message);
						}
					}
				
				})
			
			})
			
		})
	</script>
</body>
</html>

add_students.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
	<h1>学生添加</h1>
	<form method="POST" action="/add_students/">
		<p>学生名称:<input type="text" name="title"/></p>
		<p>班级选择:
			<select name="class_id">
				{% for i in class_list %}
					<option value="{{i.id}}">{{i.class_name}}</option>
				{%endfor %}
			</select>
			
		</p>
		<input type="submit" value="提交">	
	</form>
</body>
</html>

edit_students.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
	<h1>学生添加</h1>
	<form method="POST" action="/edit_students/?nid={{students_list.id}}">
		<p>学生名称:<input type="text" name="title" value="{{students_list.students_name}}" /></p>
		<p>班级选择:
			<select name="class_id">
				{% for i in class_list %}
					{%if i.id == students_list.class_id%}
						<option selected value="{{i.id}}">{{i.class_name}}</option>
					{%else %}
						<option value="{{i.id}}">{{i.class_name}}</option>
					{%endif%}
				{%endfor %}
			</select>
		</p>
		<input type="submit" value="提交">	
	</form>
</body>
</html>

第3章

1.插件

  • bootstrap-3.4.1-dist

    • https://v3.bootcss.com/

    • 插件的操作:copy、常用标签、响应式、js

    • <link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1-dist/css/bootstrap.css">
      <link rel="stylesheet" href="/static/plugins/font-awesome-4.7.0/css/font-awesome.css">
      copy是下载了stylesheet css代码库后,在官网上复制class名来使用的
    • 响应式

    • 在窗口中窗口的大小不同,要展示不同的效果,通过@media来实现
      <!doctype html>
      <html lang="en">
      <head>
          <meta  charset="UTF-8">
      	<title></title>
          <style>
              .pg-header{
                  background-color:#b2dbal;
                  height:48px;
                  }
              @media(max-width:700px){
                  ....
                  .pg-header{
                      background-color:#b2dbal;
                      height:48px;
                  }
                  /*小于等于700px后执行*/
      
              }
          </style>
          <body>
              <div class="pg-header"><div>
          </body>
      </html>
      例如:导航条,栅格都是利用了@media来实现
    •  

  • font-awesome-4.7.0

2.母板

  • 母文件

    <!DOCTYPE html>
    <html lang="en">
    	<head>
    		<meta charset="UTF-8">
    		<title></title>
    		<link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1-dist/css/bootstrap.css">
    		<link rel="stylesheet" href="/static/plugins/font-awesome-4.7.0/css/font-awesome.css">
    		<link rel="stylesheet" href="/static/css/commons.css">
    		{%block css%}{%endblock%}
    	</head>
    	<body>
    		<div class="pg-header">
    			<div class="logo left">爸爸的后台</div>
    			<div class="avatar right" style="position:relative">
    				<img style="width:40px;height:40px;"src="/static/images/a.jpg">
    				<div class="user-info">
    					<a>个人资料</a>
    					<a>注销</a>
    				</div>
    			</div>
    			<div class="rmenus right">
    				<a><i class="fa fa-commenting-o" aria-hidden="true"></i>消息</a>
    				<a><i class="fa fa-envelope-o" aria-hidden="true"></i>邮箱</a>
    			</div>
    		</div>
    		
    		<div class="pg-body">
    			<div class="menus">
    				<a>学生管理</a>
    				<a>班级管理</a>
    				<a>老师管理</a>
    			</div>
    			<div class="content">
    				{% block xx%}
                    //接收子板数据
                    {%endblock%}
    			</div>
    		</div>
    		{%block js%}{%endblock%}
    	</body>
    </html>

     

  • 子文件

    {%extends "layout.html"%}  //继承母板
    {% block xx%}			//block 块一块区域
    	//子板将数据发给母板
    {% endblock %}
    
    {%extends "layout.html"%}
    {% block css%}
    	<style>
            .hide{
                display: none;
            }
            .shadow{
                position:fixed;
                left:0;
                top:0;
                right:0;
                bottom:0;
                background-color:black;
                opacity:0.4;
                z-index:999;   
            }
            .modal{
                z-index:1000;
                position:fixed;
                left:50%;
                top:50%;
                height:300px;
                width:400px;
                background-color:white;
                margin-left:-200px;
                margin-top:-150px;
            }
        </style>
    {% endblock %}
    
    {% block xx%}
    	<div style="margin:10px;">
    		<div>
    			<a class="btn btn-primary" href = "/add_class/">添加</a>
    			<a class="btn btn-info" onclick = "ShowModal();">对话框添加 </a>
    		</div>
    		<table class="table">
    			<thead>
    				<tr>
    				<th>ID</th>
    				<th>班级名称</th>
    				<th>功能</th>
    				</tr>
    		</thead>
    	</div>
    	<tbody>
    			{% for row in classes %}
    				<tr>
    					<td>{{row.id}}</td>
    					<td>{{row.class_name}}</td>
    					<td>
    						<a href = "/edit_class/?nid={{row.id}}">编辑</a>
    						<a onclick="modelEdit(this);">对话框编辑</a>
    						<a href = "/del_class/?nid={{row.id}}">删除</a>
    					</td>
    				</tr>
    			{% endfor %}
    	</tbody>
    	</table>
    	<div id="shadow" class="shadow hide"></div>
    	<div id="modal" class="modal hide">
    		<p>
    			<input id="title" type="text" name="title" />
    		</p>
    		<input type="button" value="提交" onclick="AjaxSend();"/><span id="errormsg"></span>
    		<input type="button" value="取消" onclick="cancleModal();"/>
    	</div>
    	<!--############-->
    	<div id="editModal" class="modal hide">
    		<p>
    			<input id="editid" type="text" name="id" style="display:none;"/>
    			<input id="editTitle" type="text" name="title"/>
    		</p>
    		<input type="button" value="提交" onclick="editAjaxadd();"/><span id="editerrormsg"></span>
    		<input type="button" value="取消" onclick="cancleModel();"/>
    	</div>
    {% endblock %}
    
    {% block js%}
    	<script src="/static/jquery-3.6.0.min.js"></script>
    	<script>
    		function ShowModal(){
    		
    			document.getElementById("shadow").classList.remove("hide");
    			document.getElementById("modal").classList.remove("hide");
    		}
    		function cancleModal(){
    		
    			document.getElementById("shadow").classList.add("hide");
    			document.getElementById("modal").classList.add("hide");
    		}
    		
    		function AjaxSend(){
    			$.ajax({
    				url:"/modal_add_class/",   //将数据通过post提交到这里
    				type:"POST",    //以什么方式提交
    				data:{"title":$("#title").val()},
    				success:function(data){
    					//当服务端处理完成后,返回数据时,该函数自动调用
    					//data=服务端返回的值
    					if(data == "ok"){
    						location.href="/classes/";
    					}else{
    						$("#errormsg").text(data);
    					}
    					
    				},
    			})
    		}
    		function modelEdit(ths){
    			document.getElementById("shadow").classList.remove("hide");
    			document.getElementById("editModal").classList.remove("hide");
    			//parent() 找到当前标签的父标签
    			//prevAll() 找到父标签上面所有的标签
    			var row = $(ths).parent().prevAll();
    			$("#editTitle").val($(row[0]).text());    //id 查找并把之前查找的赋值给,刚查找的标签
    			$("#editid").val($(row[1]).text());    //id 查找并把之前查找的赋值给,刚查找的标签
    		}
    		
    		function cancleModel(){
    		
    			document.getElementById("shadow").classList.add("hide");
    			document.getElementById("editModal").classList.add("hide");
    		}
    		function editAjaxadd(){
    			$.ajax({
    				url:"/modal_edit_class/",   //将数据通过post提交到这里
    				type:"POST",    //以什么方式提交
    				data:{"title":$("#editTitle").val(),"cid":$("#editid").val()},
    				success:function(arg){
    					//当服务端处理完成后,返回数据时,该函数自动调用
    					//data=服务端返回的值
    					//JSON.parse(字符串)    是将字符串转为对象
    					//JSON.stringify(对象)     是将对象转为字符串
    					//python中的字典在ajax称为ajax对象如果条调用它“对象.建”
    					arg = JSON.parse(arg);
    					if(arg.status){
    						location.reload();   //如果是当前页面这样写直接刷新
    					}else{
    						alert(arg.message);
    					}
    				},
    			})
    		}
    	</script>
    {% endblock %}

3.cookies

  • cookies是存放在客户端路由器上的键值对,服务器可以修改。

    obj=HttpReponse() 、render()、redirect()    #都可以设置cookie
    #取值
    变量 = request.COOKIES["cookies_key"]
    变量 = request.COOKIES.get("cookies_key")
    #添加
    obj = redirect("/classes/")
    #max_age此cookie只在用户浏览器上存在10秒
    obj.set_cookie("ticket","dddddd",max_age=10) 
    #设置返回的cookies值
    import datatime
    from datetime import timedelta
    #电脑当前时间
    ct = datetime.datetime.utcnow()
    #设置时间为十秒,主要用于时间的加减
    v=timedelta(seconds=10)
    #两个时间相加
    Value= ct+v
    obj.set_cookie("ticket","dddddd",expires=value)
    obj.set_cookie("ticket","dddddd",path="url")
    #指定url只有在指定的URL上才可以显示cookie,默认全部显示
    obj.set_cookie("ticket","dddddd",domain=None)
    #指定域名如果父域名写上子域名可以用,子域名写上其他子域名无法使用,默认全部
    obj.set_cookie("ticket","dddddd",secure=False)
    #在HTTPS传输
    obj.set_cookie("ticket","dddddd",httponly=False)
    #只能自http请求传入,js代码无法获取到
    return obj
    
    #cookie签名
    #设置
    obj.set_signed_cookie("ticket","123123",salt="jjjjj")
    #将123123通过jjjjj设置结果就变为“123123:fsttsgvsdfh”
    #获取
    v=路由器返回信息.set_signed_cookie("ticket",salt="jjjjj")
    
    #xx.html
    <script src="/static/jquery.cookie.js"></script>
    //通过插件分割cookie
    var token = $.cookie("csrgtoken");

4.mysite

4.1 app01

  • views.py

    from django.shortcuts import render,redirect,HttpResponse
    from utils import sqlheper
    import pymysql
    import json
    def classes(request):
        if not request.COOKIES.get("ticket"):
            return redirect("login/")
        conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="1451964253",db="school_system")
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,class_name from class")
        class_list = cursor.fetchall()
        cursor.close()
        conn.close()
    
        return render(request,"classes.html",{"classes":class_list})
    
    def layout(request):
        return render(request,"layout.html")
    
    def login(request):
        if request.method=="GET":
            return redirect("login.html")
        else:
            name = request.POST.get("UserName")
            pwd = request.POST.get("password")
            if name == "alex" and pwd == "123":
                obj = redirect("/classes/")
                obj.set_cookie("ticket","dddddd") #设置返回的cookies值
                return obj
            else:
                return render(request,"login.html")

4.2mysite

  • urls.py

    from django.contrib import admin
    from django.urls import path
    from app01 import views
        
    urlpatterns = [
        path('admin/', admin.site.urls),
        path(r"classes/",views.classes),
        path(r"layout/",views.layout),
        path(r"login/",views.login),
    ]

4.3 static

  • css

    • commons.css

      			body{
      				margin:0;
      			}
      			.left{
      				float:left;
      			}
      			.right{
      				float:right;
      			}
      			.pg-header{
      				height:48px;
      				min-width:1190px;
      				background-color:#1723b1;
      				line-height:48px;
      			}
      			.pg-header .logo{
      				color:white;
      				font-size:18px;
      				width:200px;
      				text-align:center;
      				border-right:1px solid white;
      			}
      			.pg-header .rmenus a{
      				display: inline-block;
      				padding:0 15px;
      				color:white;
      			}
      			.pg-header .rmenus a:hover{
      				background-color:#269abc;
      			}
      			.pg-header .avatar{
      				padding:0 20px;
      			}
      			.pg-header .avatar img{
      				border-radius:50%;
      			}
      			.pg-header .avatar .user-info{
      				display:none;
      				background-color:white;
      				border:1px solid #dddddd;
      				position:absolute;
      				width:200px;
      				top:48px;
      				right:2px;
      				color:white;
      				z-index:100;
      			}
      			.pg-header .avatar:hover .user-info{
      				display:block;
      			}
      			.pg-header .avatar .user-info a{
      				display:block;
      				padding:5px;
      			}
      			.menus{
      				width:200px;
      				position:absolute;
      				left:0;
      				bottom:0;
      				top:48px;
      				background-color:#dddddd;
      			}
      			.content{
      				position:absolute;
      				left:200px;
      				top:48px;
      				right:0;
      				bottom:0;
      				min-width:990px;
      				overflow:scroll;
      				z-index:99;
      			}
      			.pg-body .menus a{
      				display:block;
      				padding:5px;
      				
      			
      			} 

4.4 templates

  • classes.html

    {%extends "layout.html"%}
    {% block css%}
    	<style>
            .hide{
                display: none;
            }
            .shadow{
                position:fixed;
                left:0;
                top:0;
                right:0;
                bottom:0;
                background-color:black;
                opacity:0.4;
                z-index:1200;   
            }
            .modal1{
                z-index:1201;
                position:fixed;
                left:50%;
                top:50%;
                height:300px;
                width:400px;
                background-color:white;
                margin-left:-200px;
                margin-top:-150px;
            }
        </style>
    {% endblock %}
    
    {% block xx%}
    	<div style="margin:10px;">
    		<div>
    			<a class="btn btn-primary" href = "/add_class/">添加</a>
    			<a class="btn btn-info" onclick = "ShowModal();">对话框添加 </a>
    		</div>
    		<table class="table">
    			<thead>
    				<tr>
    				<th>ID</th>
    				<th>班级名称</th>
    				<th>功能</th>
    				</tr>
    		</thead>
    	</div>
    	<tbody>
    			{% for row in classes %}
    				<tr>
    					<td>{{row.id}}</td>
    					<td>{{row.class_name}}</td>
    					<td>
    						<a href = "/edit_class/?nid={{row.id}}">编辑</a>
    						<a onclick="modelEdit(this);">对话框编辑</a>
    						<a href = "/del_class/?nid={{row.id}}">删除</a>
    					</td>
    				</tr>
    			{% endfor %}
    	</tbody>
    	</table>
    	<div id="shadow" class="shadow hide"></div>
    	<div id="modal" class="modal1 hide">
    		<p>
    			<input id="title" type="text" name="title" />
    		</p>
    		<input type="button" value="提交" onclick="AjaxSend();"/><span id="errormsg"></span>
    		<input type="button" value="取消" onclick="cancleModal();"/>
    	</div>
    	<!--############-->
    	<div id="editModal" class="modal hide">
    		<p>
    			<input id="editid" type="text" name="id" style="display:none;"/>
    			<input id="editTitle" type="text" name="title"/>
    		</p>
    		<input type="button" value="提交" onclick="editAjaxadd();"/><span id="editerrormsg"></span>
    		<input type="button" value="取消" onclick="cancleModel();"/>
    	</div>
    {% endblock %}
    
    {% block js%}
    	<script src="/static/jquery-3.6.0.min.js"></script>
    	<script>
    		function ShowModal(){
    		
    			document.getElementById("shadow").classList.remove("hide");
    			document.getElementById("modal").classList.remove("hide");
    		}
    		function cancleModal(){
    		
    			document.getElementById("shadow").classList.add("hide");
    			document.getElementById("modal").classList.add("hide");
    		}
    		
    		function AjaxSend(){
    			$.ajax({
    				url:"/modal_add_class/",   //将数据通过post提交到这里
    				type:"POST",    //以什么方式提交
    				data:{"title":$("#title").val()},
    				success:function(data){
    					//当服务端处理完成后,返回数据时,该函数自动调用
    					//data=服务端返回的值
    					if(data == "ok"){
    						location.href="/classes/";
    					}else{
    						$("#errormsg").text(data);
    					}
    					
    				},
    			})
    		}
    		function modelEdit(ths){
    			document.getElementById("shadow").classList.remove("hide");
    			document.getElementById("editModal").classList.remove("hide");
    			//parent() 找到当前标签的父标签
    			//prevAll() 找到父标签上面所有的标签
    			var row = $(ths).parent().prevAll();
    			$("#editTitle").val($(row[0]).text());    //id 查找并把之前查找的赋值给,刚查找的标签
    			$("#editid").val($(row[1]).text());    //id 查找并把之前查找的赋值给,刚查找的标签
    		}
    		
    		function cancleModel(){
    		
    			document.getElementById("shadow").classList.add("hide");
    			document.getElementById("editModal").classList.add("hide");
    		}
    		function editAjaxadd(){
    			$.ajax({
    				url:"/modal_edit_class/",   //将数据通过post提交到这里
    				type:"POST",    //以什么方式提交
    				data:{"title":$("#editTitle").val(),"cid":$("#editid").val()},
    				success:function(arg){
    					//当服务端处理完成后,返回数据时,该函数自动调用
    					//data=服务端返回的值
    					//JSON.parse(字符串)    是将字符串转为对象
    					//JSON.stringify(对象)     是将对象转为字符串
    					//python中的字典在ajax称为ajax对象如果条调用它“对象.建”
    					arg = JSON.parse(arg);
    					if(arg.status){
    						location.reload();   //如果是当前页面这样写直接刷新
    					}else{
    						alert(arg.message);
    					}
    				},
    			})
    		}
    	</script>
    {% endblock %}
  • layout.html

    <!DOCTYPE html>
    <html lang="en">
    	<head>
    		<meta charset="UTF-8">
    		<title></title>
    		<link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1-dist/css/bootstrap.css">
    		<link rel="stylesheet" href="/static/plugins/font-awesome-4.7.0/css/font-awesome.css">
    		<link rel="stylesheet" href="/static/css/commons.css">
    		{%block css%}{%endblock%}
    	</head>
    	<body>
    		<div class="pg-header">
    			<div class="logo left">爸爸的后台</div>
    			<div class="avatar right" style="position:relative">
    				<img style="width:40px;height:40px;"src="/static/images/a.jpg">
    				<div class="user-info">
    					<a>个人资料</a>
    					<a>注销</a>
    				</div>
    			</div>
    			<div class="rmenus right">
    				<a><i class="fa fa-commenting-o" aria-hidden="true"></i>消息</a>
    				<a><i class="fa fa-envelope-o" aria-hidden="true"></i>邮箱</a>
    			</div>
    		</div>
    		
    		<div class="pg-body">
    			<div class="menus">
    				<a>学生管理</a>
    				<a>班级管理</a>
    				<a>老师管理</a>
    			</div>
    			<div class="content">
    				{%block xx%}{%endblock%}
    			</div>
    		</div>
    		{%block js%}{%endblock%}
    	</body>
    </html>
  • login.html

    <!doctype html>
    <html lang="en">
    <head>
    	<meat charset="UTF-8"/>
    	<title></title>
    </head>
    <body>
    	<form method="POST" action="/login/">
    		<p>班级名称:<input type="text" name="UserName"/></p>
    		<p>班级名称:<input type="password" name="password"/></p>
    		<input type="submit" value="提交">	
    	</form>
    </body>
    </html>

第4章

1.程序文件

  • 创建

    #create project 
    django-admin startproject mysite
    #create view
    python manage.py startapp 文件名
    #设置服务器ip
    python manage.py runserver 0.0.0.0
    
    python manage.py syncdb
    #创建admin user
    python manage.py createsuperuser
  • 文件包含

    • admin.py Django自带后台管理相关配置

    • apps.py

    • models.py 写类,根据类创建数据库表

    • tests.py 单元测试

    • views.py 业务处理

  • 注册app

    settings.py
    #添加程序所在文件名
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        "app01"
    ]
  • adimin中放置表

    from django.contrib import admin
    from app01 import models
    class aa(admin.ModelAdmin):
        #显示
        list_display = ["title","url"]
        #修改
        list_editable = ["url"]
    #将表在django自带的管理中显示出来。
    #aa代表可以显示title和url
    admin.site.register(models.表名,aa)

     

2.路由系统

  • 在URL上写动态路由(是加了正则表达式)

    from django.urls import re_path,path
    re_path(r'edit_class/(\d+)/', views.edit_class),
    #写正则必须再前面加上re_path,也需要导入。
    urls.py
    re_path(r"^index/(\w+)/",views.index),
    re_path(r"^index/(?P<a1>\w+)",views.index),
    #指定将正则表达式的值交给谁
    #注意不能混着写
    re_path(r"^index/(\w+)$",views.index),
    #^是起始符可以查找的更精准
    #终止符$只有URL完全相同才可以访问
    re_path(r"^index/(\w+)",views.index),
    #只有URL匹配后面加什么都可以匹配
    views.py
    def index(request,a1):
        #args接收未指定的值
        #kwargs接收指定的值
        #相当于一个是列表一个是字典
    def index(request,*args,**kwargs)
        #a1就是正则表达式的值
        return HttpResnonse("....")
  • 路由分发

    • 解决了,多用户开发URL冲突的问题,分层式管理的方式。

    mysite/urls.py
    #实现这个方式需要导入include模块
    from django.conf.urls import include
    urlpatterns = [
        url(r"^app01/",include("app01.urls")),
        #它是将app01文件夹下指定urls.py文件
        #实现了双重判断路由(app01下的urls.py需要自己创建)
        url(r"^app02/",include("app02.urls")),
        url(r"",include("function()")),
        #如果用户输入错或没有输入将执行这个函数
    ]
  • url别名反向生

    url(r"^login/",views.login,name="m1")
    #给url起一个别名
    views.py
    from django.urls import reverse
    v = reverse("n1")
    #如果要反向生需要导入
    
    url(r"^login/(\d+)/",views.login,name="m1")
    #给url起一个别名
    views.py
    def login(request,a1)
    from django.urls import reverse
    v = reverse("n1",args=(112))
    #可以使用正则表达式,他就会变成"login/112"
    
    url(r"^login/(?P<a1>\d+})",views.login,name="m1")
    #给url起一个别名
    views.py
    def login(request,a1)
    from django.urls import reverse
    v = reverse("m1" kwargs={"a1":"1111"})
    #也可以使用指定正则表达式的方式,就变成了“login/1111”
    
    login.html
    i="alex"
    {% url "m1" i%}
    #可以使用别名反向生的方式加文件,就变成了“login/alex”

3.ORM操作

  • 要使用ORM需要配置一下,Django默认的数据库是SQLlite(文件型数据库)修改为pymysql

    1. 创建数据库的命令

      #create form
      python manage.py makemigrations
      python manage.py migrate
      #如果需要修改form属性也是使用它,在app01下面有一个,migrations文件夹存放的数据库表修改的配置文件轻易不要删

       

    2. 修改settings.py

      DATABASES = {
      	"default":{
              "ENGINE":"django.db.backends.mysql",
              "NAME":"数据库名",
             	"USER":"用户名",
              "PASSWORD":"密码",
              "HOST":"主机名",
              "PORT":端口,
          }   
      }
    3. 在mysite的__ init __.py中添加

      import pymysql
      pymysql.install_as_MySQLdb()
  • ORM主要操作表和数据

    • 创建表、修改表

      models.py
      from django.db import models
      class UserGroup(models.Model):
          title = models.CharField(max_length=32)
      
      class UserInfo(models.Model):
          nid = models.BigAutoField(primary_key="True")
          #创建属性nid自增型加主键,django中也可以不写自动创建名为id的属性。
          username = models.CharField(max_length=32)
          password = models.CharField(max_length=64)
          #Create name UserName char type attribute max length 32.
          age = models.IntegerField(default=1)
          #创建age是整型默认值是1的属性
          #default是设置默认值
          ug = models.ForeignKey("UserGroup",null=True)
          #设置外键如果表中有记录,可以使用null=True允许默认为空。ug_id它的名字
    • 、删除表

    • 增、删、改、查

      views.py
      
      from app01 import models
      #增加,括号中加要添加的值
      models.UserGroup.objects.create(title="行政部们")
      #删除,括号中加条件
      models.UserGroup.objects.delete(id=1)
      #修改,括号中加条件
      models.UserGroup.objects.filter(id=1).update(title="保安部门")
      #查询,括号中加条件
      models.UserGroup.objects.all()
      #拿到表中的想要的一个或一段数据
      group_list=models.UserGroup.objects.all()[:]
      #拿到表中的总数量
      group_list=models.UserGroup.objects.all().count()
      group_list=models.UserGroup.objects.filter(id=1)
      #如果加了两个条件默认是and操作
      group_list=models.UserGroup.objects.filter(id__gt=1,id__lt=10)
      #id大于1的第一个
      models.UserGRoup.objects.filter(id__gt=1).first()
      #id大于1
      models.UserGRoup.objects.filter(id__gt=1)
      #id小于1
      models.UserGRoup.objects.filter(id__lt=1)
      #group_list拿到的是一个QuerySet类型(列表),每个元组也是一个对象。
      for row in group_list:
          print(row.id,row.title)
    • 链表操作

      #注意:如果链表,只有主操作表的记录可以全部拿到,没有对应记录显示none
      
      #all拿数据
      obj = models.UserInfo.objects.all().first()
      #称为正向操作
      #row点ug是代表想关联字段的表,可以点出利用的元组值
      print(obj.nid,obj.UserNaem,obj.ug.title)
      
      obj = models.UserGroup.objects.all().first()
      #称为反向操作
      #被关联的表也是可以使用关联表,使用obj.关联表名称小写加下划线set.all()就拿到了,关联表的所有记录,正常调用即可。
      print(obj.id,obj.title)
      for i in row.userinfo_set.all():
          print(i.username,i.password)
          
      #values拿数据
      obj = obj = models.UserInfo.objects.values("id","username","password","ug__title")
      #正向操作
      #通过ug__字段名拿数据
      print(obj)
      
      obj = models.UserGroup.objects.values("id","title","userinfo__username")
      #反向操作
      #写小写表名默认拿的是id,__字段可以拿数据
      print(obj)
    • 拿到的数据类型

      #返回的数据是QuerySet类型,里面是一个个对象。如果要链表需要通过关联名称点字段
      result = models.UserInfo.objects.all()
      for i in result:
          print(i.id,i.username,i.password,i.ug.title)
      #返回的数据是QuerySet类型,里面是一个个字典。如果要链表需要通过关联名称加双下划綫加字段名
      result = models.UserInfo.objects.all().values("id","username","password","ug__title")
      for i in result:
          print(i["id"],i["username"],i["password"],i["ug__title"])
      
      #返回的数据是QuerySet类型,里面是一个个元组。如果要链表需要通过关联名称加双下划綫加字段名
      result = models.UserInfo.objects.all().values_list("id","username","password","ug__title")
      for i in result:
          print(i[0],i[1],i[2],i[3])

       

4.视图函数

  • django支持两种视图方式函数方式和类方式。

    urls.py
    from app01 import views
    urlpatterns = [
        #函数方式
        url(r"^test.html$",views.test),
    	#类方式,用类方式后面需要加上.as_view()
        url(r"^login.html",views.login.as_view()),
    ]
    
    views.py
    from django.shortcuts import render,HttpResponse
    from django.views import View
    #函数式
    def test(request):
        return HttpResponse("...")
    #类式,要使用类需要继承View(需要导入)
    class Login(View):
        """
        form有两种提交方式get,post
        ajax有很多种,行业里的浅规则定义
            get 用来查询
            post 用来创建
            put 用来更新
            delete 用来删除
        """
        #函数也需要接收一个参数接收http请求
        def get(self,request):
            return render(request,"login.html")
        def post(self,request):
            return HttpResponse("Login.post")
    login.html
    <!doctype html>
    <html lang="en">
    <head>
    	<meat charset="UTF-8" />
        <title></title>
    </head>
    <body>
    	<form>
        	<q><input type="text" name="name"/></q>
            <input type="" value="提交">
        </form>
    </body>
    </html>
  • dispaltch是在执行自写类之前执行的一个函数(在View中),如果想在自写函数之前或之后做一些批量操作时,可以将它搬到自写类中一便操作。主要完成url的比对和调用。

    views.py
    #函数中(默认放到第一个),和封装器的作用一样。
    def dispatch(self,request,*args,**kwargs):
    	print("before")
        obj = super(Login,self).dispatch(request,*args,**kwargs)
        print("after")
        return obj

     

     

5.分页模式

  • django自带

    views.py 
    from django.core.paginator import Paginator,Page
    
    def index(request):
    	current_page = request.GET.get("page")
       	user_list = models.UserInfo.objects.all()
        #paginator就是django自带的分页,10是每页显示10条数据
        paginator = paginator(user_list,10)
        # per_page: 每页显示条目数量
        # count:    数据总个数
        # num_pages:总页数
        # page_range:总页数的索引范围,如: (1,10),(1,200)
        # page:     page对象
        try:
            #current_page是页码,可以直接写1
            posts = paginator.page(current_page)
             # has_next              是否有下一页
            # next_page_number      下一页页码
            # has_previous          是否有上一页
            # previous_page_number  上一页页码
            # object_list           分页之后的数据列表
            # number                当前页
            # paginator             paginator对象
       	except pageNotAnInteger as e:
            #如果页码不是整型执行下面的代码
            posts = paginator.page(1)
        except EmptyPage as e:
            #如果是负数执行下面的代码
            posts = paginatot.page(1)
        return render(request,"index.html",{"posts":posts})
    
    index.html
    <ul>
    	#object_list是对象列表row是每个对象
    	{%for row in posts.object_list%}
        	<li>{{row.name}}</li>
        {%endfor%}
    </ul>
    <div>
    	#判断是否有下一页
    	{% if posts.has_previous %}
        	#下一页页码
        	<a href="/index.html?page={{posts.previous_page_number }}">上一页</a>
        {% endif %}
        #判断是否有下一页
        {% if posts.has_next %}
        	#下一页页码
        	<a href="/index.html?page={{posts.next_page_number }}">下一条</a>
        {%endif%}
    </div>
  • 自制分页

    #可以创建一个文件夹存储常用工具
    utils/pager.py
    class PageInfo(object):
        
        def __init__(self,current_page,all_count,base_url,per_page,show_page=11):
            #自动执行判断是否是数值型不是就报错指向page=1
            try:
            	self.current_page = int(current_page)
            except Exception as e:
                self.current_page = 1
            self.per_page = per_page
            #divmod(x,y)返回两个数第一个是x/y的结果,第二个是xy的余数
            a,b = divmod(all_count,per_page)
            if b:
                a = a+1
            self.all_pager = a
            self.show_page = show_page
            self.base_url = base_url
        def start(self):
            return (self.current_page-1)*self.per_page
        def end(self):
            return self.current_page*self.per_page
        def pager(self):
            page_list = []
            half = int((self.show_page-1)/2)
            if self.all_pager < self.show_page:
                begin = 1
                stop = self.all_pager +1
            else:
                if self.current_page <= half:
                    begin = 1
                    stop = self.show_page +1
                else:
                    if self.current_page + half > self.all_pager:
                        begin = self.all_pager - self.show_page+1
                        stop = self.all_pager + 1
                    else:
                        begin = self.current_page - half
                        stop = self.current_page + half +1
                        
            if self.current_page <=1:
                prev = "<li><a href='%s?page=#'>上一页</a></li>"
            else:
                prev = "<li><a href='%s?page=%s'>上一页</a></li>" %(self.base_url,self.current_page-1)
            page_list.append(prev)
            for i in range(begin,stop):
                if i == self.current_page:
                    temp = "<li class='active'><a href='%s?page=%s'>%s</a></li>" %(self.base_url,i,i)
                else:
                    temp = "<li><a  href='%s?page=%s'>%s</a></li>" %(self.base_url,i,i)
                page_list.append(temp)
            if self.current_page>=self.all_pager:
                nex = "<li><a href='%s?page=#'>下一页</a></li>"
            else:
                nex = "<li><a href='%s?page=%s'>下一页</a></li>" %(self.base_url,self.current_page+1)
            page_list.append(nex)
            return "".join(page_list)
                    
    
    views.py
    from django.shortcuts import render,redirect,HttpResponse
    from django.urls import reverse
    from utils.pager import PageInfo
    from app01 import models
    def custom(request):
    	all_count = models.UserInfo.objects.all().count()
        page_info = PageInfo(request.GET.get("page"),all_count,10)
        #可以使用中括号索引需要的指定数据
        user_list = models.UserInfo.objects.all()[page_info.start():page_info.end()]
        return render(request,"custom.html",{"user_list":user_list,"page_info":page_info})
    
    custom.html
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    	<link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1-dist/css/bootstrap.css">
    	<link rel="stylesheet" href="/static/plugins/font-awesome-4.7.0/css/font-awesome.css">
    </head>
    <body>
    	<ul>
        	<!--user_list是对象列表row是每个对象-->
    		{%for row in user_list%}
    			<li>{{row.username}}</li>
    		{%endfor%}
    		<nav aria-label="Page navigation">
    		  <ul class="pagination">
    			<li>
    			  <a href="#" aria-label="Previous">
    				<span aria-hidden="true">&laquo;</span>
    			  </a>
    			</li>
                <!--如果把对象给了前端,django会直接将你的前端调用的函数执行,并将返回值放到里面。这样会被判定为不安全你需要加上“|safe”-->
    			{{page_info.pager|safe}}
    			<li>
    			  <a href="#" aria-label="Next">
    				<span aria-hidden="true">&raquo;</span>
    			  </a>
    			</li>
    		  </ul>
    		</nav>
    		
    	</ul>
    </body>
    </html>

第5章

1.ORM操作

  • 中级操作

    # 查
    models.UserInfo.objects.all().order_by("id","-name")
    #排序,如果出现重复是按照自左往右的顺序排列,-好事从大到小。
    from Dango.db.models import Count,Sum,Max
    models.UserInfo.objects.all().annotate(xxx=Count(id)).filter(id__lt=2)
    #分组别名xxx,group_by括号中使用复合条件,filter代指having。使用复合条件需要导入。
    print(v.query)
    #query可以查看对应的sql命令
    models.UserInfo.objects.filter(id__gte=1)
    #查询大于等于1的数据
    models.UserInfo.objects.filter(id__lte=1)
    #查询小于等于1的数据
    models.UserInfo.objects.filter(id__in=[1,2,3])
    #查询有1,2,3的数据
    models.UserInfo.objects.filter(id__range=[1,2])
    #查询在1,2中的数据
    models.UserInfo.objects.filter(name_startswith="xx")
    #
    models.UserInfo.objects.filter(name_contains="xx")
    #查询包含xx的数据
    models.UserInfo.objects.exclude(id=1)
    #查询不等于1的数据
    
    #f操作
    from django.db.models import F,Q,extra
    models.UserInfo.objects.all().update(age=F("age")+1)
    #在python无法使用age=age+1的操作,需要用到F,F是拿到后去数据库拿数据的。不加Fpython会认为他是个变量。
    
    #Q
    confition = {"id":1,"name":"root"}
    models.UserInfo.objects.filter(**condition)
    #可以使用字典当作条件,不过前面需要加**,**代表的是接收一个字典。
    #Q的用法有两中方式:对象方式,方法方式。
    models.UserInfo.objects.filter(Q(id=1))
    models.UserInfo.objects.filter(Q(id=1)|Q(id=4))
    #|是or运算,Q是一个对象
    models.UserInfo.objects.filter(Q(id=1) & Q(id=3))
    
    q1 = Q()
    #创建一个对象付给q1
    q1.connector = "OR"
    #每个条件都用or运算链接
    q1.children.append(("id",1))
    q1.children.append(("id",2))
    #条件
    
    q2 = Q()
    #创建一个对象付给q2
    q2.connector = "OR"
    #每个条件都用or运算链接
    q2.children.append(("id",4))
    q2.children.append(("id",5))
    #条件
    
    q3 = Q()
    q3.add(q1,"AND")
    q3.add(q2,"AND")
    #对象之间用add添加,括号中传对象和运算方式
    #方法方式推荐使用
    condition_dict = {
        "k1":[1,2,3],
        "k2":[1,],
    }
    con = Q()
    for k,v in condition_dict.items():
    	q=Q()
        q.connector ="OR"
        for i in v:
            q.children.append(("id",i))
        con.add(q,"AND")
        
    #extra
    models.UserInfo.objects.all().extra({select="n":select count(1) from app01_usertype where id>%s},select_params=[1,])
    #extra是在字段的位置做操作,select_params是加参数的。
    models.UserInfo.objects.all().where(["id=1" or id="%s","name=%s"],params=[1,])
    #元素和元素之间用and链接。params放参数,在后面加条件
    models.UserInfo.objects.all().extra({select="n":select count(1) from app01_usertype where id>%s},select_params=[1,],order_by={"-nid"})
    #排序-是大到小
    models.UserInfo.objects.extra(
    	tables=["app01_usertype"])
    #再打开一个表实现迪卡尔基的效果
    #1.映射
    #select和select_params
    #select 此处 from table
    #2.条件
    #where和params
    #select * from table 此处
    #3.表
    #tables
    #select * from table,此处
    #4.排序
    #order_by
    #select * from table 此处
    #示例
    models.UserInfo.objects.extra(
    	select={"newid":"select count(1) from app01_usertype where id>%s"},
        select_params=[1,],
        where = ["age>%s"],
        params = [18,],
        order_by = ["-age"],
        tables=["app01_usertype"]
    )
    #原生sql(遇到太难的也可以用sql命令写)
    from django.db import connection connections
    #cursor = connection.cursor()
    #链接配置文件的第一个,默认default是第一个
    #cursor = connections["default"].cursor()
    #可以选择你想连接的数据库
    #二选一使用
    
    cursor.execute("""select*from user where id = %s""",[1])
    #这里写sql
    row = cursor.fetchone()
    #返回你要的数据集
  • 其他

    ##################################################################
    # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
    ##################################################################
    def all(self)
        # 获取所有的数据对象
    
    def filter(self, *args, **kwargs)
    	#查询在条件中的
    def exclude(self, *args, **kwargs)
    	#查询不在条件中的
        # 条件可以是:参数,字典,Q
    
    def select_related(self, *fields)
         性能相关:表之间进行join连表操作,一次性获取关联的数据。
         model.tb.objects.all().select_related()
         model.tb.objects.all().select_related('外键字段')
         model.tb.objects.all().select_related('外键字段__外键字段')
    
    def prefetch_related(self, *lookups)
        性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
                # 获取所有用户表
                # 获取用户类型表where id in (用户表中的查到的所有用户ID)
                models.UserInfo.objects.prefetch_related('外键字段')
    
    
    
                from django.db.models import Count, Case, When, IntegerField
                Article.objects.annotate(
                    numviews=Count(Case(
                        When(readership__what_time__lt=treshold, then=1),
                        output_field=CharField(),
                    ))
                )
    
                students = Student.objects.all().annotate(num_excused_absences=models.Sum(
                    models.Case(
                        models.When(absence__type='Excused', then=1),
                    default=0,
                    output_field=models.IntegerField()
                )))
    
    def annotate(self, *args, **kwargs)
        # 用于实现聚合group by查询
    
        from django.db.models import Count, Avg, Max, Min, Sum
    
        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
        # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id
    
        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
        # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1
    
        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
        # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1
    
    def distinct(self, *field_names)
        # 用于distinct去重
        models.UserInfo.objects.values('nid').distinct()
        # select distinct nid from userinfo
        #注:只有在PostgreSQL中才能使用distinct进行去重
    
    def order_by(self, *field_names)
        # 用于排序
        models.UserInfo.objects.all().order_by('-id','age')
    
    def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
        # 构造额外的查询条件或者映射,如:子查询
        Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
        Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
        Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
        Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
    
     def reverse(self):
        # 倒序
        models.UserInfo.objects.all().order_by('-nid').reverse()
        # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序
    
     def defer(self, *fields):
        models.UserInfo.objects.defer('username','id')
        或
        models.UserInfo.objects.filter(...).defer('username','id')
        #映射中排除某列数据,id不管排不排除都会有
    
     def only(self, *fields):
        #仅取某个表中的数据
         models.UserInfo.objects.only('username','id')
         或
         models.UserInfo.objects.filter(...).only('username','id')
    
     def using(self, alias):
         指定使用的数据库,参数为别名(setting中的设置)
    
    #上面的可以一直点下去
    ##################################################
    # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
    ##################################################
    #这些用来某个无法往下点
    def raw(self, raw_query, params=None, translations=None, using=None):
        # 执行原生SQL
        models.UserInfo.objects.raw('select * from userinfo')
    
        # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名,自己写别名
        models.UserInfo.objects.raw('select id as nid from 其他表')
    
        # 为原生SQL设置参数
        models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])
    
        # 将获取的到列名转换为指定列名,加个参数列出来
        name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
        Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
    
        # 指定数据库
        models.UserInfo.objects.raw('select * from userinfo', using="default")
    
        ################### 原生SQL ###################
        from django.db import connection, connections
        cursor = connection.cursor()  # cursor = connections['default'].cursor()
        cursor.execute("""SELECT * from auth_user where id = %s""", [1])
        row = cursor.fetchone() # fetchall()/fetchmany(..)
    
    
    def values(self, *fields):
        # 获取每行数据为字典格式
    
    def values_list(self, *fields, **kwargs):
        # 获取每行数据为元祖
    
    def dates(self, field_name, kind, order='ASC'):
        # 根据时间进行某一部分进行去重查找并截取指定内容
        # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
        # order只能是:"ASC"  "DESC"
        # 并获取转换后的时间
            - year : 年-01-01
            - month: 年-月-01
            - day  : 年-月-日
    
        models.DatePlus.objects.dates('ctime','day','DESC')
    
    def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
        # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
        # kind只能是 "year", "month", "day", "hour", "minute", "second"
        # order只能是:"ASC"  "DESC"
        # tzinfo时区对象
        models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
        models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))
    
        """
        pip3 install pytz
        import pytz
        pytz.all_timezones
        pytz.timezone(‘Asia/Shanghai’)
        """
    
    def none(self):
        # 空QuerySet对象
    
    
    ####################################
    # METHODS THAT DO DATABASE QUERIES #
    ####################################
    
    def aggregate(self, *args, **kwargs):
       # 聚合函数,获取字典类型聚合结果,对整张表进行聚合
       from django.db.models import Count, Avg, Max, Min, Sum
       result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
       ===> {'k': 3, 'n': 4}
    
    def count(self):
       # 获取个数
    
    def get(self, *args, **kwargs):
       # 获取单个对象,不建议用,找不到或找到多个都会报错
    
    def create(self, **kwargs):
       # 创建对象,添加后会有一个返回值,就是当前添加这条的元组
    obj.models.UserInfo(username="zaa")
    #这样创建只会在内存中创建
    obj.save()
    #是用来提交到数据库的不建议
    
    def bulk_create(self, objs, batch_size=None):
        # 批量插入
        # batch_size表示一次插入的个数
        objs = [
            models.DDD(name='r11'),
            models.DDD(name='r22')
        ]
        models.DDD.objects.bulk_create(objs, 10)
    
    def get_or_create(self, defaults=None, **kwargs):
        # 如果存在,则获取,否则,创建
        # defaults 指定创建时,其他字段的值
        #第一个参数先进行查询有就返回个obj,没有就创建再返回给created一个逻辑值
        obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})
    
    def update_or_create(self, defaults=None, **kwargs):
        # 如果存在,则更新,否则,创建
        # defaults 指定创建时或更新时的其他字段
        #先查后更或创
        obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})
    
    def first(self):
       # 获取第一个
    
    def last(self):
       # 获取最后一个
    
    def in_bulk(self, id_list=None):
       # 根据主键ID进行查找
       id_list = [11,21,31]
       models.DDD.objects.in_bulk(id_list)
    
    def delete(self):
       # 删除
    
    def update(self, **kwargs):
        # 更新
    
    def exists(self):
       # 是否有结果
  • 高级

    q=models.UserInfo.objects.all()
    for row in q:
        #查UserInfo表中的记录没问题,但是如果要连表查,它会在显示的时候查显示几条差几次。
        print(row.name,row.ut.title)
    #select_realted:查询主动做链表,查多次
    q= models.UserInfo.objects.all().select_reated("ut","gp")
    #它会先通过inner链接表拿到所有需要的数据,只查一次
    for row in q:
        print(row.name,row.ut.title)
    
    #prefetch_related:不做连表,做多次查询
    q = models.UserInfo.objects.all().prefetch_related("ut")
    #它会先查询UserInfo拿到数据和usertype对应的id,通过id再去usertype表里拿数据。
    #拿到id通过in去查,每个表查询一次。
    for row in q:
        print(row.id,row.ut.title)
  • 多表操作

    models.py
    class Boy(models.Model):
        name = models.CharField(max_length=32)
    class Girl(models.Model):
        nick = models.CharField(max_length=32)
    class Love(models.Model):
        b = models.ForeignKey(to="Boy",to_field="id",on_delete=models.CASCADE)
        g = models.ForeignKey(to="Girl",to_field="id",on_delete=models.CASCADE)
        #默认就是只是我写出来了。
        class Meta:
            #给b和g创建唯一索引
            unique_together = [
                ("b","g")
            ]
    
    views.py
    obj = models.boy.objects.filter(name="sb").first()
    #拿到boy表sb的第一条记录
    love_list = obj.love_set.all()
    #通过连表拿到记录
    for row in love_list:
        #循环
        print(row.g.nick)
        #显示指定字段
        
    love_list = models.Love.objects.filter(b__name="sb")
    #拿到loy表的所有记录
    for item in love_list:
        #循环出想要的的字段
        print(row.g.nick)
        
    love_list = models.Love.objects.filter(b__name="sb").values("g__nick")
    #提前拿到想要的字段
    for item in love_list:
        print(item["g__nick"])
        #数据已经在内存,不需要多次访问数据库来拿数据
    
    love_list = models.Love.objects.filter(b__name="sb").select_related("g")
    #效果一样提前说好要拿的表
    for item in love_list:
        print(item.g.nick)
        #在这里循环拿数据
  • 自动创建对应表

    #你如果要创建一个对应表,其实可以自动创建。
    #在你要创建对应表的及个中找出主表(随便)进行创建。
    models.py
    class Boy(models.Model):
        name = models.CharField(max_length=32)
        #就是主表参数是次表,它会创建一个对应表。对应表并没有写出来,无法直接操作只能间接的操作
        m = models.ManyToManyField("Girl")
    class Girl(models.Model):
        nick = models.CharField(max_length=32)
        
    views.py
    obj = models.Boy.objects.filter(name="sb").first()
    #只有创建了的表可以操作
    #添加
    obj.m.add(2)
    #代表当前查询id和2(Girl表的id)绑定关系
    #删除指定记录
    obj.m.remove(1)
    obj.m.remove(*[1,2])
    #括号中的id是Girl表的id,删除对应表中Girl表id为1的记录。可以加列表(*变量)
    #重置记录并添加一条”sb“id和1的一条记录
    obj.m.set([1,])
    #查看
    q = obj.m.all()
    #拿到的是Girl表,q中的对象也是Girl对象
    #删除所有记录
    obj.m.clear()
    
    #反向功能一样从Girl向Boy
    obj = models.Girl.objects.filter(nick="小雨").first()
    v = obj.boy_set.all()
    #拿到Boy表
    print(v)
  • 两种融合

    models.py
    class Boy(models.Model):
        name = models.CharField(max_length=32)
        m = models.ManyToManyField("Girl",through="Love",through_fields=("b","g",))
        #不加后面的参数会创建4个表,加上创建3个。
        #第二个参数加类名,第三个参数加列名。
    class Girl(models.Model):
        nick = models.CharField(max_length=32)
        
    class Love(models.Model):
        b = models.ForeignKey("Boy")
        g = models.ForeignKey("Girl")
        class Meta:
            #给b和g创建唯一索引
            unique_together = [
                ("b","g")
            ]
    views.py
    obj = models.Boy.objects.filter(name="sb").first()
    #通过m时只有查看和删除所有可以用
    #查看
    q = obj.m.all()
    #删除所有记录
    obj.m.clear()
  • 数据类型

    SmallIntegerField(IntegerField):
            - 小整数 -32768 ~ 32767
    
        PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
            - 正小整数 0 ~ 32767
        IntegerField(Field)
            - 整数列(有符号的) -2147483648 ~ 2147483647
    
        PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
            - 正整数 0 ~ 2147483647
    
        BigIntegerField(IntegerField):
            - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
    
        自定义无符号整数字段
    
            class UnsignedIntegerField(models.IntegerField):
                def db_type(self, connection):
                    return 'integer UNSIGNED'
    
            PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
                'AutoField': 'integer AUTO_INCREMENT',
                'BigAutoField': 'bigint AUTO_INCREMENT',
                'BinaryField': 'longblob',
                'BooleanField': 'bool',
                'CharField': 'varchar(%(max_length)s)',
                'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
                'DateField': 'date',
                'DateTimeField': 'datetime',
                'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
                'DurationField': 'bigint',
                'FileField': 'varchar(%(max_length)s)',
                'FilePathField': 'varchar(%(max_length)s)',
                'FloatField': 'double precision',
                'IntegerField': 'integer',
                'BigIntegerField': 'bigint',
                'IPAddressField': 'char(15)',
                'GenericIPAddressField': 'char(39)',
                'NullBooleanField': 'bool',
                'OneToOneField': 'integer',
                'PositiveIntegerField': 'integer UNSIGNED',
                'PositiveSmallIntegerField': 'smallint UNSIGNED',
                'SlugField': 'varchar(%(max_length)s)',
                'SmallIntegerField': 'smallint',
                'TextField': 'longtext',
                'TimeField': 'time',
                'UUIDField': 'char(32)',
    
        BooleanField(Field)
            - 布尔值类型
    
        NullBooleanField(Field):
            - 可以为空的布尔值
    
        CharField(Field)
            - 字符类型
            - 必须提供max_length参数, max_length表示字符长度
    
        TextField(Field)
            - 文本类型
    
        EmailField(CharField):
            - 字符串类型,Django Admin以及ModelForm中提供验证机制
    
        IPAddressField(Field)
            - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
    
        GenericIPAddressField(Field)
            - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
            - 参数:
                protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
                unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"
    
        URLField(CharField)
            - 字符串类型,Django Admin以及ModelForm中提供验证 URL
    
        SlugField(CharField)
            - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
    
        CommaSeparatedIntegerField(CharField)
            - 字符串类型,格式必须为逗号分割的数字
    
        UUIDField(Field)
            - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
    
        FilePathField(Field)
            - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
            - 参数:
                    path,                      文件夹路径
                    match=None,                正则匹配
                    recursive=False,           递归下面的文件夹
                    allow_files=True,          允许文件
                    allow_folders=False,       允许文件夹
    
        FileField(Field)
            - 字符串,路径保存在数据库,文件上传到指定目录
            - 参数:
                upload_to = ""      上传文件的保存路径
                storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
    
        ImageField(FileField)
            - 字符串,路径保存在数据库,文件上传到指定目录
            - 参数:
                upload_to = ""      上传文件的保存路径
                storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
                width_field=None,   上传图片的高度保存的数据库字段名(字符串)
                height_field=None   上传图片的宽度保存的数据库字段名(字符串)
    
        DateTimeField(DateField)
            - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
    
        DateField(DateTimeCheckMixin, Field)
            - 日期格式      YYYY-MM-DD
    
        TimeField(DateTimeCheckMixin, Field)
            - 时间格式      HH:MM[:ss[.uuuuuu]]
    
        DurationField(Field)
            - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
    
        FloatField(Field)
            - 浮点型
    
        DecimalField(Field)
            - 10进制小数
            - 参数:
                max_digits,小数总长度
                decimal_places,小数位长度
    
        BinaryField(Field)
            - 二进制类型

     

  • 索引

    #联合唯一索引
    class Meta:
        unique_together = (
        	("字段名","字段名")
        )
    #联合索引
    class Meta:
        index_together = (
        	("字段名","字段名")
        )
    null                数据库中字段是否可以为空
    db_column           数据库中字段的列名
    default             数据库中字段的默认值
    primary_key         数据库中字段是否为主键
    db_index            数据库中字段是否可以建立索引
    unique              数据库中字段是否可以建立唯一索引
    unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
    unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
    unique_for_year     数据库中字段【年】部分是否可以建立唯一索引
    
    verbose_name        Admin中显示的字段名称
    blank               Admin中是否允许用户输入为空
    editable            Admin中是否可以编辑
    help_text           Admin中该字段的提示信息
    choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
                            如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
        #用于选项固定的场景
    #存放在程序中序号存在数据库中
    
    error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                            字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                            如:{'null': "不能为空.", 'invalid': '格式错误'}
    
    validators          自定义错误验证(列表类型),从而定制想要的验证规则
                            from django.core.validators import RegexValidator
                            from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
                            MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                            如:
                                test = models.CharField(
                                    max_length=32,
                                    error_messages={
                                        'c1': '优先错信息1',
                                        'c2': '优先错信息2',
                                        'c3': '优先错信息3',
                                    },
                                    validators=[
                                        RegexValidator(regex='root_\d+', message='错误了', code='c1'),
                                        RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
                                        EmailValidator(message='又错误了', code='c3'), ]
                                )

     

2.xss攻击

  • 后端先前端提交标签时会被判定为不安全,可以通过两种方式让它变得安全。不过要慎用。

    #后端设置
    from django.utils.safestring import mark_safe
    temp = "<a href='http://www.baidu.com'>baidu</a>"
    newtemp = mark_safe(temp)
    #前端设置
    {{函数名|safe}}

3.CSRF

  • csrf是通过一个随机字符串判断是否是,自己网页发出的请求。

  • cookies里面也有生成的随机字符串,它自动发的。通过POST提交时。

  • 函数使用

    #在第1章中禁用了
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        #'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]#是防止xss攻击的
    #基本配置
        #打开注释掉的配置
        #在html中form表单提交时需要加上{% csrf_token %}
        #会生成一个随机字符串防止xss攻击
    #全站禁用
    	#把它注释掉
    #局部禁用
    	#把注释掉的去掉
        from django.views.decorators.csrf import csrf_exempt,csrf_protect
        #csrf_exempt不然某些使用
        @csrf_exempt
        def csrf1(request):
            pass
    #局部使用
    	#把注释掉的去掉
        from django.views.decorators.csrf import csrf_exempt,csrf_protect
        #csrf_protect然某些使用
        @csrf_protect
        def csrf1(request):
            pass
        
  • 类使用

    from django.views import View
    from django.utils.decorators import method_decorator
    
    @method_decorator(csrf_protect,name="方法名")
    #如果想让所有的方法地使用name写为dispatch方法名
    class Foo(View):
        def get(self,request):
            pass
        def post(self,request):
            pass
  • 类使用装饰器

    def wrapper(func):
        def inner(*args,**kwargs)
        	return func(*args,**kwargs)
       	return inner
    #指定装饰器放在某个方法
    class Foo(View):
        @method_decorator(wrapper)
        def age(self):
            pass
    #指定装饰器放在某个类上
    @method_decorator(wrapper,name="方法名")
    class Foo(View):
        def age(self):
            pass
  • Ajax提交数据时,携带CSRF

    <!--通过input拿到随机生成的字符串-->
    <form>
    	{% csrf_token %}
        <input id ="user" type="text">
        <input type="submit" value="提交">
        <a onclick="submitForm();">Ajax提交</a>
    </form>
    <script src="/static/jquery-3.6.min.js"></script>
    <script>
    	function submitForm(){
            //它是input框通过name获得值
            var csrf = $("input[name='csrgmiddlewaretoken']").val();
            var user = $("#user").val();
            $.ajax({
                url:"/csrf1.html",
                type:"POST",
                //注意拿到的名字是什么,发到后端的名字就是什么,修改会导致接收不到。
                data:{"user":user,"csrgmiddlewaretoken":csrgmiddlewaretoken}
                success:function(arg){
                	console.log(arg);
            }
            })
        }
    </script>
    <!--从cookie中拿去生成的字符串,放到文件头中-->
    <form>
        <input id ="user" type="text">
        <input type="submit" value="提交">
        <a onclick="submitForm();">Ajax提交</a>
    </form>
    <script src="/static/jquery-3.6.min.js"></script>
    <script src="/static/jquery.cookie.js"></script>
    <script>
    	function submitForm(){
            //通过建拿到值,通过导入的插件把建和值分开
            var token = $.cookie("csrgtoken");
            var user = $("#user").val();
            $.ajax({
                url:"/csrf1.html",
                type:"POST",
                //在文件头中添加,X-CSRFToken是固定的不然接收不到数据。
                headers:{"X-CSRFToken":token},
                data:{"user":user},
                success:function(arg){
                	console.log(arg);
            }
            })
        }
    
    //你不获取将发过来的字符串直接写到ajax的数据中也是可以的
    data:{"user":user,"csrgmiddlewaretoken":{% csrf_token %}}
    </script>

4.Session

  • session时保存在服务器上的数据(键值对),依赖于cookie。起Wed保持会话的作用。推荐用session

    VIEW.PY
    def login(request):
        if m == "GET":
            return render(request,login.html)
        else:
            u = request.POST.get("user")
            p = request.POST.get("pwd")
            if u == "alex" and p == "123":
                #1.生成随机字符串
                #2.通过cookie发送给客户端
                #3.服务端保存
                #3.{随机字符串:{key:date}}
                request.session["user"]="alex"
                #这条命令实现上面的所有,session保存在数据库中。
                return redirect("/index/")
            else:
                return render(request,"login.html",{"msg":"用户错误"})
    def index(request):
        #1.获取客户端cookie中的随机字符串
        #2.去session中查找有没有随机字符串
        #3.去session对应key中value中查看是否有uaer
        v = request.session.get("user")
        #这条命令做上面的操作
        if v:
            return HttpResponse("成功:%s" %v)
        else:
            return redirect("/login/")
        return HttpResponse("成功")
    login.html
    <form>
    	…………
    </form>
  • session提供了5种类型的Session提供使用,设置session的存储方式。

    #数据库(默认)
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
    
    #缓存
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
        
    #文件
    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()    
        
    #缓存+数据库
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
    
    #加密cookie
    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

     

  • 配置ip

    settings.py
    ALLOWED_HOSTS = ["192.168.1.104"]
    #配置后在同一局域网下的计算机可以访问。
  • Session数据库

    Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
     
    a. 配置 settings.py
     
        SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
         
        SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
        SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
        SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
        SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
        SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
        SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
        SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
        SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
     
     
     
    b. 使用
     
        def index(request):
            # 获取、设置、删除Session中数据
            request.session['k1']
            request.session.get('k1',None)
            request.session['k1'] = 123
            request.session.setdefault('k1',123) # 存在则不设置
            del request.session['k1']
     
            # 所有 键、值、键值对
            request.session.keys()
            request.session.values()
            request.session.items()
            #有迭代器需要一个一个取
            request.session.iterkeys()
            request.session.itervalues()
            request.session.iteritems()
     
     
            # 用户session的随机字符串
            session_key=request.session.session_key
            # 将所有Session失效日期小于当前日期的数据删除
            request.session.clear_expired()
     
            # 检查 用户session的随机字符串 在数据库中是否
            request.session.exists("session_key")
     
            # 删除当前用户的所有Session数据
            request.session.delete("session_key")
     
            request.session.set_expiry(value)
                * 如果value是个整数,session会在些秒数后失效。
                * 如果value是个datatime或timedelta,session就会在这个时间后失效。
                * 如果value是0,用户关闭浏览器session就会失效。
                * 如果value是None,session会依赖全局session失效策略。
  • 缓存Session

    a. 配置 settings.py
     
        SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
        SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
     
     
        SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
        SESSION_COOKIE_PATH = "/"                                # Session的cookie保存的路径
        SESSION_COOKIE_DOMAIN = None                              # Session的cookie保存的域名
        SESSION_COOKIE_SECURE = False                             # 是否Https传输cookie
        SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支持http传输
        SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
        SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否关闭浏览器使得Session过期
        SESSION_SAVE_EVERY_REQUEST = False                        # 是否每次请求都保存Session,默认修改之后才保存。设为False第一次访问网站三十分钟过期,设为True每次访问如果超过三十分钟才过期。

     

5.模块引擎

  • 常用方法

    {% for i in data.key %}
    {% for i in data.value %}
    {% for k,v in data.items %}
    {{ data|upper }}

     

  • include

    pub.html
    <div>
        <h1>组件</h1>
    </div>
    test.html
    <body>
    	{% include "pub.html"%}
        <!--导入自己写的组件,可以导入多此-->
    </body>

     

  • 自定义simple_tag

    1. 在app中创建templatetags模块

    2. 创建任意 .py 文件,如:xx.py

      #!/usr/bin/env python
      #coding:utf-8
      from django import template
      #啥也不能变,变量也不行
      register = template.Library()
      #必须加装饰器
      #可以在前端当作条件使用,只能传2个参数
      @register.filter
      def my_upper(val,age):
          return val+age
      #不能条件参数,可以传输多个参数
      @register.simple_tag
      def my_lower(a1,a2,a3):
          return a1+a2+a3
      
      @register.simple_tag
      def my_input(id,arg):
          result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
          return mark_safe(result)
    3. 在使用自定义simple_tag的html文件中导入之前创建的 xx.py 文件名

      {% load xx %}
    4. 使用simple_tag

      {{ val|my_upper:"666"}}
      #接收两个参数时第二个参数加到后面
      {% my_lower '1' '2' '3'%}
      {% my_input 'id_username' 'hide'%}
    5. 在settings中配置当前app,不然django无法找到自定义的simple_tag  

      INSTALLED_APPS = (
          'django.contrib.admin',
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.messages',
          'django.contrib.staticfiles',
          'app01',
      )

第6章

  • django只做后端操作不做服务器和客户机的链接,通过其他模块连接。现在使用的都是wsgiref和django搭配使用,公司用uwsgi和django。它们都遵循WSGI规则。

  • django框架的开始

    from wsgiref.simple_server import make_server
    #wsgiref连接django并做操作。
    def RunServer(environ,start_response):
        start_response("200 Ok",[("Content-Type","text/html")])
        return [bytes("<h1>Hello,Web!</h1>",encoding="UTF-8"),]
    if __name__ == "__main__":
        httpd = make_server("127.0.0.1",8000,RunServer)
        httpd.serve_forever()
        
    #django的生命周期,连接接并收请求后经过中间件的检测,路由器匹配,匹配成功后执行视图函数,访问数据库调用模板,再通过中间件后发送到网页。
  • MVC、MTV是将任务分配的文件夹。django属于MTV

  • models(数据库,模型),views(html模板),controllers(业务逻辑处理)

  • models(数据库,模型),templates(html模板),views(业务逻辑处理)

1.中间件

  • 中间件是用户和服务器之间通信时必须经过的类。

    #settings.py
    #这里的都是中间件
    from django.middleware.common import CommonMiddleware
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        "m1.Middle"
        #写上它你的中间件就可以用了。
    ]
    
    #views.py
    class JsonResponse:
        def __init__(self,req,status,msg):
            self.req = req
            self.status = status
            self.msg = msg
        def render(self):
            import json
            ret = {
                "status":self.status,
                "msg":self.msg
            }
            return HttpResponse(json.dumps(ret))
    def test(request):
        return JsonResponse(request,True,"错误信息")
    #帮你写了错误信息。render会被process_template_response调用所以直接将对象发过去就成。
    #middlewares.py
    from django.utils.deprecation import MiddlewareMixin
    #自己写中间件
    class Middle1(MiddlewareMixin):
        #接收时,不需要返回内部会自动返回,如果在这返回你的视图函数将无法执行。直接就将结果返回到了用户页面。
        #request接收发送到服务的信息
    	def process_request(self,request):
    		print("m1.process_request")
        #callback视图函数
        def process_view(self,tequest,callback,callback_args,callback_kwargs):
            print("m1.process_view")
            return callback(tequest,*callback_args,**callback_kwargs)
        #发送时,必须返回不然会报错
        #response发送给客户机的信息。
    	def process_response(self,request,response):
    		print("m1.process_response")
             return response
        #exception错误信息
        def process_exception(self,request,exception):
            print("m1.process_exception")
        #process_template_response只有再视图函数中有render方法时才会调用。
        def process_template_response(self,request,response):
            print("m1.process_template_response")
             return response
            
        
    class Middle2(MiddlewareMixin):
    	def process_request(self,request):
    		print("m2.process_request")
        def process_view(self,tequest,callback,callback_args,callback_kwargs):
            print("m2.process_view")
            return callback(tequest,*callback_args,**callback_kwargs)
    	def process_response(self,request,response):
    		print("m2.process_response")
             return response
        def process_exception(self,request,exception):
            print("m2.process_exception")
        def process_template_response(self,request,response):
            print("m2.process_template_response")
             return response
    #process_request加返回值时返回:
    m1.process_request|m1.process_response
    #process_view加返回值时返回:
    m1.process_request|m2.process_request|m1.process_view|view|m2.process_response|m1.process_response
    #都不加时返回:
    m1.process_request|m2.process_request|m1.process_view|m1.process_view|view|m2.process_response|m1.process_response
    #视图函数报错时返回:
    m1.process_request|m2.process_request|m1.process_view|m1.process_view|view|m2.process_exception|m1.process_exception|m2.process_response|m1.process_response
    #视图函数报错而process_exception加返回值时返回:
    m1.process_request|m2.process_request|m1.process_view|m1.process_view|view|m2.process_exception|m2.process_response|m1.process_response
    #有render方法加返回时返回:
    m1.process_request|m2.process_request|m1.process_view|m1.process_view|view|m2.process_template_response|m2.process_response|m1.process_response
  • 先执行所有中间件的process_request,再执行所有中间件的process_view,再执行视图函数最后执行所有中间件的process_response。如果有两个中间件再process_request加上返回值,则不往下执行直接通过process_request返回(同等级下的)。而process_view是跳到最后一个process_response再执行回去。

  • 如果视图函数报错则执行process_exception

2.form验证

  • form验证是解决用户输入问题,错误反馈信息。

  • 自定义规则

    #它可以帮你规范输入的数据
    from django.forms import Form
    from django.forms import fields
    class LoginForm(Form):
        #不能为空,小于18、大于6
        username = fields.CharField(
            max_length=18,
            min_length=6,
            required=True,
            #自己写每个错误的信息
            error_messages={
                "required":"留下空的想死吗",
                "min_length":"写这么短你要死吗",
                "max_length":"这怎么长怎么没把你累死",
            }
        )
        password = fields.CharField(max_length=16,required=True)
    
    def login(request):
        if request.method == "GET":
            return render(request,"login.html")
        else:
            #你不在需要将数据取出做验证,只需要直接传进去,get或post都可以。
            obj = LoginForm(request.POST)
            #拿到的是个逻辑值满足条件则是true
            if obj.is_valid():
                #拿到的是一个字典类型的数据
                print(obj.cleaned_data)
                return redirect("http://www.baidu.com")
            else:
                #所有的错误信息
                #print(obj.errors)
                #那每个单独的错误的第一个
                #print(obj.errors["username"][0])
                #print(obj.errors["password"][0])
                return render(request,"login.html",{"obj":obj})
    def ajax_login(request):
    	import json
    	ret = {"status":True,"msg":None}
    	obj = LoginForm(request.POST)
    	if obj.is_valid():
    		print(obj.cleaned_data)
    		return redirect("http://www.baidu.com")
    	else:
    		ret["status"] = False
    		ret["msg"] = obj.errors
    	v = json.dumps(ret)
    	 return HttpResponse(v)
        
    login.html
    <!DOCTYPE html>
    <html lang="en"> 
    <head>
    	<meta charset="UTF-8"/>
    	<title></title>
    </head>
    <body>
    	{{obj.user.label}}
    	<form id="f1" action="/login/" method="POST">
    		{% csrf_token %}
    		<p>
    			<input type="text" name="user">
    		</p>
    		<p>
    			<input type="password" name="pwd">
    		</p>
    		<input type="submit" value="提交">
    		<a onclick="submitForm();">ajax提交</a>
    	</form>
    	<script src="/static/jquery-1.12.4.js"></script>
    	<script>
    		function submitForm(){
    			$.(".c1").remove();
    			$.ajax({
    				url:"/ajax_login/",
    				type:"POST",
    				//serialize将查到的form中的信息提取,以前写的字典也是这个
    				//提取后的信息是"user=alex&pwd=123&csrf_token=adferg"
    				data:$("#f1").serialize(),
    				dataType:"JSON",
    				success:function(arg){
    					if(arg.status){
    					
    					}else{
    						$.each(arg.msg,function(index,value){
    							var tag = document.createElement("span");
    							tag.innerHTML = value[0];
    							tag.className = "c1";
    							//找到并判断下面的如果满足就添加一个在他的后面
    							$("#f1").find('input[name="'+ index +'"]').after(tag);
    							
    						})
    					
    					}
    				}
    			
    			})
    		}
    	</script>
    </body>
    </html>
    #ajax和form都可以提交后端做的操作也是一样的。
  • is_valid的工作原理

    • 调用类是实例化,self.fields是一个字典类中变量是键,正则是值。循环self.fields拿到每个键去前端拿值,判断正则和值是否匹配,匹配filag返回true。(所以要保证前端拿到的和后端的一致)

  • 正则

    Field
        required=True,               是否允许为空
        #from django.forms import widgets 需要导入
        widget=None,                 HTML插件
        label=None,                  用于生成Label标签或显示内容
        initial=None,                初始值
        help_text='',                帮助信息(在标签旁边显示)
        error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
        show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
        validators=[],               自定义验证规则
        localize=False,              是否支持本地化
        disabled=False,              是否可以编辑
        label_suffix=None            Label内容后缀
     
     
    CharField(Field)			    字符型
        max_length=None,             最大长度
        min_length=None,             最小长度
        strip=True                   是否移除用户输入空白
     
    IntegerField(Field)				整型
        max_value=None,              最大值
        min_value=None,              最小值
     
    FloatField(IntegerField)
        ...
     
    DecimalField(IntegerField)
        max_value=None,              最大值
        min_value=None,              最小值
        max_digits=None,             总长度
        decimal_places=None,         小数位长度
     
    BaseTemporalField(Field)
        input_formats=None          时间格式化   
     						      
    DateField(BaseTemporalField)    格式:2015-09-01
    TimeField(BaseTemporalField)    格式:11:12
    DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
     
    DurationField(Field)            时间间隔:%d %H:%M:%S.%f
        ...
     
    RegexField(CharField)
        regex,                      自定制正则表达式
        max_length=None,            最大长度
        min_length=None,            最小长度
        error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
     
    #eamil内部也是继承的CharFIeld所以也可以使用它的方法
    EmailField(CharField)      
        ...
     
    FileField(Field)
        allow_empty_file=False     是否允许空文件
     
    ImageField(FileField)      
        ...
        注:需要PIL模块,pip3 install Pillow
        以上两个字典使用时,需要注意两点:
            - form表单中 enctype="multipart/form-data"
            - view函数中 obj = MyForm(request.POST, request.FILES)
     
    URLField(Field)
        ...
     
     
    BooleanField(Field)  
        ...
     
    NullBooleanField(BooleanField)
        ...
     
    ChoiceField(Field)
        ...
        choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
        required=True,             是否必填
        widget=None,               插件,默认select插件
        label=None,                Label内容
        initial=None,              初始值
        help_text='',              帮助提示
     
     
    ModelChoiceField(ChoiceField)
        ...                        django.forms.models.ModelChoiceField
        queryset,                  # 查询数据库中的数据
        empty_label="---------",   # 默认空显示内容
        to_field_name=None,        # HTML中value的值对应的字段
        limit_choices_to=None      # ModelForm中对queryset二次筛选
         
    ModelMultipleChoiceField(ModelChoiceField)
        ...                        django.forms.models.ModelMultipleChoiceField
     
     
         
    TypedChoiceField(ChoiceField)
        coerce = lambda val: val   对选中的值进行一次转换
        empty_value= ''            空值的默认值
     
    MultipleChoiceField(ChoiceField)
        ...
     
    TypedMultipleChoiceField(MultipleChoiceField)
        coerce = lambda val: val   对选中的每一个值进行一次转换
        empty_value= ''            空值的默认值
     
    ComboField(Field)
        fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                                   fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
     
    MultiValueField(Field)
        PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
     
    SplitDateTimeField(MultiValueField)
        input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
        input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
     
    FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
        path,                      文件夹路径
        match=None,                正则匹配
        recursive=False,           递归下面的文件夹
        allow_files=True,          允许文件
        allow_folders=False,       允许文件夹
        required=True,
        widget=None,
        label=None,
        initial=None,
        help_text=''
     
    GenericIPAddressField
        protocol='both',           both,ipv4,ipv6支持的IP格式
        unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
     
    SlugField(CharField)           数字,字母,下划线,减号(连字符)
        ...
     
    UUIDField(CharField)           uuid类型
        ...
  • 可以生成标签(不推荐)

    widget=None,
    label="用户名", 
    disabled=True,			
    label_suffix="||",	
    initial="alex",
    help_text="hello world!"
  • 让页面保留生一次输入的信息。

    from django.shortcuts import render,redirect,HttpResponse 
    from django.forms import Form
    from django.forms import fields
    class LoginForm(Form):
        user = fields.CharField(label="用户名")
    #不管是get还是POST都将obj提交过去,get提交时生成标签会将数据提交到后台,就变成了POST生成标签时会判断有没有值,如果有就带过去当成他的value。
    def login(request):
        if request.method == "GET":
    		obj = LoginForm()
            return render(request,"login.html",{"obj":obj})
        else:
            obj = LoginForm(request.POST)
            if obj.is_valid():
                print(obj.cleaned_data)
                return redirect("http://www.baidu.com")
            else:
                return render(request,"login.html",{"obj":obj})
    login.html
    <!DOCTYPE html>
    <html lang="en"> 
    <head>
    	<meta charset="UTF-8"/>
    	<title></title>
    </head>
    <body>
    	<form id="f1" action="/login/" method="POST" novalidate><!--浏览器会帮你做验证加上它浏览器就不会帮你做了-->
    		{% csrf_token %}
    		<p>
    			{{obj.user}}{{obj.user.errors.user.0}}
    		</p>
            <input type="submit" value="提交">
    	</form>
    </body>
    </html>
  • ajax提交

    #它可以帮你规范输入的数据
    from django.forms import Form
    from django.forms import fields
    class LoginForm(Form):
        #不能为空,小于18、大于6
        username = fields.CharField(
            max_length=18,
            min_length=6,
            required=True,
            #自己写每个错误的信息
            error_messages={
                "required":"留下空的想死吗",
                "min_length":"写这么短你要死吗",
                "max_length":"这怎么长怎么没把你累死",
            }
        )
        password = fields.CharField(max_length=16,required=True)
    def ajax_login(request):
    	import json
    	ret = {"status":True,"msg":None}
    	obj = LoginForm(request.POST)
    	if obj.is_valid():
    		print(obj.cleaned_data)
    		return redirect("http://www.baidu.com")
    	else:
    		ret["status"] = False
    		ret["msg"] = obj.errors
    	v = json.dumps(ret)
    	 return HttpResponse(v)
        
    login.html
    <!DOCTYPE html>
    <html lang="en"> 
    <head>
    	<meta charset="UTF-8"/>
    	<title></title>
    </head>
    <body>
    	{{obj.user.label}}
    	<form id="f1" action="/login/" method="POST">
    		{% csrf_token %}
    		<p>
    			<input type="text" name="user">
    		</p>
    		<p>
    			<input type="password" name="pwd">
    		</p>
    		<input type="submit" value="提交">
    		<a onclick="submitForm();">ajax提交</a>
    	</form>
    	<script src="/static/jquery-1.12.4.js"></script>
    	<script>
    		function submitForm(){
    			$.(".c1").remove();
    			$.ajax({
    				url:"/ajax_login/",
    				type:"POST",
    				//serialize将查到的form中的信息提取,以前写的字典也是这个
    				//提取后的信息是"user=alex&pwd=123&csrf_token=adferg"
    				data:$("#f1").serialize(),
    				dataType:"JSON",
    				success:function(arg){
    					if(arg.status){
    					
    					}else{
    						$.each(arg.msg,function(index,value){
    							var tag = document.createElement("span");
    							tag.innerHTML = value[0];
    							tag.className = "c1";
    							//找到并判断下面的如果满足就添加一个在他的后面
    							$("#f1").find('input[name="'+ index +'"]').after(tag);
    							
    						})
    					
    					}
    				}
    			
    			})
    		}
    	</script>
    </body>
    </html>
    #ajax和form都可以提交后端做的操作也是一样的。
  • 内部插件

    TextInput(Input)
    NumberInput(TextInput)
    EmailInput(TextInput)
    URLInput(TextInput)
    PasswordInput(TextInput)
    HiddenInput(TextInput)
    Textarea(Widget)
    DateInput(DateTimeBaseInput)
    DateTimeInput(DateTimeBaseInput)
    TimeInput(DateTimeBaseInput)
    CheckboxInput
    Select
    NullBooleanSelect
    SelectMultiple
    RadioSelect
    CheckboxSelectMultiple
    FileInput
    ClearableFileInput
    MultipleHiddenInput
    SplitDateTimeWidget
    SplitHiddenDateTimeWidget
    SelectDateWidget
  • 常用选择插件

    # 单radio,值为字符串
    # user = fields.CharField(
    #     initial=2,
    #     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
    # )
     
    # 单radio,值为字符串
    # user = fields.ChoiceField(
    #     choices=((1, '上海'), (2, '北京'),),
    #     initial=2,
    #     widget=widgets.RadioSelect
    # )
     
    # 单select,值为字符串
    # user = fields.CharField(
    #     initial=2,
    #     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
    # )
     
    # 单select,值为字符串
    # user = fields.ChoiceField(
    #     choices=((1, '上海'), (2, '北京'),),
    #     initial=2,
    #     widget=widgets.Select
    # )
     
    # 多选select,值为列表
    # user = fields.MultipleChoiceField(
    #     choices=((1,'上海'),(2,'北京'),),
    #     initial=[1,],
    #     widget=widgets.SelectMultiple
    # )
     
     
    # 单checkbox
    # user = fields.CharField(
    #     widget=widgets.CheckboxInput()
    # )
     
     
    # 多选checkbox,值为列表
    # user = fields.MultipleChoiceField(
    #     initial=[2, ],
    #     choices=((1, '上海'), (2, '北京'),),
    #     widget=widgets.CheckboxSelectMultiple
    # )

3.练习

  • models

    from django.db import models
    
    # Create your models here.
    class Classes(models.Model):
    	title = models.CharField(max_length=32)
    
    class Student(models.Model):
    	name = models.CharField(max_length=32)
    	email = models.CharField(max_length=32)
    	age = models.IntegerField(32)
    	cls = models.ForeignKey("Classes",on_delete=models.CASCADE)
    
    class Teacher(models.Model):
    	tname = models.CharField(max_length=32)
    	ct = models.ManyToManyField("Classes")
  • urls.py

    from django.contrib import admin
    from django.urls import re_path,path
    from app01 import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
    	path('class_list/', views.class_list),
        path('add_class/', views.add_class),
        re_path(r'edit_class/(\d+)/', views.edit_class),
    	path('student_list/', views.student_list),
        path('add_student/', views.add_student),
        re_path('edit_student/(\d+)/', views.edit_student),
    ]
  • views.py

    from django.shortcuts import render,redirect,HttpResponse 
    from django.forms import Form
    from django.forms import fields
    from django.forms import widgets
    #用于forms插件
    from app01 import models
    
    """
    class LoginForm(Form):
        user = fields.CharField(
            required=True,
    		widget=None,     
    		label="用户名",         #标识信息
    		disabled=True,			#是否可以编辑
    		label_suffix="||",		
    		initial="alex",
    		help_text="hello world!"
    	)
        pwd = fields.CharField(max_length=16)
    
    def login(request):
        if request.method == "GET":
    		obj = LoginForm()
            return render(request,"login.html",{"obj":obj})
        else:
            obj = LoginForm(request.POST)
            if obj.is_valid():
                print(obj.cleaned_data)
                return redirect("http://www.baidu.com")
            else:
                return render(request,"login.html",{"obj":obj})
    
    def ajax_login(request):
    	import json
    	ret = {"status":True,"msg":None}
    	obj = LoginForm(request.POST)
    	if obj.is_valid():
    		print(obj.cleaned_data)
    		return redirect("http://www.baidu.com")
    	else:
    		ret["status"] = False
    		ret["msg"] = obj.errors
    	v = json.dumps(ret)
    	return HttpResponse(v)
    """
    class ClassForm(Form):
        title = fields.CharField(
            required=True,
    		widget=None,     
    		label="用户名",         #标识信息
    		label_suffix="||",		
    		help_text="hello world!"
    	)
    	
    def class_list(request):
    	stu_list = models.Classes.objects.all()
    	return render(request,"class_list.html",{"stu_list":stu_list})
    	
    def add_class(request):
    	if request.method == "GET":
    		obj = ClassForm()
    		return render(request,"add_class.html",{"obj":obj})
    	else:
    		obj = ClassForm(request.POST)
    		if obj.is_valid():
    			models.Classes.objects.create(**obj.cleaned_data)
    			return redirect("/class_list/")
    		return render(request,"add_class.html",{"obj":obj})
    	
    def edit_class(request,nid):
    	if request.method == "GET":
    		row = models.Classes.objects.filter(id=nid).first()
    		#默认是data接收,接收后会校验,现在我们写成initial时默认值第一次不会校验。
    		obj = ClassForm(initial={"title":row.title})
    		return render(request,"edit_class.html",{"nid":nid,"obj":obj})
    	else:
    		obj = ClassForm(request.POST)
    		if obj.is_valid():
    			models.Classes.objects.filter(id=nid).update(**obj.cleaned_data)
    			return redirect("/class_list/")
    		return render(request,"edit_class.html",{"nid":nid,"obj":obj})
    
    
    class StudentForm(Form):
    	#widgets.TextInput代表普通输入框,attrs是属性给input添加属性。
        name = fields.CharField(min_length=2,max_length=6,widget=widgets.TextInput(attrs={"class":"form-control"}))
        email = fields.EmailField(widget=widgets.TextInput(attrs={"class":"form-control"}))
        age = fields.IntegerField(min_value=18,max_value=25,widget=widgets.TextInput(attrs={"class":"form-control"}))
    	#widget是插件,select是下拉框choices是下拉框的值格式是[(x:xx),(x:xx)]。刚好values_list拿到的也是这个格式id=下拉框的value,title是显示的数据。
        cls_id = fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list("id","title"),attrs={"class":"form-control"}))
    
    def student_list(request):
    	stu_list = models.Student.objects.all()
    	return render(request,"student_list.html",{"stu_list":stu_list})
    
    def add_student(request):
    	if request.method == "GET":
    		obj = StudentForm()
    		return render(request,"add_student.html",{"obj":obj})
    	else:
    		obj = StudentForm(request.POST)
    		if obj.is_valid():
    			models.Student.objects.create(**obj.cleaned_data)
    			return redirect("/student_list/")
    		return render(request,"add_student.html",{"obj":obj})
    
    def edit_student(request,nid):
    	if request.method == "GET":
    		row = models.Student.objects.filter(id=nid).values("name","email","age","cls_id").first()
    		obj = StudentForm(initial=row)
    		return render(request,"edit_student.html",{"nid":nid,"obj":obj})
    	else:
    		obj = StudentForm(request.POST)
    		if obj.is_valid():
    			models.Student.objects.filter(id=nid).update(**obj.cleaned_data)
    			return redirect("/student_list/")
    		return render(request,"edit_student.html",{"nid":nid,"obj":obj})
  • html

    #class_list.html
    
    <!DOCTYPE html>
    <html lang="en" >
    <head>
    	<meta charset="UTF-8"/>
    	<title></title>
    </head>
    <body>
    	<a href="/add_class/">添加</a>
    	<ul>
    		{% for row in stu_list %}
    			<li>{{row.title}} <a href="/edit_class/{{row.id}}"/>编辑</a> </li>
    		{% endfor %}
    	</ul>
    </body>
    </html>
    
    #add_class.html
    
    <!DOCTYPE html>
    <html lang="en"> 
    <head>
    	<meta charset="UTF-8"/>
    	<title></title>
    </head>
    <body>
    	<form action="/add_class/" method="POST">
    		{% csrf_token %}
    		<p>
    			{{obj.title}} {{obj.errors.title.0}}
    		</p>
    		<input type="submit" value="提交" />
    	</form>
    </body>
    </html>
    
    #edit_class.html
    
    <!DOCTYPE html>
    <html lang="en"> 
    <head>
    	<meta charset="UTF-8"/>
    	<title></title>
    </head>
    <body>
    	<form action="/edit_class/{{nid}}/" method="POST">
    		{% csrf_token %}
    		<p>
    			{{obj.title}} {{obj.errors.title.0}}
    		</p>
    		<input type="submit" value="提交" />
    	</form>
    </body>
    </html>
    
    #student_list.html
    
    <!DOCTYPE html>
    <html lang="en" >
    <head>
    	<meta charset="UTF-8"/>
    	<title></title>
    </head>
    <body>
    	<a href="/add_student/">添加</a>
    	<ul>
    		{% for row in stu_list %}
    			<li>{{row.name}}-{{row.email}}-{{row.age}}-{{row.cls_id}}-{{row.cls.title}}  <a href="/edit_student/{{row.id}}/"/>编辑</a></li>
    		{% endfor %}
    	</ul>
    </body>
    </html>
    
    #add_student.html
    
    <!DOCTYPE html>
    <html lang="en" >
    <head>
    	<meta charset="UTF-8"/>
    	<title></title>
    </head>
    <body>
    	<form action="/add_student/" method="POST">
    		{% csrf_token %}
    		<p>
    			{{obj.name}}
    		</p>
    		<p>
    			{{obj.email}}
    		</p>
    		<p>
    			{{obj.age}}
    		</p>
    		<p>
    			{{obj.cls_id}}
    		</p>
    		<input type="submit" value="提交">
    	</form>
    </body>
    </html>
    #edit_student.html
    
    <!DOCTYPE html>
    <html lang="en" >
    <head>
    	<meta charset="UTF-8"/>
    	<title></title>
    	<link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1-dist/css/bootstrap.css">
    </head>
    <body>
    	<div style="width:500px;margin:0 auto;">
    		<h1>编辑学生</h1>
    		<form class="form-horizontal" action="/edit_student/{{nid}}/" method="POST">
    		  {% csrf_token %}
    		  <div class="form-group">
    			<label  class="col-sm-2 control-label">Email</label>
    			<div class="col-sm-10">
    			  {{obj.name}}
    			</div>
    		  </div>
    		  <div class="form-group">
    			<label  class="col-sm-2 control-label">Email</label>
    			<div class="col-sm-10">
    			  {{obj.email}}
    			</div>
    		  </div>
    		  <div class="form-group">
    			<label  class="col-sm-2 control-label">Email</label>
    			<div class="col-sm-10">
    			  {{obj.age}}
    			</div>
    		  </div>
    		  <div class="form-group">
    			<label  class="col-sm-2 control-label">Email</label>
    			<div class="col-sm-10">
    			  {{obj.cls_id}}
    			</div>
    		  </div>
    		  <div class="form-group">
    			<div class="col-sm-offset-2 col-sm-10">
    			  <input type="submit"class="btn btn-default" value="提交">
    			</div>
    		  </div>
    		</form>
    	</div>
    </body>
    </html>

第7章

基本认识

1 问题

  1. 前后端分离?是后端给前段返回json数据被称为前后分离。

  2. 移动端盛行。是后端给app返回json数据。

  3. PC端应用?crm项目,前段后端一起写,运行在浏览器上。 一般情况下都是PC端使用。

  4. drf的意义?帮助你们在django框架基础上快速搭建遵循restful规范接口的程序。drf是一个基于django开发的组件,本质是一个django的app。

  5. 以前我们写的是一个类型(get、post、put、patch、delete)一个函数,现在我们遵循restful使用一个接口实现增删改查的效果,使用json格式来传输数据。

  6. 基于django可以实现遵循restful规范的接口开发:

    1. FBV,可以实现比较麻烦。

      urlpatterns = [
          url(r'^drf/info/', views.info),
      ]
      def info(request):
          if request.method == "GET":
              pass
          if request.method == "POST":
              pass
    2. CBV,相比较简答根据method做的了不同的区分。

      urlpatterns = [
          url(r'^drf/info/', views.Info.as_view()),
      ]
      class Info(APIView):
          def get(self,request,*args,**kwargs):
              pass
          def post(self,request,*args,**kwargs):
              pass
  7. DRF的应用场景,以后在公司参与前后端分离项目、参与为app写接口时,用drf会比较方便。

  8. drf框架:不基于drf也可以实现restful规范来开发接口程序、使用了drf之后,可以快速帮我们开发restful规范来开发接口。

  9. 视图,继承APIView(在内部apiview继承了django的View)

  10. 什么是restful规范

    是一套规则,用于程序之间进行数据交换的约定。
    他规定了一些协议,对我们感受最直接的的是,以前写增删改查需要写4个接口,restful规范的就是1个接口,根据method的不同做不同的操作,比如:get/post/delete/put/patch/delete.
    初次之外,resetful规范还规定了:
    	- 数据传输通过json
    	
    扩展:前后端分离、app开发、程序之间(与编程语言无关)
  11. JSON格式

    JSON:
        {
            name:'alex',
            age:18,
            gender:'男'
        }
  12. 以前用webservice,做前后端的数据传输。

    以前用webservice,数据传输格式xml。(过时)
    XML
    	<name>alex</name>
    	<age>alex</age>
    	<gender>男</gender>
  13. drf如何帮助我们快速开发的?drf提供了那些功能?

    	解析器,根据用户请求体格式不同进行数据解析,解析之后放在request.data中。
    		在进行解析时候,drf会读取http请求头 content-type.
    		如果content-type:x-www-urlencoded,那么drf会根据 & 符号分割的形式去处理请求体。
    			user=wang&age=19
    		如果content-type:application/json,那么drf会根据 json 形式去处理请求体。
    		 {"user":"wang","age":19}
    - 序列化,可以对QuerySet进行序列化,也可以对用户提交的数据进行校验。
    - 渲染器,可以帮我们把json数据渲染到页面上进行友好的展示。(内部会根据请求设备不同做不同的展示)
  14. 写程序的潜规则:约束

    # 约束子类中必须实现f1
    class Base(object):
    	def f1(self):
    		#类中从此类错误信息就代表,要在子类中实现。
    		raise NotImplementedError('asdfasdfasdfasdf')
    
    class Foo(Base):
    	
    	def f1(self) :
    		print(123)
    	
    	
    obj = Foo()
    obj.f1()
  15. restful规范

    1. 建议用https代替http
    2. 在URL中体现api,添加api标识
    	https://www.cnblogs.com/xwgblog/p/11812244.html   # 错误
    	https://www.cnblogs.com/api/xwgblog/p/11812244.html  # 正确
    	https://api.cnblogs.com/xwgblog/p/11812244.html # 正确
    	
    	建议:https://www.cnblogs.com/api/...
    3. 在URL中要体现版本
    	https://www.cnblogs.com/api/v1/userinfo/
    	https://www.cnblogs.com/api/v2/userinfo/
    4. 一般情况下对于api接口,用名词不用动词。
    	https://www.cnblogs.com/api/v1/userinfo/
    5. 如果有条件的话,在URL后面进行传递。
    	https://www.cnblogs.com/api/v1/userinfo/?page=1&category=2
    6. 根据method不同做不同操作
    	get/post/put/patch/delete
  16. 简述drf组件。

  17. 类继承关系

    class View(object):
    	def dipatch(self):
    		print(123)
    	
    class APIView(View):
    	def dipatch(self):
    		method = getattr(self,"get")
    		return method()
    	
    class GenericAPIView(APIView):
    	queryset = None
    	serilizer_class = None 
    	def get_queryset(self):
    		return self.queryset
    		
    	def get_serilizer(self,*arg,**kwargs):
    		cls = self.get_serilizer_class()
    		return cls(*arg,**kwargs)
    	
    	def get_serilizer_class(self):
    		return self.serilizer_class
    class ListModelMixin(object):
    	def list(self):
    		queryset = self.get_queryset()
    		ser = self.get_serilizer(queryset,many=True)
    		return Reponse(ser.data)
    		
    class ListAPIView(ListModelMixin,GenericAPIView):
    	def get(self):
    		return self.list(...)
    	
    	
    class TagView(ListAPIView):
    	queryset = models.User.object.all()
    	serilizer_class = TagSerilizer
    	
    
    obj = TagView()
    x = obj.dispatch()
    给用户返回x
  18. 继承关系

    class View(object):
    	def dipatch(self):
    		print(123)
    	
    class APIView(View):
    	version_class = settings.xxx 
    	parser_class = settings.sxx
    	permision_classes = []
    	
    	def dipatch(self):
    	
    		self.initial()
    	
    		method = getattr(self,"get")
    		return method()
    		
    	def initial(self):
    		self.version_class()
    		self.parser_class()
    		for item in self.permision_classes:
    		 	item()
    	
    class GenericAPIView(APIView):
    	queryset = None
    	serilizer_class = None 
    	def get_queryset(self):
    		return self.queryset
    		
    	def get_serilizer(self,*arg,**kwargs):
    		cls = self.get_serilizer_class()
    		return cls(*arg,**kwargs)
    	
    	def get_serilizer_class(self):
    		return self.serilizer_class
    class ListModelMixin(object):
    	def list(self):
    		queryset = self.get_queryset()
    		ser = self.get_serilizer(queryset,many=True)
    		return Reponse(ser.data)
    		
    class ListAPIView(ListModelMixin,GenericAPIView):
    	def get(self):
    		return self.list(...)
    	
    	
    class TagView(ListAPIView):
    	queryset = models.User.object.all()
    	serilizer_class = TagSerilizer
    	version_class = URLPathClass
    	parser_class = JSONParser
    	permission_classes = [Foo,Bar ]
    
    obj = TagView()
    x = obj.dispatch()
    给用户返回x

 

2 初识

  1. 安装pip3 install djangorestframework

  2. 使用

    # 注册app
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        #drf就是个app所以需要注册
        'rest_framework'
    ]
    
    # 写路由
    
    from django.contrib import admin
    from django.urls import path,re_path
    from api import views
    
    urlpatterns = [
        re_path(r'^drf/info/$', views.DrfInfoView.as_view()),
    ]
    
    # 写视图
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    #使用drf需要继承APIView
    class DrfInfoView(APIView):
    
        def get(self,request,*args,**kwargs):
            data = [
                {'id': 1, 'title': '震惊了...王阳居然...', 'content': '...'},
            ]
            #使用Response会给你套个模板(变得好看点个)
            return Response(data)

3 基本

  1. 数据库(models.py)

    from django.db import models
    
    class Category(models.Model):
        """
        文章分类
        """
        name = models.CharField(verbose_name='分类',max_length=32)
    
    
    class Article(models.Model):
        """
        文章表
        """
        title = models.CharField(verbose_name='标题',max_length=32)
        summary = models.CharField(verbose_name='简介',max_length=255)
        content = models.TextField(verbose_name='文章内容')
        category = models.ForeignKey(verbose_name='分类',to='Category',on_delete=models.CASCADE)
  2. 接口,实现访问接口时,创建一个文章类型

    # urls.py
    
    from django.contrib import admin
    from django.urls import path,re_path
    from api import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        re_path(r'^info/$', views.InfoView.as_view()),
        re_path(r'^drf/info/$', views.DrfInfoView.as_view()),
        re_path(r'^drf/category/$', views.DrfCategoryView.as_view()),
    ]
    
    # views.py
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    class DrfCategoryView(APIView):
    	pass
  3. 为了以后工作不会麻烦到前端人员,可以是用postman模拟前端人员发请求的操作。(是一个exe的应用程序)

  4. postman

    1. 点击加号添加一个新的页面、在url框中输入url,旁边选为post、在下一栏中选择Body、下一栏选x-www-urlencoded,会发送指定的个是后端只有post才能解析、下方可以添加要返回到后端的数据,key,value。

      #使用body可以查看任意类型,只要在请求体中。
      request.body: name=alex&age=19&gender=12
      #通过post可以拿到数据并转为dict格式
      request.POST: {'name': ['alex'], 'age': ['19'], 'gender': ['12']}
    2. 用raw最后选择json。就会发送json的数据到后端。

      #用body可以达到数据,拿到的是一个字节
      request.body: b'{"ID":1,"name":"Alex","age":19}'
      #post没有数据因为post无法解析json格式的数据
      request.POST: {}
      #使用request.data可以将Json的数据转换为dict格式。
    3. 如果你用浏览器页面查看,而后端发的是json。它会报错,需要在url后面加上 ?format=json 就可以查看了。postman模拟浏览器进行发送请求

  5. 代码

    # urls.py
    
    from django.contrib import admin
    from django.urls import path,re_path
    from api import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        re_path(r'^info/$', views.InfoView.as_view()),
        re_path(r'^drf/info/$', views.DrfInfoView.as_view()),
        re_path(r'^drf/category/$', views.DrfCategoryView.as_view()),
        re_path(r'^drf/category/(?P<pk>\d+)/$', views.DrfCategoryView.as_view()),
        re_path(r'^new/category/$', views.NewCategoryView.as_view()),
        re_path(r'^new/category/(?P<pk>\d+)/$', views.NewCategoryView.as_view()),
    ]
    
    # views.py
    
    from api import models
    from django.forms.models import model_to_dict
    class DrfCategoryView(APIView):
        
        def get(self,request,*args,**kwargs):
            """获取所有文章分类/单个文章分类"""
            #因为id在url的后面所以用kwargs取
            pk = kwargs.get('pk')
            if not pk:
                #从数据库拿数据时需要通过values取拿,应为json不能将queryset对象转换为json格式。
                queryset = models.Category.objects.all().values('id','name')
                #values拿到内部是字典,可以转换。
                data_list = list(queryset)
                return Response(data_list)
            else:
                #有id就走它
                category_object = models.Category.objects.filter(id=pk).first()
                #使用model_to_dict可以直接将其转为字典。
                data = model_to_dict(category_object)
                return Response(data)
    
        def post(self,request,*args,**kwargs):
            """增加一条分类信息"""
            #使用request.data可以将Json的数据转换为dict格式。
            #request.data不存在于django,而在于drf中。
            #key相同可以直接使用
         	models.Category.objects.create(**request.data)
            return Response('成功')
    
        def delete(self,request,*args,**kwargs):
            """删除"""
            pk = kwargs.get('pk')
            models.Category.objects.filter(id=pk).delete()
            return Response('删除成功')
    
        def put(self,request,*args,**kwargs):
            """更新"""
            pk = kwargs.get('pk')
            #key相同可以直接使用。
            models.Category.objects.filter(id=pk).update(**request.data)
            return Response('更新成功')
  6. request.data,解析请求体中的数据,将其变成我们想要的格式。

4 序列化

  1. 文章类型,drf的 serializers帮助我们提供了序列化

    # url.py
    
    from django.urls import path,re_path
    from django.contrib import admin
    from api import views
    urlpatterns = [
        re_path(r'^admin/', admin.site.urls),
        re_path(r'^info/$', views.InfoView.as_view()),
        re_path(r'^drf/info/$', views.DrfInfoView.as_view()),
        re_path(r'^drf/category/$', views.DrfCategoryView.as_view()),
        re_path(r'^drf/category/(?P<pk>\d+)/$', views.DrfCategoryView.as_view()),
    
    
        re_path(r'^new/category/$', views.NewCategoryView.as_view()),
        re_path(r'^new/category/(?P<pk>\d+)/$', views.NewCategoryView.as_view()),
    ]
    
    # views.py
    
    #引入serializers
    from rest_framework import serializers
    #创建本地类,主要用于定义自己的信息
    class NewCategorySerializer(serializers.ModelSerializer):
        class Meta:
            #写表名
            model = models.Category
            #序列化所有字段
            # fields = "__all__"
            #序列化指定字段
            fields = ['id','name']
    
    class NewCategoryView(APIView):
        def get(self,request,*args,**kwargs):
            pk = kwargs.get('pk')
            if not pk:
                queryset = models.Category.objects.all()
                #将数据库拿到的数据序列化,many是是否处理多条
                #将从数据库拿到的数据传给instance帮你做序列化
                ser = NewCategorySerializer(instance=queryset,many=True)
                return Response(ser.data)
            else:
                model_object = models.Category.objects.filter(id=pk).first()
                ser = NewCategorySerializer(instance=model_object, many=False)
                return Response(ser.data)
    
        def post(self,request,*args,**kwargs):
            #serializers不止可以序列化还可以做验证。
            #将前端的数据传给data
            ser = NewCategorySerializer(data=request.data)
            #如果验证成功为True
            if ser.is_valid():
                #帮你把数据提交到数据库
                ser.save()
                #返回刚才的数据
                return Response(ser.data)
            #使用drf模板返回错误信息。
            return Response(ser.errors)
        
        def put(self,request,*args,**kwargs):
            pk = kwargs.get('pk')
            category_object = models.Category.objects.filter(id=pk).first()
            #将原数据交给instance,需要修改的数据交给data
            ser = NewCategorySerializer(instance=category_object,data=request.data)
            #验证
            if ser.is_valid():
                #提交
                ser.save()
                return Response(ser.data)
            return Response(ser.errors)
        
        def delete(self,request,*args,**kwargs):
            pk = kwargs.get('pk')
            #删除无其他操作,可以直接使用数据库的命令
            models.Category.objects.filter(id=pk).delete()
            return Response('删除成功')
    1. 序列化,对对象或对象列表(queryset)进行序列化操作以及表单验证的功能。

    2. 查询模板的顺序,优先根目录下:templates,根据app的注册顺序去每个app的templates目录中找。

  2. 文章

    # models.py
    
    from django.db import models
    
    class Category(models.Model):
        """
        文章分类
        """
        name = models.CharField(verbose_name='分类',max_length=32)
    
    
    class Article(models.Model):
        """
        文章表
        """
        status_choices = (
            (1,"发布"),
            (2,"删除"),
    
        )
        status = models.IntegerField(verbose_name="状态",choices=status_choices,default=1)
        title = models.CharField(verbose_name='标题',max_length=32)
        summary = models.CharField(verbose_name='简介',max_length=255)
        content = models.TextField(verbose_name='文章内容')
        category = models.ForeignKey(verbose_name='分类',to='Category',on_delete=models.CASCADE)
        tag = models.ManyToManyField(verbose_name="标签",to="Tag",null=True,blank=True)
    
    class Tag(models.Model):
        """标签"""
        title = models.CharField(verbose_name="标签",max_length=32)
    
    
    # urls.py
    
    from django.contrib import admin
    from django.urls import path,re_path
    from api import views
    
    urlpatterns = [
        # get获取列表
        # post增加数据
        re_path(r'^drf/article/$', views.ArticleView.as_view()),
        re_path(r'^drf/article/(?P<pk>\d+)/$', views.ArticleView.as_view()),
    ]
    
    # views.py
    
    from django.shortcuts import render
    from django.http import JsonResponse,HttpResponse
    from django.views import View
    from aqi import serializer
    
    class ArticleView(APIView):
        def get(self,request,*args,**kwargs):
            pk = kwargs.get('pk')
            if not pk:
                queryset = models.Article.objects.all()
                ser = serializer.ArticleSerializer(instance=queryset,many=True)
                return Response(ser.data)
            article_object = models.Article.objects.filter(id=pk).first()
            ser = serializer.ArticleSerializer(instance=article_object, many=False)
            return Response(ser.data)
    
        def post(self,request,*args,**kwargs):
            ser = serializer.ArticleSerializer(data=request.data)
            if ser.is_valid():
                ser.save()
                return Response(ser.data)
            return Response(ser.errors)
    
        def put(self,request,*args,**kwargs):
            """全部更新"""
            pk = kwargs.get('pk')
            article_object = models.Article.objects.filter(id=pk).first()
            #使用put时必须把所有字段写上,不然无法更新只会报错。
            ser = serializer.ArticleSerializer(instance=article_object,data=request.data)
            if ser.is_valid():
                ser.save()
                return Response(ser.data)
            return Response(ser.errors)
    
        def patch(self,request,*args,**kwargs):
            """局部"""
            pk = kwargs.get('pk')
            article_object = models.Article.objects.filter(id=pk).first()
            #partial是否允许部分字段。
            ser = serializer.ArticleSerializer(instance=article_object, data=request.data,partial=True)
            if ser.is_valid():
                ser.save()
                return Response(ser.data)
            return Response(ser.errors)
    
        def delete(self,request,*args,**kwargs):
            pk = kwargs.get('pk')
            models.Article.objects.filter(id=pk).delete()
            return Response('删除成功')
    
    # serializer.py
    
    from rest_framework import serializers
    from api import models
    class ArticleSerializer(serializers.ModelSerializer):
        #第二种:通过创建字段并指定字段原的方法实现。
        #创建一个字段source(原、源头)数据原为category.name字段,是否必须只要
        #source,无需加括号,在源码内部会去判断是否可执行,如果可执行自动加括号。【fk/choice】
        category_txt = serializers.CharField(source='category.name',required=False)
        #第三种:通过和钩子差不多的方法拿数据,需要在下面定义一个函数,名是指定的get_+变量名“get_x1”【m2m】
        x1 = serializers.SerializerMethodField()
    
        status_txt = serializers.CharField(source='get_status_display',required=False)
    
        x2 = serializers.SerializerMethodField()
        class Meta:
            model = models.Article
            # fields = "__all__"
            fields = ['id','title','summary','content','category','category_txt','x1','status','status_txt','x2']
            #第一种:通过depth拿其他表中的数据,官方文档中最大数为10可以取当前表,连接的十层数据(不推荐,应为有用没用的数据都会被拿出来。)
            # depth = 1
    
        def get_x1(self,obj):
            # obj 是主表,点出想要的字段
            return obj.category.name
    
        def get_x2(self,obj):
            #你在models中写了指定的后,要显示文本信息就写成这样。
            return obj.get_status_display()
  3. 文章标签

    # urls.py
    
    re_path(r'^new/article/$', views.NewArticleView.as_view()),
    re_path(r'^new/article/(?P<pk>\d+)/$', views.NewArticleView.as_view()),
    
    # views.py
    
    
    class NewArticleView(APIView):
    
        def get(self,request,*args,**kwargs):
            pk = kwargs.get('pk')
            if not pk:
                queryset = models.Article.objects.all()
                ser = serializer.NewArticleSerializer(instance=queryset,many=True)
                return Response(ser.data)
            article_object = models.Article.objects.filter(id=pk).first()
            ser = serializer.NewArticleSerializer(instance=article_object, many=False)
            return Response(ser.data)
    
        def post(self,request,*args,**kwargs):
            ser = serializer.FormNewArticleSerializer(data=request.data)
            if ser.is_valid():
                ser.save()
                return Response(ser.data)
            return Response(ser.errors)
    
        def put(self, request, *args, **kwargs):
            """全部更新"""
            pk = kwargs.get('pk')
            article_object = models.Article.objects.filter(id=pk).first()
            ser = serializer.FormNewArticleSerializer(instance=article_object, data=request.data)
            if ser.is_valid():
                ser.save()
                return Response(ser.data)
            return Response(ser.errors)
    
        def patch(self,request,*args,**kwargs):
            """局部"""
            pk = kwargs.get('pk')
            article_object = models.Article.objects.filter(id=pk).first()
            ser = serializer.FormNewArticleSerializer(instance=article_object, data=request.data,partial=True)
            if ser.is_valid():
                ser.save()
                return Response(ser.data)
            return Response(ser.errors)
    
        def delete(self,request,*args,**kwargs):
            pk = kwargs.get('pk')
            models.Article.objects.filter(id=pk).delete()
            return Response('删除成功')
        
    # serializer.py
    
    class NewArticleSerializer(serializers.ModelSerializer):
        tag_info = serializers.SerializerMethodField()
        class Meta:
            model = models.Article
            fields = ['title','summary','tag_info']
    
        def get_tag_info(self,obj):
            return [row for row in obj.tag.all().values('id','title')]
    
    class FormNewArticleSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Article
            fields = '__all__'

5 分页

1 PageNumberPagination

  1. 配置 settings.py

    REST_FRAMEWORK = {
        #指定一页显示几个
        "PAGE_SIZE":2
    }
  2. 在视图的列表页面

    from rest_framework.pagination import PageNumberPagination
    from rest_framework import serializers
    
    class PageArticleSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Article
            fields = "__all__"
    
    class PageArticleView(APIView):
        def get(self,request,*args,**kwargs):
            queryset = models.Article.objects.all()
    
            # 方式一:仅数据
            #页码1、2、3、4.。。。
            #http:127.0.0.1:8000/index/?page=1
            """
            # 分页对象
            page_object = PageNumberPagination()
            # 调用 分页对象.paginate_queryset方法进行分页,得到的结果是分页之后的数据
            # result就是分完页的一部分数据
            result = page_object.paginate_queryset(queryset,request,self)
            # 序列化分页之后的数据
            ser = PageArticleSerializer(instance=result,many=True)
            return Response(ser.data)
            """
            # 方式二:数据 + 分页信息
            """
            page_object = PageNumberPagination()
            result = page_object.paginate_queryset(queryset, request, self)
            ser = PageArticleSerializer(instance=result, many=True)
            return page_object.get_paginated_response(ser.data)
            """
            # 方式三:数据 + 部分分页信息
    
            page_object = PageNumberPagination()
            result = page_object.paginate_queryset(queryset, request, self)
            ser = PageArticleSerializer(instance=result, many=True)
            return Response({'count':page_object.page.paginator.count,'result':ser.data})

2 LimitOffsetPagination

  1. viwes

    from rest_framework.pagination import PageNumberPagination
    from rest_framework.pagination import LimitOffsetPagination
    from rest_framework import serializers
    
    class PageArticleSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Article
            fields = "__all__"
    
    class HulaLimitOffsetPagination(LimitOffsetPagination):
        #设置最大页码,防止二货玩
        max_limit = 2
    #http:127.0.0.1:8000/index/?offset=0&limit=2
    class PageArticleView(APIView):
        def get(self,request,*args,**kwargs):
            queryset = models.Article.objects.all()
    
            page_object = HulaLimitOffsetPagination()
            result = page_object.paginate_queryset(queryset, request, self)
            ser = PageArticleSerializer(instance=result, many=True)
            return Response(ser.data)

3 扩展

  1. drf提供了三种分页,常用的就这两种

    # urls.py
    
    re_path(r'^page/view/article/$', views.PageViewArticleView.as_view()),
    
    # views.py
    
    from rest_framework.generics import ListAPIView
    
    class PageViewArticleSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Article
            fields = "__all__"
    #做了继承分页
    class PageViewArticleView(ListAPIView):
        queryset = models.Article.objects.all()
        serializer_class = PageViewArticleSerializer
    
    # setting.py
    
    REST_FRAMEWORK = {
        "PAGE_SIZE":2,
        "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination"
    }
  2. restful规范

    - URL中一般用名词: 
    	http://www.luffycity.com/article/ (面向资源编程,网络上东西都视为资源)
    - 根据请求不同做不同操作:GET/POST/PUT/DELTE/PATCH
    - 筛选条件,在URL参数中进行传递:
    	http://www.luffycity.com/article/?page=1&category=1
    	
    一般传输的数据格式都是JSON

6 视图

1 呼啦圈

  1. 创建呼啦圈app并在配置文件中注册。

  2. 添加接口

    from django.urls import path,re_path,include
    urlpatterns += [
        re_path(r'^hg/', include('hg.urls')),
    ]
  3. 在app中添加子接口文件。

    from django.urls import path,re_path
    from . import views
    urlpatterns = [
        # 登录
        re_path(r'^login/$', views.LoginView.as_view()),
        # 文章
        re_path(r'^article/$', views.ArticleView.as_view()),
        re_path(r'^article/(?P<pk>\d+)/$', views.OneArticleView.as_view()),
        # 评论
        re_path(r'^comment/$', views.CommentView.as_view()),
    ]
  4. 添加表

    #不会经常变化的值放在内存:choices形式,避免跨表性能低。
    #分表:如果表中列太多/大量内容可以选择水平分表
    from django.db import models
    
    class UserInfo(models.Model):
        """ 用户表 """
        username = models.CharField(verbose_name='用户名',max_length=32)
        password = models.CharField(verbose_name='密码',max_length=64)
        token =models.CharField(verbose_name="登录认证码",max_length=64,null=True,blank=True)
    
    class Article(models.Model):
        """ 文章表 """
        #固定不变的可以写在类中不用创建表。
        category_choices = (
            (1,'咨询'),
            (2,'公司动态'),
            (3,'分享'),
            (4,'答疑'),
            (5,'其他'),
        )
        category = models.IntegerField(verbose_name='分类',choices=category_choices)
        title = models.CharField(verbose_name='标题',max_length=32)
        image = models.CharField(verbose_name='图片路径',max_length=128) # /media/upload/....
        summary = models.CharField(verbose_name='简介',max_length=255)
    	#余数据,就是说评论表中拿数据会降低查询速度。
        comment_count = models.IntegerField(verbose_name='评论数',default=0)
        read_count = models.IntegerField(verbose_name='浏览数',default=0)
    	
        author = models.ForeignKey(verbose_name='作者',to='UserInfo',on_delete=models.CASCADE)
        #创建一个时间类型,auto_now_add代表是否自动添加时间。
        date = models.DateTimeField(verbose_name='创建时间',auto_now_add=True)
    
    class ArticleDetail(models.Model):
        #不一定所以的文章都会被阅读,所以将其放到其他表中会节约查询时间。
        #article创建了一对一的关系。
        article = models.OneToOneField(verbose_name='文章表',to='Article')
        content = models.TextField(verbose_name='内容')
    
    
    class Comment(models.Model):
        """ 评论表 """
        article = models.ForeignKey(verbose_name='文章',to='Article',on_delete=models.CASCADE)
        content = models.TextField(verbose_name='评论')
        user = models.ForeignKey(verbose_name='评论者',to='UserInfo',on_delete=models.CASCADE)
        #这里本来要创建自关联的,可是为了时间就不写了。
        # parent = models.ForeignKey(verbose_name='回复',to='self', null=True,blank=True)
  5. 编写视图

    from django.shortcuts import render
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.pagination import PageNumberPagination
    import uuid
    from rest_framework import serializers
    from rest_framework.generics import ListAPIView, CreateAPIView, DestroyAPIView, UpdateAPIView, RetrieveAPIView,ListCreateAPIView
    from rest_framework.filters import BaseFilterBackend
    from . import models
    from django.db.models import F
    
    from hg.myauth import LoginAuth
    class ArticleSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Article
            # fields = "__all__"
            #不去验证某个字段。
            exclude = ['author', ]
    
    
    class ArticleDetailSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.ArticleDetail
            # fields = "__all__"
            exclude = ['article', ]
    
    
    class ArticleListSerializer(serializers.ModelSerializer):
        date = serializers.SerializerMethodField()
    
        class Meta:
            model = models.Article
            fields = "__all__"
    
        def get_date(self, obj):
            return obj.date.strftime('%Y-%m-%d %H:%M')
    
    
    class PageArticleSerializer(serializers.ModelSerializer):
        #添加几个字段并指定数据原
        content = serializers.CharField(source="articledetail.content")
        author = serializers.CharField(source="author.username")
        category = serializers.CharField(source="get_category_display")
        date = serializers.SerializerMethodField()
     
        class Meta:
            model = models.Article
            fields = "__all__"
    
        def get_date(self, obj):
            print(obj.date, type(obj.date))
            # from datetime import datetime
            return obj.date.strftime('%Y-%m-%d %H:%M')
    
    
    class ArticleView(APIView):
        """ 文章视图类 """
    
        def get(self,request,*args,**kwargs):
            """ 获取文章列表 """
            pk = kwargs.get('pk')
            if not pk:
                condition = {}
                category = request.query_params.get('category')
                if category:
                    condition['category'] = category
                #拿到数据后通过时间降序排列。
                queryset = models.Article.objects.filter(**condition).order_by('-date')
                #做分页
                pager = PageNumberPagination()
                result = pager.paginate_queryset(queryset,request,self)
                ser = ArticleListSerializer(instance=result,many=True)
                return Response(ser.data)
            #取拿后一条文章
            article_object = models.Article.objects.filter(id=pk).first()
            ser = PageArticleSerializer(instance=article_object,many=False)
            return Response(ser.data)
    
        def post(self,request,*args,**kwargs):
            """ 新增文章(应该在后台管理开发)"""
            #向两个表中添加记录
            ser = ArticleSerializer(data=request.data)
            ser_detail = ArticleDetailSerializer(data=request.data)
            if ser.is_valid() and ser_detail.is_valid():
                # 增加文章#添加后会得到一个此纪录的对象
                article_object = ser.save(author_id=1)
                #括号中可以加没有被验证的字段值
                ser_detail.save(article=article_object)
                return Response('添加成功')
            return Response('错误')
    
    
    class CommentSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Comment
            fields = "__all__"
    
    
    class PostCommentSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Comment
            exclude = ['user', "article"]
    
    #查看评论时`http://127.0.0.1:8000/hg/comment/?article=2`
    class CommentView(APIView):
        """ 评论接口 """
    
        def get(self,request,*args,**kwargs):
            """ 评论列表 """
            #query_params内部等于request.GET.get()
            article_id = request.query_params.get('article')
            queryset = models.Comment.objects.filter(article_id=article_id)
            #序列化
            ser = CommentSerializer(instance=queryset,many=True)
            return Response(ser.data)
    
        def post(self,request,*args,**kwargs):
            """ 添加评论 """
            #做表单验证
            ser = PostCommentSerializer(data=request.data)
            if ser.is_valid():
                ser.save(user_id=2)
                return Response('成功')
            return Response('失败')
    
    
    
    
    class LoginView(APIView):
        authentication_classes = []
    
        def post(self, request, *args, **kwargs):
    
            user_obj = models.UserInfo.objects.filter(**request.data)
            if user_obj:
                token = str(uuid.uuid4())
                user_obj.update(token=token)
                Response(token)
            else:
                return Response("用户名或密码错误")
    
    
    class ArticleBaseFilterBackend(BaseFilterBackend):  # 文章筛选类
        def filter_queryset(self, request, queryset, view):
            val = {}
            if request.query_params.get("category"):
                val = {"category": request.query_params.get("category")}
            return queryset.filter(**val).order_by("-date")
    
    
    class ArticleView(ListAPIView, CreateAPIView):
        queryset = models.Article.objects.all()  # 获取全部文章
        serializer_class = ArticleListSerializer  # 展示全部文章信息
        filter_backends = [ArticleBaseFilterBackend]  # 筛选某一类别的文章
    
        def get_serializer_class(self):
            if self.request.method == "POST":
                return ArticleSerializer
            elif self.request.method == "GET":
                return self.serializer_class
    
        def perform_create(self, serializer):  # 重构数据存储方法
            ser_detail = ArticleDetailSerializer(data=self.request.data)  # 文章的内容校验
            if ser_detail.is_valid():
                article_object = serializer.save(author_id=1)  # 文章新增  添加作者id 并返回当前对象
                print(article_object, type(article_object))  # Article object <class 'hg.models.Article'>
                ser_detail.save(article=article_object)
    
    
    class OneArticleView(RetrieveAPIView):
        queryset = models.Article.objects
        serializer_class = PageArticleSerializer
    
        def get(self, request, *args, **kwargs):
            models.Article.objects.filter(pk=kwargs.get("pk")).update(read_count=F("read_count") + 1)
            return super().get(request, *args, **kwargs)
    
    
    class CommentFilter(BaseFilterBackend):
        def filter_queryset(self, request, queryset, view):
            val = {}
            if request.query_params.get("article"):
                val = {"article": request.query_params.get("article")}
            return queryset.filter(**val).order_by("-id")
    
    
    class CommentView(ListCreateAPIView):
        queryset = models.Comment.objects
        serializer_class = CommentSerializer
        filter_backends = [CommentFilter]
    
        def get_serializer_class(self):
            if self.request.method == "POST":
                return PostCommentSerializer
            elif self.request.method == "GET":
                return self.serializer_class
    
        def perform_create(self, serializer):
            serializer.save(user=2, article=self.request.query_params.get("article"))

2 筛选

  1. 案例:在文章列表时候,添加筛选功能。

  2. 访问url

    全部:http://127.0.0.1:8000/hg/article/
    筛选:http://127.0.0.1:8000/hg/article/?category=2
  3. views

    class ArticleView(APIView):
        """ 文章视图类 """
    
        def get(self,request,*args,**kwargs):
            """ 获取文章列表 """
            pk = kwargs.get('pk')
            if not pk:
                #自己写的筛选
                condition = {}
                category = request.query_params.get('category')
                if category:
                    condition['category'] = category
                queryset = models.Article.objects.filter(**condition).order_by('-date')
                
                pager = PageNumberPagination()
                result = pager.paginate_queryset(queryset,request,self)
                ser = ArticleListSerializer(instance=result,many=True)
                return Response(ser.data)
            article_object = models.Article.objects.filter(id=pk).first()
            ser = PageArticleSerializer(instance=article_object,many=False)
            return Response(ser.data)
内置的筛选
  1. views

  from django.shortcuts import render
  from rest_framework.views import APIView
  from rest_framework.response import Response
  from . import models
  
  from rest_framework.filters import BaseFilterBackend
  
  class MyFilterBackend(BaseFilterBackend):
  
      def filter_queryset(self, request, queryset, view):
          val = request.query_params.get('cagetory')
          return queryset.filter(category_id=val)
      
  
  class IndexView(APIView):
  
      def get(self,request,*args,**kwargs):
          # http://www.xx.com/cx/index/
          # models.News.objects.all()
  
          # http://www.xx.com/cx/index/?category=1
          # models.News.objects.filter(category=1)
  
          # http://www.xx.com/cx/index/?category=1
          # queryset = models.News.objects.all()
          # obj = MyFilterBackend()
          # result = obj.filter_queryset(request,queryset,self)
          # print(result)
          
          return Response('...')
  

3 视图

  1. APIView,感觉没提供功能。

  2. GenericAPIView,桥梁,内部定义:get_queryset/get_serilizer/get_page...

  3. ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
  4. views

    from rest_framework.generics import GenericAPIView,ListAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView,RetrieveAPIView
    from rest_framework.filters import BaseFilterBackend
    from rest_framework import serializers
    from rest_framework.pagination import PageNumberPagination
    class NewFilterBackend(BaseFilterBackend):
    
        def filter_queryset(self, request, queryset, view):
            val = request.query_params.get('cagetory')
            return queryset.filter(category_id=val)
        
    class TagSer(serializers.ModelSerializer):
        class Meta:
            model = models.Tag
            fields = "__all__"
    #listapiview里面有get方法查询全部,createapiview里面有post
    class TagView(ListAPIView,CreateAPIView):
        #必须写
        queryset = models.Tag.objects.all()
        #指定做序列化的类
        serializer_class = TagSer
        #指定做筛选
        filter_backends = [NewFilterBackend,]
    
        def get_serializer_class(self):
            # self.request
            # self.args
            # self.kwargs
            #可以在这里判断以不懂的方式来,指定调用不同的类。
            if self.request.method == 'GET':
                return TagSer
            elif self.request.method == 'POST':
                return OtherTagSer
        def perform_create(self,serializer):
            #可以添加额外的字段值
            serializer.save(author=1)
    #RetrieveAPIView单个查询需要带id所以用连个类,实现增删改查。应为list和retricve里面都是get写在一起会被截胡。
    #UpdateAPIView包含了写的全局更新和局部更新,DestroyAPIView是删除。
    class TagDetailView(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
        queryset = models.Tag.objects.all()
        #指定了tagser做验证
        serializer_class = TagSer
        

7 组件

1 请求的封装

  1. 源码解析

    class HttpRequest(object):
    	def __init__(self):
    		pass
    	@propery
    	def GET(self):
    		pass
    	
    	@propery
    	def POST(self):
    		pass
    	
    	@propery
    	def body(self):
    		pass
    
    class Request(object):
    	def __init__(self,request):
            #将传入的对象初始化
    		self._request = request
    	
    	def data(self):
    		if content-type == "application/json"
    			reutrn json.loads(self._request.body.decode('urf-8'))
    		elif content-type == "x-www-...":
    			return self._request.POST
    		
    	def query_params(self):
    		return self._reqeust.GET
    
    #创建对象
    req = HttpRequest()
    #将对象当参数传递
    request = Request(req)
    
    #这样就可以调用两个类中的方法了。
    request.data
    request.query_prams
    request._request.GET
    request._request.POST
    request._request.body
  2. drf入口请求流程:

    • 路由

      urlpatterns = [
          re_path(r'^order/$', views.OrderView.as_view()),
      ]
    • 视图关系

      #源码中的
      class View(object):
      	@classonlymethod
          def as_view(cls, **initkwargs):
              def view(request, *args, **kwargs):
                  return self.dispatch(request, *args, **kwargs)
              return view
      	
      class APIView(View):
      	@classmethod
          def as_view(cls, **initkwargs):
              view = super().as_view(**initkwargs)
              return csrf_exempt(view)
      	def dispatch(self, request, *args, **kwargs):
              # 新request内部包含老request(_reuqest=老request)
              request = self.initialize_request(request, *args, **kwargs)
              self.request = request
              
              self.initial(request, *args, **kwargs)
              # 通过反射执行“get”方法,并传入新的request
              handler = getattr(self, request.method.lower())
              response = handler(request, *args, **kwargs) # get(requst)
              return self.response
          
      #自己写的
      class OrderView(APIView):
          def get(self,request,*args,**kwargs):
              return Response('海狗')
      	
    • 第一步:自动执行dispatch方法,调用initialize_request方法,将老reuqest传入初始化_request(再将其返回)。

    • 第二步:通过反射执行get方法,返回结果。

    • 第三步:视图后面的类的.as_view(),执行并找父类的as_view,执行后将view返回(视图类等于view)。

    • 继承django的view会报csrf的错,为了测试可以使用。自定义去除csrf验证。(.as_view()帮你做了)

      from django.views import View
      from django.views.decorators.csrf import csrf_exempt
      from django.views.decorators import method_decorator
      #一种
      @method_decorator(csrf_exempt,name="dispatch")
      class xxx(View):
          #第二种
          @method_decorator(csrf_exempt)
          def dispatch(self,request,*args,**kwargs):
              return super().dispatch(request,*atgs,**kwargs)
          def get(...):
              pass
          def post(...):
              pass

2 版本

  1. 配置文件

    REST_FRAMEWORK = {
        #每页数据个数
        "PAGE_SIZE":2,
        #使用的分页方法
        "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
        #版本传递方法有两种:URLPathVersioning(推荐),QueryParameterVersioning
        #query需要写xxx/?xxx=v1而url只需要xxx/v1
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
        #指定版本
        "ALLOWED_VERSIONS":['v1','v2'],
        #指定用version来做版本的查找。
        'VERSION_PARAM':'version',
    }
  2. url(主)

    urlpatterns += [
        #用正则接收版本
        re_path(r'^api/(?P<version>\w+)/', include('kka.urls')),
    ]
  3. url(支)

    from django.urls import path,re_path
    from django.contrib import admin
    from . import views
    urlpatterns = [
        re_path(r'^order/$', views.OrderView.as_view()),
    ]
  4. 自定determine_version

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    
    class MyVersion(object):
        def determine_version(self,request,*args,**kwargs):
            #这种等于api的query方法
            #return request.query_params.get("version")
            #这种等于api的url方法
            v = kwargs.get("version")
    class OrderView(APIView):
        #指定要要调用的类
        versioning_class = MyVersion
    
    #配置文件写后会自动执行的,
    class OrderView(APIView):
        def get(self,request,*args,**kwargs):
            #显示版本
            print(request.version)
            #显示版本控制方案
            print(request.versioning_scheme)
            return Response('...')
    
        def post(self,request,*args,**kwargs):
            return Response('post')
  5. 使用api的

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    #配置文件写后会自动执行的,
    class OrderView(APIView):
        def get(self,request,*args,**kwargs):
            #显示版本
            print(request.version)
            #显示版本控制方案
            print(request.versioning_scheme)
            return Response('...')
    
        def post(self,request,*args,**kwargs):
            return Response('post')
  6. 源码流程

    class APIView(View):
        versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
        
    	def dispatch(self, request, *args, **kwargs):
           
            # ###################### 第一步 ###########################
            """
            request,是django的request,它的内部有:request.GET/request.POST/request.method
            args,kwargs是在路由中匹配到的参数,如:
                url(r'^order/(\d+)/(?P<version>\w+)/$', views.OrderView.as_view()),
                http://www.xxx.com/order/1/v2/
            """
            self.args = args
            self.kwargs = kwargs
    
            """
            request = 生成了一个新的request对象,此对象的内部封装了一些值。
            request = Request(request)
                - 内部封装了 _request = 老的request
            """
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
    
            self.headers = self.default_response_headers  # deprecate?
    
            try:
                # ###################### 第二步 ###########################
                self.initial(request, *args, **kwargs)
    
                执行视图函数。。
    	
    	def initial(self, request, *args, **kwargs):
           
            # ############### 2.1 处理drf的版本 ##############
            version, scheme = self.determine_version(request, *args, **kwargs)
            request.version, request.versioning_scheme = version, scheme
    		...
            
        def determine_version(self, request, *args, **kwargs):
            if self.versioning_class is None:
                return (None, None)
            scheme = self.versioning_class() # obj = XXXXXXXXXXXX()
            return (scheme.determine_version(request, *args, **kwargs), scheme)
            
    class OrderView(APIView):
        #指定版本获取方式
        versioning_class = URLPathVersioning
        #自定义get方法
        def get(self,request,*args,**kwargs):
            #显示版本
            print(request.version)
            print(request.versioning_scheme)
            return Response('...')
    
        def post(self,request,*args,**kwargs):
            return Response('post')
  7. URLPathVersioning源码

    class URLPathVersioning(BaseVersioning):
        """
        urlpatterns = [
            url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
           
        ]
        """
        invalid_version_message = _('Invalid version in URL path.')
    
        def determine_version(self, request, *args, **kwargs):
            version = kwargs.get(self.version_param, self.default_version)
            if version is None:
                version = self.default_version
    
            if not self.is_allowed_version(version):
                raise exceptions.NotFound(self.invalid_version_message)
            return version
  8. 推荐

    # urls.py
    
    re_path(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
    re_path(r'^(?P<version>\w+)/users/$', users_list, name='users-list'),
    
    # settings.py
    
    REST_FRAMEWORK = {
        "PAGE_SIZE":2,
        "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
        #写在配置文件中可以实现全局应用(推荐)
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
        "ALLOWED_VERSIONS":['v1','v2'],
        'VERSION_PARAM':'version'
    }
    
    # views.py
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    
    class OrderView(APIView):
    	#在配置文件中不写,写在这里是给此类应用(局部)。不推荐
        versioning_class = URLPathVersioning
        def get(self,request,*args,**kwargs):
            print(request.version)
            print(request.versioning_scheme)
            return Response('...')
    
        def post(self,request,*args,**kwargs):
            return Response('post')

3 认证(面试)

  1. auth

    from rest_framework.authentication import BaseAuthentication
    from . import models
    
    #自定义认证
    class TokenAuthentication(BaseAuthentication):
        def authenticate(self, request):
            token = request.query_params.get('token')
            user_object = models.UserInfo.objects.filter(token=token).first()
            if user_object:
                return (user_object,token)
            return (None,None)
  2. 配置文件

    REST_FRAMEWORK = {
        "PAGE_SIZE":2,
        "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
        "ALLOWED_VERSIONS":['v1','v2'],
        'VERSION_PARAM':'version',
        #指定认证
        "DEFAULT_AUTHENTICATION_CLASSES":["kka.auth.TokenAuthentication",]
    }
  3. url

    from django.urls import path,re_path
    from django.contrib import admin
    from . import views
    urlpatterns = [
        re_path(r'^login/$', views.LoginView.as_view()),
        re_path(r'^order/$', views.OrderView.as_view()),
        re_path(r'^user/$', views.UserView.as_view()),
    ]
  4. 视图

    import uuid
    from django.shortcuts import render
    from django.views import View
    from django.views.decorators.csrf import csrf_exempt
    from django.utils.decorators import method_decorator
    from rest_framework.versioning import URLPathVersioning
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from . import models
    
    class LoginView(APIView):
    
        def post(self,request,*args,**kwargs):
            user_object = models.UserInfo.objects.filter(**request.data).first()
            if not user_object:
                return Response('登录失败')
            random_string = str(uuid.uuid4())
            user_object.token = random_string
            user_object.save()
            return Response(random_string)
    
    class OrderView(APIView):
        #列表为空代表不做认证
        #authentication_classes = []
        def get(self,request,*args,**kwargs):
            #只有user调用时才会执行
            print(request.user)
            print(request.auth)
            return Response('order')
    
    class UserView(APIView):
        #使用配置文件的
        #authentication_classes = [MyAuthentication,]
        def get(self,request,*args,**kwargs):
            print(request.user)
            print(request.auth)
            return Response('user')
  5. 源码分析

    class Request:
    
        def __init__(self, request,authenticators=None):
            #将老request初始化
            self._request = request
            self.authenticators = authenticators or ()
            
    	@property
        def user(self):
            #判断如果没有就执行。
            if not hasattr(self, '_user'):
                with wrap_attributeerrors():
                    self._authenticate()
            #有的话就返回
            return self._user
        
        def _authenticate(self):
            for authenticator in self.authenticators:
                try:
                    user_auth_tuple = authenticator.authenticate(self)
                except exceptions.APIException:
                    self._not_authenticated()
                    raise
    
                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    self.user, self.auth = user_auth_tuple
                    return
            self._not_authenticated()
            
    	@user.setter
        def user(self, value):
            #将值赋给_user
            self._user = value
            self._request.user = value
  6. 代码分析

    class APIView(View):
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
        #自动执行
    	def dispatch(self, request, *args, **kwargs):
    		#封装
            self.args = args
            self.kwargs = kwargs
    		#调用函数
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
    	#执行
    	def initialize_request(self, request, *args, **kwargs):
            
            parser_context = self.get_parser_context(request)
    		
            return Request(
                request,
                parsers=self.get_parsers(),
                #调用函数
                authenticators=self.get_authenticators(), # [MyAuthentication(),]
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
    	#执行
        def get_authenticators(self):
            #循环认证函数并执行和返回信息
            return [ auth() for auth in self.authentication_classes ]
        
    class LoginView(APIView):
        authentication_classes = []
        def post(self,request,*args,**kwargs):
            user_object = models.UserInfo.objects.filter(**request.data).first()
            if not user_object:
                return Response('登录失败')
            random_string = str(uuid.uuid4())
            user_object.token = random_string
            user_object.save()
            return Response(random_string)
    
    class OrderView(APIView):
        # authentication_classes = [TokenAuthentication, ]
        def get(self,request,*args,**kwargs):
            print(request.user)
            print(request.auth)
            if request.user:
                return Response('order')
            return Response('滚')
    
    class UserView(APIView):
        同上
  7. 总结

    当用户发来请求时,找到认证的所有类并实例化成为对象列表,然后将对象列表封装到新的request对象中。
    
    以后在视同中调用request.user
    
    在内部会循环认证的对象列表,并执行每个对象的authenticate方法,该方法用于认证,他会返回两个值分别会赋值给
    request.user/request.auth 

4 权限

  1. 权限判断

    from rest_framework.permissions import BasePermission
    from rest_framework import exceptions
    
    class MyPermission(BasePermission):
        #判断不成功的错误页面的返回信息。
        message = {'code': 10001, 'error': '你没权限'}
        #判断多个
        def has_permission(self, request, view):
            if request.user:
                return True
            #也可以主动推送异常
            # raise exceptions.PermissionDenied({'code': 10001, 'error': '你没权限'})
            return False
    	#判断单个
        def has_object_permission(self, request, view, obj):
            return False
  2. 视图

    class OrderView(APIView):
        permission_classes = [MyPermission,]
        def get(self,request,*args,**kwargs):
            return Response('order')
    
    
    class UserView(APIView):
        permission_classes = [MyPermission, ]
        def get(self,request,*args,**kwargs):
            return Response('user')

     

  3. 配置文件

    REST_FRAMEWORK = {
        "PAGE_SIZE":2,
        "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
        "ALLOWED_VERSIONS":['v1','v2'],
        'VERSION_PARAM':'version',
        "DEFAULT_AUTHENTICATION_CLASSES":["kka.auth.TokenAuthentication",]
        #配置全局权限验证
        "DEFAULT_PERMISSION_CLASSES":["api.xx.MyPermission",]
    }
  4. 源码分析

    class APIView(View):
        #去配置文件取数据
        permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
        #请求来自动执行
        def dispatch(self, request, *args, **kwargs):
            封装request对象
            self.initial(request, *args, **kwargs)
            通过反射执行视图中的方法
    	#执行
    	def initial(self, request, *args, **kwargs):
            版本的处理
            # 认证
            self.perform_authentication(request)
    		
            # 权限判断
            self.check_permissions(request)
            
            
            self.check_throttles(request)
            
        def perform_authentication(self, request):
            request.user
    	#执行定义的权限方法
        def check_permissions(self, request):
            # [对象,对象,]
            for permission in self.get_permissions():
                if not permission.has_permission(request, self):
                    self.permission_denied(request, message=getattr(permission, 'message', None))
        def permission_denied(self, request, message=None):
            if request.authenticators and not request.successful_authenticator:
                raise exceptions.NotAuthenticated()
            raise exceptions.PermissionDenied(detail=message)
            
        def get_permissions(self):
            return [permission() for permission in self.permission_classes]
        
    class UserView(APIView):
        permission_classes = [MyPermission, ]
        
        def get(self,request,*args,**kwargs):
            return Response('user')

5 练习

  1. 创建项目并创建程序

  2. 配置文件

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'api.apps.ApiConfig',
        'rest_framework',
    ]
    
    REST_FRAMEWORK = {
        'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",
        "ALLOWED_VERSIONS":['v1',]
    }
  3. 主接口文件

    from django.urls import path,re_path,include
    from api import urls
    urlpatterns = [
    	re_path(r'api/(?<version>/w+)', include.urls),
    ]
  4. 支接口文件

    from django.urls import path,re_path,include
    from django.contrib import admin
    from api.views import account
    from api.views import article
    
    urlpatterns = [
        re_path(r'^login/', account.LoginView.as_view()),
        re_path(r'^article/', article.ArticleView.as_view()),
    ]
  5. 认证文件

    from rest_framework.authentication import BaseAuthentication
    from . import models
    
    #自定义认证
    class TokenAuthentication(BaseAuthentication):
        def authenticate(self, request):
            token = request.query_params.get('token')
            user_object = models.UserInfo.objects.filter(token=token).first()
            #应为get方式来的不需要做验证
            if request.method =="GET":
                return (None,None)
            if user_object:
                return (user_object,token)
            return (None,None)
  6. 权限文件

    from rest_framework.permissions import BasePermission
    from rest_framework import exceptions
    
    class MyPermission(BasePermission):
        message = {'code': 10001, 'error': '你没权限'}
        def has_permission(self, request, view):
            if request.user:
                return True
            return False
        def has_object_permission(self, request, view, obj):
            return False
  7. 视图文件

    #account.py
    import uuid
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.versioning import URLPathVersioning
    from api import models
    
    
    class LoginView(APIView):
        """
        登录接口
        """
        def post(self,request,*args,**kwargs):
            user_object = models.UserInfo.objects.filter(**request.data).first()
            if not user_object:
                return Response('登录失败')
            random_string = str(uuid.uuid4())
            user_object.token = random_string
            user_object.save()
            return Response(random_string)
    #article.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    class ArticleView(APIView):
        def get(self,request,*args,**kwargs):
            return Response('文章列表')
    
    class ArticleDetailView(APIView):
        def get(self,request,*args,**kwargs):
            return Response('文章列表')

高级认识

1.restful相关

  1. restful规范?

    第一步:整体说restful规范是什么?
    
    第二步:再详细说restful建议
    	1. https代替http,保证数据传输时安全。
    	2. 在url中一般要体现api标识,这样看到url就知道他是一个api。
    		http://www.luffycity.com/api/....(建议,因为他不会存在跨域的问题)
    		http://api.luffycity.com/....
    		假设:
    			前段:https://www.luffycity.com/home
    			后端:https://www.luffycity.com/api/
    	3. 在接口中要体现版本
    		http://www.luffycity.com/api/v1....(建议,因为他不会存在跨域的问题)
    		注意:版本还可以放在请求头中
    			http://www.luffycity.com/api/
    			accept: ...
    			
    	4. restful也称为面向资源编程,视网络上的一切都是资源,对资源可以进行操作,所以一般资源都用名词。
    		http://www.luffycity.com/api/user/
    		
    	5. 如果要加入一些筛选条件,可以添加在url中	
    		http://www.luffycity.com/api/user/?page=1&type=9
    
    	6. 根据method不同做不同操作。
    	
    	7. 返回给用户状态码
    		- 200,成功
    		- 300,301永久 /302临时
    		- 400,403拒绝 /404找不到
    		- 500,服务端代码错误
    		
    		很多公司:
                    def get(self,request,*args,**kwargs):
                        result = {'code':1000,'data':None,'error':None}
                        try:
                            val = int('你好')
                        except Exception as e:
                            result['code'] = 10001
                            result['error'] = '数据转换错误'
    
                        return Response(result)
            
    	8. 返回值
    		GET http://www.luffycity.com/api/user/
    			[
    				{'id':1,'name':'alex','age':19},
    				{'id':1,'name':'alex','age':19},
    			]
    		POST http://www.luffycity.com/api/user/
    			{'id':1,'name':'alex','age':19}
    			
    		GET http://www.luffycity.com/api/user/2/
    			{'id':2,'name':'alex','age':19}
    			
    		PUT http://www.luffycity.com/api/user/2/
    			{'id':2,'name':'alex','age':19}
    		
    		PATCH https//www.luffycity.com/api/user/2/
    			{'id':2,'name':'alex','age':19}
    			
    		DELETE https//www.luffycity.com/api/user/2/
    			空
    	9. 操作异常时,要返回错误信息
    	
    		{
                error: "Invalid API key"
            }
    	10. 对于下一个请求要返回一些接口:Hypermedia AP
    		{
    			'id':2,
    			'name':'alex',
    			'age':19,
    			'depart': "http://www.luffycity.com/api/user/30/"
    		}
  2. 状态码和api接口码

    #里面包含了很多的全局状态码。
    from rest_framework import status
    
    def post(self...):
    	...
        data_info = {
            #写api接口码(是一个约定)
            "code":10001,
            #数据
            "data":random_string
        }
        #response第二个参数加状态码。
        return Response(data_info,status.HTTP_200_OK)
  3. drf框架

    记忆:请求到来之后,先执行视图的dispatch方法。
    
    1. 视图
    2. 版本处理
    3. 认证
    4. 权限
    5. 节流(频率限制)
    6. 解析器
    7. 筛选器
    8. 分页
    9. 序列化
    10. 渲染

2 跨域

  1. 由于浏览器具有“同源策略”的限制。

    1. 如果在同一个域下发送ajax请求,浏览器的同源策略不会阻止。

    2. 如果在不同域下发送ajax,浏览器的同源策略会阻止。

  2. 域相同,永远不会存在跨域。

    1. crm,非前后端分离,没有跨域。

    2. 路飞学城,前后端分离,没有跨域(之前有,现在没有)。

  3. 域不同时,才会存在跨域。

    1. l拉勾网,前后端分离,存在跨域(设置响应头解决跨域)

  4. 随着技术的发展,现在的浏览器可以支持主动设置从而允许跨域请求,即:跨域资源共享(CORS,Cross-Origin Resource Sharing),其本质是设置响应头,使得浏览器允许跨域请求。

解决跨域:CORS

  1. 在跨域时,发送的请求会分为两种:

  2. 简单请求,发一次请求。

    #本质在数据返回值设置响应头
    #设置响应头就可以解决
    from django.shortcuts import render,HttpResponse
    
    def json(request):
        response = HttpResponse("JSONasdfasdf")
        response['Access-Control-Allow-Origin'] = "*"
        return response
  3. 复杂请求,发两次请求:预检请求和请求。

    @csrf_exempt
    def put_json(request):
        response = HttpResponse("JSON复杂请求")
        if request.method == 'OPTIONS':
            # 处理预检
            #访问控制允许来源
            response['Access-Control-Allow-Origin'] = "*"
            #访问控制允许方法
            response['Access-Control-Allow-Methods'] = "PUT"
            return response
        elif request.method == "PUT":
            return response
  4. 条件

    条件:
        1、请求方式:HEAD、GET、POST
        2、请求头信息:
            Accept
            Accept-Language
            Content-Language
            Last-Event-ID
            Content-Type 对应的值是以下三个中的任意一个
                                    application/x-www-form-urlencoded
                                    multipart/form-data
                                    text/plain
     
    注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求
  5. 由于浏览器具有“同源策略”的限制,所以在浏览器上跨域发送Ajax请求时,会被浏览器阻止。

  6. 解决跨域

    1. CORS(跨站资源共享,本质是设置响应头来解决)。

      1. 简单请求:发送一次请求

      2. 复杂请求:发送两次请求

  7. django-cors-headers模块可以帮助我们配置请求头,实现跨域访问

    # 第一步安装模块
    pip install django-cors-headers
    # 第二步添加app
    INSTALLED_APPS = ["corsheaders",]
    # 第三步添加中间件
    MIDDLEWARE = ["corsheaders.middleware.CorsMiddleware",]
    # 第二步添加白名单
    # 设置允许所有的域名都可以访问,或设置白名单。
    # CORS_ORIGIN_ALLOW_ALL = True
    CORS_ORIGIN_WHITELIST = ('*')
    # 允许携带cookie
    CORS_ALLOW_CREDENTIALS = True
    """
    #跨域增加忽略
    CORS_ALLOW_CREDENTIALS = True
    CORS_ORIGIN_ALLOW_ALL = True
    CORS_ORIGIN_WHITELIST = (
        '*'
    )
    
    CORS_ALLOW_METHODS = (
        'DELETE',
        'GET',
        'OPTIONS',
        'PATCH',
        'POST',
        'PUT',
        'VIEW',
    )
    
    CORS_ALLOW_HEADERS = (
        'XMLHttpRequest',
        'X_FILENAME',
        'accept-encoding',
        'authorization',
        'content-type',
        'dnt',
        'origin',
        'user-agent',
        'x-csrftoken',
        'x-requested-with',
        'Pragma',
    )
    
    """

3 drf的访问频率限制

  1. 频率限制在认证、权限之后

  2. 大概流程

    匿名用户,用IP作为用户唯一标记,但如果用户换代理IP,无法做到真正的限制。
    登录用户,用用户名或用户ID做标识。
    具体实现:
    	在django的缓存中 = {
            throttle_anon_1.1.1.1:[100121340,],
            1.1.1.2:[100121251,100120450,]
        }
    
    
        限制:60s能访问3次
        来访问时:
            1.获取当前时间 100121280
            2.100121280-60 = 100121220,小于100121220所有记录删除
            3.判断1分钟以内已经访问多少次了? 4 
            4.无法访问
        停一会
        来访问时:
            1.获取当前时间 100121340
            2.100121340-60 = 100121280,小于100121280所有记录删除
            3.判断1分钟以内已经访问多少次了? 0
            4.可以访问
  3. 配置

    REST_FRAMEWORK = {
        #{'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
        #前面是次数
        "DEFAULT_THROTTLE_RATES":"3/m"
    }
  4. 视图

    from rest_framework.views import APIView
    from rest_framework.response import Response
    #调用类
    from rest_framework.throttling import AnonRateThrottle,BaseThrottle
    
    class ArticleView(APIView):
        throttle_classes = [AnonRateThrottle,]
        def get(self,request,*args,**kwargs):
            return Response('文章列表')
    
    class ArticleDetailView(APIView):
        def get(self,request,*args,**kwargs):
            return Response('文章列表')
  5. 源码分析

    class BaseThrottle:
        """
        Rate throttling of requests.
        """
    
        def allow_request(self, request, view):
            """
            Return `True` if the request should be allowed, `False` otherwise.
            """
            raise NotImplementedError('.allow_request() must be overridden')
    
        def get_ident(self, request):
            """
            Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR
            if present and number of proxies is > 0. If not use all of
            HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR.
            """
            xff = request.META.get('HTTP_X_FORWARDED_FOR')
            remote_addr = request.META.get('REMOTE_ADDR')
            num_proxies = api_settings.NUM_PROXIES
    
            if num_proxies is not None:
                if num_proxies == 0 or xff is None:
                    return remote_addr
                addrs = xff.split(',')
                client_addr = addrs[-min(num_proxies, len(addrs))]
                return client_addr.strip()
    
            return ''.join(xff.split()) if xff else remote_addr
    
        def wait(self):
            """
            Optionally, return a recommended number of seconds to wait before
            the next request.
            """
            return None
    
    
    class SimpleRateThrottle(BaseThrottle):
        """
        A simple cache implementation, that only requires `.get_cache_key()`
        to be overridden.
    
        The rate (requests / seconds) is set by a `rate` attribute on the View
        class.  The attribute is a string of the form 'number_of_requests/period'.
    
        Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')
    
        Previous request information used for throttling is stored in the cache.
        """
        cache = default_cache
        timer = time.time
        cache_format = 'throttle_%(scope)s_%(ident)s'
        scope = None
        THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
    
        def __init__(self):
            if not getattr(self, 'rate', None):
                self.rate = self.get_rate()
            self.num_requests, self.duration = self.parse_rate(self.rate)
    
        def get_cache_key(self, request, view):
            """
            Should return a unique cache-key which can be used for throttling.
            Must be overridden.
    
            May return `None` if the request should not be throttled.
            """
            raise NotImplementedError('.get_cache_key() must be overridden')
    
        def get_rate(self):
            """
            Determine the string representation of the allowed request rate.
            """
            if not getattr(self, 'scope', None):
                msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                       self.__class__.__name__)
                raise ImproperlyConfigured(msg)
    
            try:
                return self.THROTTLE_RATES[self.scope]
            except KeyError:
                msg = "No default throttle rate set for '%s' scope" % self.scope
                raise ImproperlyConfigured(msg)
    
        def parse_rate(self, rate):
            """
            Given the request rate string, return a two tuple of:
            <allowed number of requests>, <period of time in seconds>
            """
            if rate is None:
                return (None, None)
            num, period = rate.split('/')
            num_requests = int(num)
            duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
            return (num_requests, duration)
    
        def allow_request(self, request, view):
            """
            Implement the check to see if the request should be throttled.
    
            On success calls `throttle_success`.
            On failure calls `throttle_failure`.
            """
            if self.rate is None:
                return True
    
            # 获取请求用户的IP
            self.key = self.get_cache_key(request, view)
            if self.key is None:
                return True
    
            # 根据IP获取他的所有访问记录,[]
            self.history = self.cache.get(self.key, [])
    
            self.now = self.timer()
    
            # Drop any requests from the history which have now passed the
            # throttle duration
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            return self.throttle_success()
    
        def throttle_success(self):
            """
            Inserts the current request's timestamp along with the key
            into the cache.
            """
            self.history.insert(0, self.now)
            self.cache.set(self.key, self.history, self.duration)
            return True
    
        def throttle_failure(self):
            """
            Called when a request to the API has failed due to throttling.
            """
            return False
    
        def wait(self):
            """
            Returns the recommended next request time in seconds.
            """
            if self.history:
                remaining_duration = self.duration - (self.now - self.history[-1])
            else:
                remaining_duration = self.duration
    
            available_requests = self.num_requests - len(self.history) + 1
            if available_requests <= 0:
                return None
    
            return remaining_duration / float(available_requests)
    
    
    class AnonRateThrottle(SimpleRateThrottle):
        """
        Limits the rate of API calls that may be made by a anonymous users.
    
        The IP address of the request will be used as the unique cache key.
        """
        scope = 'anon'
    
        def get_cache_key(self, request, view):
            if request.user.is_authenticated:
                return None  # Only throttle unauthenticated requests.
    
            return self.cache_format % {
                'scope': self.scope,
                'ident': self.get_ident(request)
            }

4 jwt

  1. 用于在前后端分离时,实现用户登录相关。

  2. 一般用户认证有2中方式:

  3. token

    1. 用户登录成功之后,生成一个随机字符串,自己保留一分+给前端返回一份。

    2. 以后前端再来发请求时,需要携带字符串。

    3. 后端对字符串进行校验。

      def post(self,request,*args,**kwargs):
              user_object = models.UserInfo.objects.filter(**request.data).first()
              if not user_object:
                  return Response('登录失败')
              random_string = str(uuid.uuid4())
              user_object.token = random_string
              user_object.save()
              return Response(random_string)
  4. jwt

    用户登录成功之后,生成一个随机字符串,给前端。
    	- 生成随机字符串
    		{typ:"jwt","alg":'HS256'}     {id:1,username:'alx','exp':10}
    		98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928374
    		- 类型信息通过base64加密
    		- 数据通过base64加密
    		- 两个密文拼接在h256加密+加盐
    	- 给前端返回
    		98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928375
    
    前端获取随机字符串之后,保留起来。
    以后再来发送请求时,携带98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928375。
    
    后端接受到之后,
    	1.先做时间判断
        2.字符串合法性校验。
  5. 安装pip3 install djangorestframework-jwt

  • JSON Web Tokens,是一种开发的行业标准RFC7519,用于安全的表示双方之间的声明。目前,jwt广泛应用在系统的用户认证方面,特别是现在前后端分离项目。

1.jwt认证流程

  1. 浏览器发送用户名、密码到服务器

  2. 用户名密码判断正确,生成一个随机字符串。

  3. 将token返回给浏览器

  4. 浏览器携带token发来请求

  5. 服务器校验token的合法性,合法则返回数据,否则返回错误信息。

  6. 数据或错误信息。

  7. 传统token方式和jwt在认证方面有什么差异?

    1. 传统token方式

      • 用户登录成功后,服务端生成一个随机token给用户,并且在服务端(数据库或缓存)中保存一份token,以后用户再来访问时需携带token,服务端接收到token之后,去数据库或缓存中进行校验token的是否超时和是否合法。

    2. jwt方式

      • 用户登录成功后,服务端通过jwt生成一个随机token给用户(服务端无需保留token),以后用户再来访问时需携带token,服务端接收到token之后,通过jwt对token进行校验是否超时、是否合法。

2.jwt创建token

2.1 原理
  • jwt的生成token格式如下,即:由.连接的三段字符串组成。

    eyJhbGcioiJIUzI1NiIsInR5cCI6IkpxvCJ9.eyJzdwIioiIxMjM0NTY30DkwIiwibmFtZSI6IkpvaG4gRG91IiwiawF0IjoxNTE2MjM5MDIyfQ.Sf1KxwRJSMeKKF2QT4fwpMelf36P0k6yJV_adqssw5c
  • 生成规则如下:

    • 第一段HEADER部分,固定包含算法和token类型,对此json进行base64url加密,这就是token的第一段。

      {
          "alg”:"HS256"
          "typ":" JWT"
      }
    • 第二段PAYLOAD部分,包含一些数据,对此json进行base64url加密,这就是token的第二段。

      {
          "sub":"1234567890",
      	"name":"John Doe",
      	"iat":1516239022	
      }
    • 第三段SIGNATURE部分,把前两段的base密文通过.拼接起来,然后对其进行HS256加密,再然后对hs256密文进行base64url加密,最终得到token的第三段。

      base64ur1(
      	HMACSHA256(
      		base64urlEncode(header)+"."+base64urlEncode(payload),
      		your-256-bit-secret(秘钥加盐)
          )
      )
  • 最后将三段字符串通过,拼接起来就生成了jwt的token。

  • 注意:base64url加密是先做base64加密,然后再将-替代+及_替代/。

2.2 代码实现
  1. 基于Python的pyjwt模块创建jwt的token。

  2. ·安装

    pip3 install pyjwt
  3. 实现

    import jwt
    import datetime
    from jwt import exceptions
    
    SALT='iv%x6xo717_u9bf_u!9#g#m*)*=ej@bek5)(@u3kh*72+univ='
    
    def create_token():
    	#构造header
    	headers ={
    		'typ':'jwt',
    		'alg': 'H5256'
        }
    	# 构造payload
    	payload={
    		'user_id':1,#自定义用户ID
    		'username':'wupeiqi',#自定义用户名
    		'exp': datetime.datetime.utcnowO + datetime.timedelta(minutes=5)#超时时间
        }
    	result = jwt.encode(payload=payload,key=SALT, algorithm="HS256",headers=headers).decode('utf-8')
    	return result
    
    if__name__=="__main__":
    	token = create_token()
    	print(token)

3.jwt校验token

  • 一般在认证成功后,把jwt生成的token返回给用户,以后用户再次访问时候需要携带token,此时jwt需要对token进行 超时 及 合法性 校验。

  • 获取token之后,会按照以下步骤进行校验:

    • 将token分割成 header_segment、payload_segment、crypto_segment 三部分

      jwt_token=
      "eyJhbGcioi]IUzI1NiISInR5cCI6IkpXVCJ9.eyJzdwIioiIxMjMONTY30DkwIiwibmFt2S16IkpvaG4gRG911iwiawF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36pok6yJV_adqs5w5c"
      
      signing_input, crypto_segment = jwt_token.rsplit(b'.', 1)
      header_segment, payload_segment = signing_input.split(b".",1)
    • 对第一部分header_segment进行base64url解码,得到header

    • 对第二部分payload_segment进行base64url解码,得到payload

    • 对第三部分crypto_segment进行base64解码,得到signature

    • 对第三部分signature部分数据进行合法校验

      • 拼接前两段密文,即:signing_input

      • 从第一段明文获取加密算法,默认:HS256

      • 使用算法+盐对signing_input进行加密,将得到的结果和signature密文进行比较。

        import jwt
        import datetime
        from jwt import exceptions
        
        def get_payload(token):
        	#根据token获取payload
            try:
        		#从token中获取payload【不校验合法性】
                # unverified_payload = iwt.decode(token, None, False)
                #print(unverified_payload)
                
                #从token中获取payload 【校验合法性】
                verified_payload = jwt.decode(token, SALT,True)
                return verified pavload
            except exceptions.ExpiredsignatureError:
                print('token已失效')
            except jwt.DecodeError:
                print('token认证失败')
            except jwt.InvalidTokenError
                print('非法的token')
        if __name__ = '__main__':
            token = "eyJhbGcioiJIUzI1NiIsInR5cCI6IkpxVCJ9.eyJ1eHAiOjE1NZM1NTU1NzksInvzzXJuYw11Ijod3vwzw1xasIsInvzzxjfawoi0jF9.xj-7q5ts6Yg5U155-aU0HJ54KSaeLq5weXMui2IIEJU"
        	payload - get_payload(token)

4.jwt实战

4.1 django 案例
  • 在用户登录成功之后,生成token并返回,用户再次来访问时需携带token。

  • 此示例在django的中间件中对tokne进行校验,内部编写了两个中间件来支持用户通过两种方式传递token。

  • url 传参

    http://www.pythonav.com?token=ey]hbGcioiJIUzI1N...
  • Authorization 请求头

    GET /something/ HTTP/1.1
    Host:pythonav.com
    Authorization:JWT eyJhbGcioiAisFMyNTYiLcAidHlwIj
4.2 案例
  • app中注册

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'api.apps.ApiConfig',
        'rest_framework',
        'rest_framework_jwt'
    ]
  • 接口

    from rest_framework_jwk.views import obtain_jwt_token
    urlpatterns = [
        #使用jwt需要写成这样
        #ObtainJSONWebToken.as_view()
        #里面调用了它,它里面有继承了序列化,序列化里面有个post
        #你也可以自己写
    	re_path(r"^jwt/login/",obtain_jwt_token),    
    ]

     

  • 用户登录

    import uuid
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.versioning import URLPathVersioning
    from rest_framework import status
    
    from api import models
    
    class LoginView(APIView):
        """
        登录接口
        """
        def post(self,request,*args,**kwargs):
    
            # 基于jwt的认证
            # 1.去数据库获取用户信息
            from rest_framework_jwt.settings import api_settings
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
    		#拿到数据库用户信息。
            user = models.UserInfo.objects.filter(**request.data).first()
            #判断没有该用户
            if not user:
                return Response({'code':1000,'error':'用户名或密码错误'})
    		#一些判断和准备
            payload = jwt_payload_handler(user)
            #进行生成随机字符串。
            token = jwt_encode_handler(payload)
            return Response({'code':1001,'data':token})
  • 用户认证

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    # from rest_framework.throttling import AnonRateThrottle,BaseThrottle
    
    
    class ArticleView(APIView):
        # throttle_classes = [AnonRateThrottle,]
    
        def get(self,request,*args,**kwargs):
            # 获取用户提交的token,进行一步一步校验
            import jwt
            from rest_framework import exceptions
            from rest_framework_jwt.settings import api_settings
            jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
            #前端发来的数据拿到随机字符串
            jwt_value = request.query_params.get('token')
            try:
                #判断验证
                payload = jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                #时间过期主动推送错误消息。
                msg = '签名已过期'
                raise exceptions.AuthenticationFailed(msg)
            except jwt.DecodeError:
                #认证码错误主动推送错误消息。
                msg = '认证失败'
                raise exceptions.AuthenticationFailed(msg)
            except jwt.InvalidTokenError:
                raise exceptions.AuthenticationFailed()
            print(payload)
    
            return Response('文章列表')

5 练习

  1. 配置文件

    #这里的都是全局应用
    REST_FRAMEWORK = {
        #版本读取方式
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
        #版本限制
        "ALLOWED_VERSIONS":['v1','v2'],
        #设置user、token为none
        "UNAUTHENTICATED_USER":None,
        "UNAUTHENTICATED_TOKEN":None,
        #配置认证
        "DEFAULT_AUTHENTICATION_CLASSES": ["api.extensions.auth.HulaQueryParamAuthentication",]
    }
    import datetime
    JWT_AUTH = {
        #设置随机字符串的过期时间(源码默认是300s)
        "JWT_EXPIRATION_DELTA":datetime.timedelta(minutes=10)
    }
  2. 路由分支接口

    from django.urls import path,re_path
    from django.contrib import admin
    from .views import account
    from .views import order
    from .views import comment
    
    urlpatterns = [
        re_path(r'^login/$',account.LoginView.as_view()),
        re_path(r'^order/$',order.OrderView.as_view()),
        re_path(r'^comment/$',comment.CommentView.as_view()),
    ]
  3. 数据库表

    from django.db import models
    
    
    class UserInfo(models.Model):
        """ 用户表 """
        username = models.CharField(verbose_name='用户名',max_length=32)
        password = models.CharField(verbose_name='密码',max_length=64)
    
    
    class Article(models.Model):
        """ 文章表 """
        category_choices = (
            (1,'咨询'),
            (2,'公司动态'),
            (3,'分享'),
            (4,'答疑'),
            (5,'其他'),
        )
        category = models.IntegerField(verbose_name='分类',choices=category_choices)
        title = models.CharField(verbose_name='标题',max_length=32)
        image = models.CharField(verbose_name='图片路径',max_length=128) # /media/upload/....
        summary = models.CharField(verbose_name='简介',max_length=255)
    
        comment_count = models.IntegerField(verbose_name='评论数',default=0)
        read_count = models.IntegerField(verbose_name='浏览数',default=0)
    
        author = models.ForeignKey(verbose_name='作者',to='UserInfo',on_delete=models.CASCADE)
        date = models.DateTimeField(verbose_name='创建时间',auto_now_add=True)
    
    
    class ArticleDetail(models.Model):
        article = models.OneToOneField(verbose_name='文章表',to='Article')
        content = models.TextField(verbose_name='内容')
    
    class Comment(models.Model):
        """ 评论表 """
        article = models.ForeignKey(verbose_name='文章',to='Article',on_delete=models.CASCADE)
        content = models.TextField(verbose_name='评论')
        user = models.ForeignKey(verbose_name='评论者',to='UserInfo',on_delete=models.CASCADE)
  4. 认证判断

    import jwt
    from rest_framework import exceptions
    from rest_framework.authentication import BaseAuthentication
    from rest_framework_jwt.settings import api_settings
    from api import models
    
    class HulaQueryParamAuthentication(BaseAuthentication):
        def authenticate(self, request):
            """
            # raise Exception(), 不在继续往下执行,直接返回给用户。
            # return None ,本次认证完成,执行下一个认证
            # return ('x',"x"),认证成功,不需要再继续执行其他认证了,继续往后权限、节流、视图函数
            """
            token = request.query_params.get('token')
            if not token:
                raise exceptions.AuthenticationFailed({'code':10002,'error':"登录成功之后才能操作"})
    
            jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
            try:
                payload = jwt_decode_handler(token)
            except jwt.ExpiredSignature:
                raise exceptions.AuthenticationFailed({'code':10003,'error':"token已过期"})
            except jwt.DecodeError:
                raise exceptions.AuthenticationFailed({'code':10004,'error':"token格式错误"})
            except jwt.InvalidTokenError:
                raise exceptions.AuthenticationFailed({'code':10005,'error':"认证失败"})
    
            jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER
            username = jwt_get_username_from_payload(payload)
            user_object = models.UserInfo.objects.filter(username=username).first()
            return (user_object,token)
  5. 视图

    #account
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework_jwt.settings import api_settings
    from rest_framework.throttling import AnonRateThrottle
    from api import models
    
    
    class LoginView(APIView):
        authentication_classes = []
        def post(self,request,*args,**kwargs):
            # 1.根据用户名和密码检测用户是否可以登录
            user = models.UserInfo.objects.filter(username=request.data.get('username'),password=request.data.get('password')).first()
            if not user:
                return Response({'code':10001,'error':'用户名或密码错误'})
    
            # 2. 根据user对象生成payload(中间值的数据)
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            payload = jwt_payload_handler(user)
    
            # 3. 构造前面数据,base64加密;中间数据base64加密;前两段拼接然后做hs256加密(加盐),再做base64加密。生成token
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            token = jwt_encode_handler(payload)
            return Response({'code': 10000, 'data': token})
    #comment
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    class CommentView(APIView):
        """ 评论相关接口 """
    
        def get(self, request, *args, **kwargs):
            """ 评论列表接口 """
            return Response('所有评论')
    
        def post(self,request,*args,**kwargs):
            """ 新增评论(需要登录才能访问)"""
            return Response('添加评论')
    
        def get_authenticators(self):
            if self.request.method == "GET":
                return []
            elif self.request.method == 'POST':
                #此类中是post都会做认证
                #去找父类的此方法,会循环认证类
                return super().get_authenticators()
    #order
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from api.extensions.auth import HulaQueryParamAuthentication
    
    
    class OrderView(APIView):
        """ 订单相关接口(所都需要登录才能看) """
    
        def get(self, request, *args, **kwargs):
            """ 订单列表接口 """
            print(request.user)
            print(request.auth)
            return Response('订单列表')
    
        def post(self,request,*args,**kwargs):
            """ 订单评论(需要登录才能访问)"""
            return Response('创建订单')
    
    #article
    from rest_framework.views import APIView
    
    class ArticleView(APIView):
        """ 文章相关接口,无需登录就可以查看 """
        authentication_classes = []
        def get(self,request,*args,**kwargs):
            """ 文章列表接口 & 文章详细接口 """
            pass

6 写视图的方法

  • 第一种:原始APIView

    re_path(r'^login/$',account.LoginView.as_view()),
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework_jwt.settings import api_settings
    from rest_framework.throttling import AnonRateThrottle
    from api import models
    
    
    class LoginView(APIView):
        authentication_classes = []
        def post(self,request,*args,**kwargs):
            # 1.根据用户名和密码检测用户是否可以登录
            user = models.UserInfo.objects.filter(username=request.data.get('username'),password=request.data.get('password')).first()
            if not user:
                return Response({'code':10001,'error':'用户名或密码错误'})
    
            # 2. 根据user对象生成payload(中间值的数据)
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            payload = jwt_payload_handler(user)
    
            # 3. 构造前面数据,base64加密;中间数据base64加密;前两段拼接然后做hs256加密(加盐),再做base64加密。生成token
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            token = jwt_encode_handler(payload)
            return Response({'code': 10000, 'data': token})
  • 第二种:ListApiView等

    re_path(r'^article/$',article.ArticleView.as_view()),
    re_path(r'^article/(?P<pk>\d+)/$',article.ArticleDetailView.as_view()),
    from rest_framework.throttling import AnonRateThrottle
    from rest_framework.response import Response
    from rest_framework.generics import ListAPIView,RetrieveAPIView
    from api import models
    from api.serializer.article import ArticleSerializer,ArticleDetailSerializer
    
    class ArticleView(ListAPIView):
        authentication_classes = []
        # throttle_classes = [AnonRateThrottle,]
    
        queryset = models.Article.objects.all()
        serializer_class = ArticleSerializer
    
    class ArticleDetailView(RetrieveAPIView):
        authentication_classes = []
        queryset = models.Article.objects.all()
        serializer_class = ArticleDetailSerializer
  • 第三种:

    re_path(r'^article/$',article.ArticleView.as_view({"get":'list','post':'create'})),
        url(r'^article/(?P<pk>\d+)/$',article.ArticleView.as_view({'get':'retrieve','put':'update','patch':'partial_update','delete':'destroy'}))
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin
    from api.serializer.article import ArticleSerializer,ArticleDetailSerializer
    
    class ArticleView(GenericViewSet,ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin):
        authentication_classes = []
        throttle_classes = [AnonRateThrottle,]
    
        queryset = models.Article.objects.all()
        serializer_class = None
    
        def get_serializer_class(self):
            pk = self.kwargs.get('pk')
            if pk:
                return ArticleDetailSerializer
            return ArticleSerializer

7 drf 相关知识点梳理

  1. 装饰器

    def outer(func):
        def inner(*args,**kwargs):
            return func(*args,**kwargs)
        return inner
    
    @outer
    def index(a1):
        pass
    
    index()
    def outer(func):
        def inner(*args,**kwargs):
            return func(*args,**kwargs)
        return inner
    
    def index(a1):
        pass
    
    index = outer(index)
    
    index()
  2. django中可以免除csrftoken认证

    from django.views.decorators.csrf import csrf_exempt
    from django.shortcuts import HttpResponse
    
    @csrf_exempt
    def index(request):
        return HttpResponse('...')
    
    # index = csrf_exempt(index)
    
    urlpatterns = [
        url(r'^index/$',index),
    ]
    urlpatterns = [
        url(r'^login/$',account.LoginView.as_view()),
    ]
    
    class APIView(View):
        @classmethod
        def as_view(cls, **initkwargs):
            view = super().as_view(**initkwargs)
            view.cls = cls
            view.initkwargs = initkwargs
    
            # Note: session based authentication is explicitly CSRF validated,
            # all other authentication is CSRF exempt.
            return csrf_exempt(view)
  3. 面向对象中基于继承+异常处理来做的约束

    class BaseVersioning:
        def determine_version(self, request, *args, **kwargs):
            raise NotImplementedError("must be implemented")
            
    class URLPathVersioning(BaseVersioning):
    	def determine_version(self, request, *args, **kwargs):
            version = kwargs.get(self.version_param, self.default_version)
            if version is None:
                version = self.default_version
    
            if not self.is_allowed_version(version):
                raise exceptions.NotFound(self.invalid_version_message)
            return version
  4. 面向对象封装

    class Foo(object):
    	def __init__(self,name,age):
    		self.name = name
    		self.age = age 
    		
    obj = Foo('汪洋',18)
    class APIView(View):
        def dispatch(self, request, *args, **kwargs):
    
            self.args = args
            self.kwargs = kwargs
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
    		...
            
    	def initialize_request(self, request, *args, **kwargs):
            """
            Returns the initial request object.
            """
            parser_context = self.get_parser_context(request)
    
            return Request(
                request,
                parsers=self.get_parsers(),
                authenticators=self.get_authenticators(), # [MyAuthentication(),]
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
  5. 面向对象继承

    class View(object):
       	pass
    
    class APIView(View):
        def dispatch(self):
            method = getattr(self,'get')
            method()
    
    class GenericAPIView(APIView):
        serilizer_class = None
        
        def get_seriliser_class(self):
            return self.serilizer_class
    
    class ListModelMixin(object):
        def get(self):
            ser_class = self.get_seriliser_class()
            print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
        pass
    
    class UserInfoView(ListAPIView):
        pass
    
    
    view = UserInfoView()
    view.dispatch()
    class View(object):
       	pass
    
    class APIView(View):
        def dispatch(self):
            method = getattr(self,'get')
            method()
    
    class GenericAPIView(APIView):
        serilizer_class = None
        
        def get_seriliser_class(self):
            return self.serilizer_class
    
    class ListModelMixin(object):
        def get(self):
            ser_class = self.get_seriliser_class()
            print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
        pass
    
    class UserInfoView(ListAPIView):
        serilizer_class = "汪洋"
    
    
    view = UserInfoView()
    view.dispatch()
    class View(object):
       	pass
    
    class APIView(View):
        def dispatch(self):
            method = getattr(self,'get')
            method()
    
    class GenericAPIView(APIView):
        serilizer_class = None
        
        def get_seriliser_class(self):
            return self.serilizer_class
    
    class ListModelMixin(object):
        def get(self):
            ser_class = self.get_seriliser_class()
            print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
        pass
    
    class UserInfoView(ListAPIView):
        
        def get_seriliser_class(self):
            return "咩咩"
    
    view = UserInfoView()
    view.dispatch()
  6. 反射

    class View(object):
    	def dispatch(self, request, *args, **kwargs):
            # Try to dispatch to the right method; if a method doesn't exist,
            # defer to the error handler. Also defer to the error handler if the
            # request method isn't on the approved list.
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)
  7. 发送ajax请求

    $.ajax({
    	url:'地址',
    	type:'GET',
    	data:{...},
    	success:function(arg){
    		console.log(arg);
    	}
    })
  8. 浏览器具有 "同源策略的限制",导致 发送ajax请求 + 跨域 存在无法获取数据。

    • 简单请求,发送一次请求。

    • 复杂请求,先options请求做预检,然后再发送真正请求

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>常鑫的网站</h1>
        <p>
            <input type="button" value="点我" onclick="sendMsg()">
        </p>
        <p>
            <input type="button" value="点他" onclick="sendRemoteMsg()">
        </p>
    
      
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <script>
            function sendMsg() {
                $.ajax({
                    url:'/msg/',
                    type:'GET',
                    success:function (arg) {
                        console.log(arg);
                    }
                })
            }
            function sendRemoteMsg() {
                $.ajax({
                    url:'http://127.0.0.1:8002/json/',
                    type:'GET',
                    success:function (arg) {
                        console.log(arg);
                    }
                })
    
            }
        </script>
    </body>
    </html>
  9. 如何解决ajax+跨域?

    CORS,跨站资源共享,本质:设置响应头。
  10. 常见的Http请求方法

    get
    post
    put
    patch
    delete
    options
  11. http请求中Content-type请起头

    情况一:
        content-type:x-www-form-urlencode
        name=alex&age=19&xx=10
    	
    	request.POST和request.body中均有值。
    	
    情况二:
    	content-type:application/json
        {"name":"ALex","Age":19}
        
        request.POST没值
        request.body有值。
  12. django中F查询

  13. django中获取空Queryset

    models.User.object.all().none()
  14. 基于django的fbv和cbv都能实现遵循restful规范的接口

    def user(request):
        if request.metho == 'GET':
            pass
        
        
    class UserView(View):
        def get()...
        
        def post...
  15. 基于django rest framework框架实现restful api的开发。

    - 免除csrf认证
    - 视图(APIView、ListAPIView、ListModelMinx)
    - 版本
    - 认证
    - 权限
    - 节流
    - 解析器
    - 筛选器
    - 分页
    - 序列化
    - 渲染器
  16. 简述drf中认证流程?

  17. 简述drf中节流的实现原理以及过程?匿名用户/非匿名用户 如何实现频率限制?

  18. GenericAPIView视图类的作用?

    他提供了一些规则,例如:
    
    class GenericAPIView(APIView):
        serializer_class = None
        queryset = None
        lookup_field = 'pk'
        
        filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
        pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
        
        def get_queryset(self):
            return self.queryset
        
        def get_serializer_class(self):
            return self.serializer_class
        
    	def filter_queryset(self, queryset):
            for backend in list(self.filter_backends):
                queryset = backend().filter_queryset(self.request, queryset, self)
            return queryset
        
        @property
        def paginator(self):
            if not hasattr(self, '_paginator'):
                if self.pagination_class is None:
                    self._paginator = None
                else:
                    self._paginator = self.pagination_class()
            return self._paginator
        
    他相当于提供了一些规则,建议子类中使用固定的方式获取数据,例如:
    class ArticleView(GenericAPIView):
        queryset = models.User.objects.all()
        
        def get(self,request,*args,**kwargs):
            query = self.get_queryset()
    
    我们可以自己继承GenericAPIView来实现具体操作,但是一般不会,因为更加麻烦。
    而GenericAPIView主要是提供给drf内部的 ListAPIView、Create....
    class ListModelMixin:
        def list(self, request, *args, **kwargs):
            queryset = self.filter_queryset(self.get_queryset())
    
            page = self.paginate_queryset(queryset)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)
    
            serializer = self.get_serializer(queryset, many=True)
            return Response(serializer.data)
        
    class ListAPIView(mixins.ListModelMixin,GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
    class MyView(ListAPIView):
        queryset = xxxx 
        ser...
    总结:GenericAPIView主要为drf内部帮助我们提供增删改查的类LIstAPIView、CreateAPIView、UpdateAPIView、提供了执行流程和功能,我们在使用drf内置类做CURD时,就可以通过自定义 静态字段(类变量)或重写方法(get_queryset、get_serializer_class)来进行更高级的定制。
  19. jwt以及其优势。

  20. 序列化时many=True和many=False的区别?

  21. 应用DRF中的功能进行项目开发

    *****
    	解析器:request.query_parmas/request.data
    	视图
    	序列化
    	渲染器:Response
    
    ****
    	request对象封装
    	版本处理
    	分页处理
    ***
    	认证
    	权限
    	节流
    • 基于APIView实现呼啦圈

    • 继承ListAPIView+ GenericViewSet,ListModelMixin实现呼啦圈

8 paramiko

  1. 用于帮助开发者通过代码远程连接服务器,并对服务器进行操作。

1.操作远程服务器

  1. 安装pip3 install paramiko

  2. 远程执行命令【用户名和密码】

  3. 远程执行命令【公钥和私钥】(公钥必须提前上传到服务器)

    import paramiko
    
    private_key = paramiko.RSAKey.from_private_key_file(r'C:/Users/Administrator/.ssh/id_rsa')
    
    # 创建SSH对象
    ssh = paramiko.SSHClient()
    # 允许连接不在know_hosts文件中的主机
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 连接服务器
    ssh.connect(hostname='192.168.16.85', port=22, username='root', pkey=private_key)
    
    # 执行命令
    stdin, stdout, stderr = ssh.exec_command('df')
    # 获取命令结果
    result = stdout.read()
    
    # 关闭连接
    ssh.close()
    
    print(result)
  4. 远程上传和下载文件【用户名和密码】

    import paramiko
    
    transport = paramiko.Transport(('192.168.16.85', 22))
    transport.connect(username='root', password='123456')
    sftp = paramiko.SFTPClient.from_transport(transport)
    
    
    # 将location.py 上传至服务器 /tmp/test.py
    # sftp.put('wy.txt', '/data/wy.txt')
    sftp.get('/data/wy.txt', 'xx.txt')
    
    transport.close()
    import paramiko
    
    # 创建SSH对象
    ssh = paramiko.SSHClient()
    
    # 允许连接不在know_hosts文件中的主机
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    
    # 连接服务器
    ssh.connect(hostname='192.168.16.85', port=22, username='root', password='123456')
    
    # 执行命令
    stdin, stdout, stderr = ssh.exec_command('df')
    # 获取命令结果
    result = stdout.read()
    # 关闭连接
    ssh.close()
    
    print(result.decode('utf-8'))
  5. 远程上传和下载文件【公钥和私钥】

    import paramiko
    
    private_key = paramiko.RSAKey.from_private_key_file(r'C:/Users/Administrator/.ssh/id_rsa')
    
    transport = paramiko.Transport(('192.168.16.85', 22))
    transport.connect(username='root', pkey=private_key)
    
    sftp = paramiko.SFTPClient.from_transport(transport)
    # 将location.py 上传至服务器 /tmp/test.py
    # sftp.put('/tmp/location.py', '/tmp/test.py')
    
    # 将remove_path 下载到本地 local_path
    # sftp.get('remove_path', 'local_path')
    
    transport.close()
  6. 通过私钥字符串也可以连接远程服务器。

    key = """-----BEGIN RSA PRIVATE KEY-----
    MIIG5AIBAAKCAYEAu0fkMInsVRnIBSiZcVYhKuccWCh6hapYgB1eSOWZLz3+xFGy
    G5p2z8HgiHzfT838gAm+5OajuyAuE4+fHI77LXSg+pLbr1FhPVKAP+nbsnLgvHty
    ykZmt74CKKvZ08wdM7eUWJbkdpRNWmkwHBi99LeO0zYbHdXQ+m0P9EiWfdacJdAV
    RDVCghQo1/IpfSUECpfQK1Hc0126vI8nhtrvT3V9qF420U1fwW9GJrODl71WRqvJ
    BgSsKsjV16f0RKARESNmtA2vEdvMeutttZoO4FbvZ+iLKpcRM4LGm2+odryr8ijv
    dCPCLVvoDExOPuqP1dgt5MWcCWf6ZNhMwAs/yvRHAKetvo5gtz8YvzwlikopCLM7
    bS6C6woyppMHfIPjoGJ6JuKpeaWtAgugOw/oVvj1rRYoCv48R13NftqhkFD1KD8z
    km9CjDC8hv+2DmIedtjvVwUz2QF4PN/RC/i1jo3+3rbP1DLu9emTHiortBBrpQ5o
    K+y4Rzv+6NusD6DHAgMBAAECggGBAJ4hTaNOUaZpZmI0rZrsxoSbL2ugghNqid9i
    7MFQW89v4TWSZXi5K6iwYw3bohKYMqNJl01fENBnk4AgvJA4ig0PdP0eEzAs3pYQ
    mwlcRIygQvHiqkHwv7pVTS1aLUqQBfgtAazre2xEPCwitOSEX5/JfWcJQEwoxZMt
    k1MIF0mZc67Zy5sT/Vwn+XScnDt2jbsEBFkPfg1aDto3ZYCQS5Aj/D21j0OauUdy
    1SDIYkw1Kivx0IKsX1Kg0S6OOcnX/B6YrJvisrlQDeZnWlTsTyKSVTekIybJjUHE
    ZgLIIbifSbTW1Bv1iCkDAJBd4Cj4txjXPIgea9ylZ39wSDSV5Pxu0t/M3YbdA26j
    quVFCKqskNOC+cdYrdtVSij2Ypwov67HYsXC/w32oKO7tiRqy51LAs/WXMwQeS5a
    8oWDZLiYIntY4TCYTVOvFlLRtXb+1SbwWKjJdjKvdChv4eo/Ov5JEXD2FVbVC/5E
    Qo3jyjIrt1lrwXUdpJa0/iz4UV33wQKBwQDprCPZVCI7yK/BWTmUvCcupotNk6CC
    +QIKDcvVxz63YFD5nXto4uG7ywXR6pEwOwmycO0CBuouvlPdSioQ3RYi6k0EO3Ch
    9dybC5RZ3MENBHROHvU3mp01EWPUYnXAwNpvknujJqfXMxyURZvvox7hOnu/s3m4
    C3eCBrMMg+uqNZDbLqAymw3pMGhHVWjy5oO8eLuLeJv6er+XoSSPNb21Da7StdQS
    fBPQ1H0/+RXnhFJOzANc4mRZcXMCNGVZX6MCgcEAzSz3evuCRQ47AaSOrDd89jAw
    PgpT+PG4gWw1jFZqHTbQ8MUl3YnElOVoaWRdIdDeslg9THg1cs5Yc9RrbIibyQjV
    F9k/DlXGo0F//Mgtmr7JkLP3syRl+EedRbu2Gk67XDrV7XIvhdlsEuSnEK9xOiB6
    ngewM0e4TccqlLsb6u7RNMU9IjMu/iMcBXKsZ9Cr/DENmGQlTaRVt7G6UcAYGNgQ
    toMoCQWjR/HihlZHssLBj9U8uPyD38HKGy2OoXyNAoHBAKQzv9lHYusJ4l+G+IyJ
    DyucAsXX2HJQ0tsHyNYHtg2cVCqkPIV+8UtKpmNVZwMyaWUIL7Q98bA5NKuLIzZI
    dfbBGK/BqStWntgg8fWXx90C5UvEO2MAdjpFZxZmvgJeQuEmWVVTo5v4obubkrF5
    ughhVXZng0AOZsNrO8Suqxsnmww6nn4RMVxNFOoTnbUawTXezUN71HfWa+38Ybl0
    9UNWQyR0e3slz7LurrkWqwrOlBwlBrPtrsCflUbWVOXR6wKBwDFq+Dy14V2SnOG7
    aeXPA5kkaCo5QJqAVglOL+OaWLqqnk6vnXwrl56pVqmz0762WT0phbIqbe02CBX1
    /t3IVYVpTDIPUGG6hTqDJzmSWXGhLFlfD3Ulei3/ycCnAqh5eCUxwp8LVqjtgltW
    mWqqZyIx+nafsW/YgWqyYu4p1wKR/O+x5hSbsWDiwfgJ876ZgyMeCYE/9cAqqb6x
    3webtfId8ICVPIpXwkks2Hu0wlYrFIX5PUPtBjJZsb00DtuUbQKBwF5BfytRZ0Z/
    6ktTfHj1OJ93hJNF9iRGpRfbHNylriVRb+hjXR3LBk8tyMAqR4rZDzfBNfPip5en
    4TBMg8UATf43dVm7nv4PM2e24CRCWXMXYl7G3lFsQF/g7JNUoyr6bZQBf3pQcBw4
    IJ38IcKV+L475tP4rfDrqyJz7mcJ+90a+ai5cSr9XoZqviAqNdhvBq5LjGOLkcdN
    bS0NAVVoGqjqIY/tOd2NMTEF6kVoYfJ7ZJtjxk/R3sdbdtajV3YsAg==
    -----END RSA PRIVATE KEY-----"""
    ​
    import paramiko
    from io import StringIO
    ​
    private_key = paramiko.RSAKey(file_obj=StringIO(key))
    ​
    # 创建SSH对象
    ssh = paramiko.SSHClient()
    # 允许连接不在know_hosts文件中的主机
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 连接服务器
    ssh.connect(hostname='192.168.16.85', port=22, username='root', pkey=private_key)
    ​
    # 执行命令
    stdin, stdout, stderr = ssh.exec_command('df')
    # 获取命令结果
    result = stdout.read()
    ​
    # 关闭连接
    ssh.close()
    ​
    print(result)

2.xshell连接服务器

  1. 用户名和密码

  2. 公钥和私钥(rsa)

    • 生成公钥和私钥

      ssh-keygen.exe -m pem
      ​
      在当前用户家目录会生成: .ssh/id_rsa.pub    .ssh/id_rsa
    • 把公钥放到服务器

      ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.16.85 
    • 以后再连接服务器时,不需要在输入密码

      ssh root@192.168.16.85
posted @ 2023-10-03 10:38  無敗の草  阅读(22)  评论(0)    收藏  举报