28-django高级(一)

要求

- 每周必须交作业

- 开始自己做小项目作为以后的面试项目(可以结组完成)

 

1. django复习

- Http请求本质

浏览器(socket客户端):

2. DNS解析 + 三次握手 + socket.connect(ip,端口)

3. socket.send("http://www.xiaohuar.com/index.html....")

  规则:http协议

    GET请求:

"GET /index.html?k1=1&k2=2 Http/1.1\r\nhost:www.xiaohuar.com\r\ncontent-type:application/json\r\n\r\n"

 

请求头和请求体使用\r\n\r\n分割,前面头,后面是体,请求内容在请求头中

请求会被拼接成:www.xiaohuar.com/index.html?k1=1&k2=2

POST请求:

"POST /index.html Http/1.1\r\nhost:www.xiaohuar.com\r\ncontent-type:application/json\r\n\r\nusername=alex&pwd=123123

 

请求头和请求体使用\r\n\r\n分割,前面头,后面是体,请求内容在请求体中

 

6. 获取相应

响应头,响应体 = data.split('\r\n\r\n')

 

7. 断开连接

网站(socket服务端):

1. 服务端运行: ip,端口

4. 字符串 = server.recv()

头,体 = data.split("\r\n\r\n=")

request.POST.get() 就是从头中获取的信息

5. 服务端响应:

conn.send('......')

响应头:

 

响应体:

7. 断开连接

 

总结:

 

a. Http请求中本质都是字符串

b. Http请求短连接(请求,响应,然后断开连接)

c. 请求和响应都有:头、体

请求:

请求头

\r\n\r\n

请求体

响应:

响应头

\r\n\r\n

响应体

<html>

....

</html>

 

 

- Web框架

Django本质:

socket(wsgiref)PS:Django 本身不包含socket,而是封装了第三方的socketref

解析和封装http请求(*)

 

使用Django

a. 安装

b. 

projcet

django-admin startproject mysite

app

cd mysite

python manage.py startapp app01

 

写代码(****)

 

python manage.py runserver ip:port

 

 

- 写代码

- django运行流程:

模板(html、tpl) + DB数据渲染 = 字符串

- 路由系统 URL

/login/        -> funcname    name='f1'

/login/\d+/    -> funcname    name='f1'

/login/(?P<n>\d+)/    -> funcname    name='f1'

/login/\d+/    -> include('app01.urls')

 

- 视图函数

 

def index(request):

request.GET #请求头中取信息

request.body # 原生的请求体

request.POST # 如果请求头中: content-type: urlencode-form。。。。,才将request.body转换称字典,默认请求头就是这个

- 可能有

- 可能没有【比如改了请求头中的content-type】

request.method

request.Meta

 

request.GET.get()

request.GET.getlist() ===前端用户选择checkbox、select,比如爱好、书籍的作者

request.POST.get()

request.POST.getlist() ===前端用户选择checkbox、select,比如爱好,书籍的作者

 

 

return HttpResponse('字符串/字节')

return render(request,"html路径",{})

return redirect('URL')##返回的也是字符串,字符串里有一个特殊的响应头location:www.baidu.com,会根据location的信息再次发送一个http请求 

 

- 模板

 

- 继承

- 模板语言

for

if

- filter,sample_tag

 

- Models操作

- 创建表: 业务线

 

- models.xx.objects.create(name='欧美')

- models.xx.objects.create(**dic)

 

- models.xx.objects.filter(id__gt=1).delete()

- models.xx.objects.filter(id=1).delete()

- models.xx.objects.exclude(id=1).delete()

ps:过滤条件

id>5     filter(id__gt=5)

id=5     filter(id=5)

id不等于5的   exclude(id=5)

- models.xx.objects.filter(id=1).update(name='ddd')

- models.xx.objects.filter(id=1).update(**dic)

 

- 创建表: 

业务线

主机表

id host port bs

 

# queryset = [对象,对象,...]

- objs = models.xx.objects.all()

  for row in objs:

row.id

row.host

row.port

row.bs.name通过对象的方式跨表,通过 . 的方式区对象的属性

 

# queryset = [{},{},...]

- objs = models.xx.objects.all().values('id','host','port','bs__name') #集合对象是字典,在values中通过__(双下划线)的方式跨表

  for row in objs:

row['id']

row['bs__name']

 

# queryset = [(1,1.1.11,80,'Web'),(),()...]

- objs = models.xx.objects.all().values_list('id','host','port','bs__name') #集合对象是元组,在values_list中通过__(双下划线)的方式跨表

  for row in objs:

row[0]

row[1]

 

 

- 创建表: 

用户表(id, user,pwd,email,mm)

业务线(id, name) # 用户表_set 和用户表的关系是多对多,和主机表的关系是一对多

主机表(id host port bs)

用户业务线关系表(id  uid  bid)   ******

1   22    1

2   22    11

-   obj = modes.userinfo.objects.filter(user='日语哥').first() #假设日语歌的uid是22

obj.mm.add(1)

obj.mm.add(11)

# 日语哥负责的所有业务线【多对多关系】-> [业务线对象,业务线对象,]

obj = models.userinfo.objects.filter(user='日语哥').first()

queryset = obj.mm.all() 

for row in queryset:

row.id

row.name

 

 

- 二手车业务线是由那些人负责

obj = models.business_unit.objects.filter(name='二手车').first()

queryset = obj.userinfo_set.all() #[用户对象,用户对象,]

for row in queryset:

row.user

row.pwd


不能操作第三张表,因为没有这个表对象的类
通过其中一张表的mm字段来操作 表.mm (等价于第三张表)
obj.mm.add(1)

====通过对象的方式关联表记住两点
1,
定义了ManyToMany的表(userinfo),定义的字段为mm:userinfo.mm(定义了的就方便,通过.mm即获取第三张表)
没有定义ManyToMany的表(bussiness_unit):bussiness_unit.userinfo_set(没有定义mm的,得通过.关联表名_set的方式获取第三张表,关联表名得小写???)
解释:
没有定义mm的另一个表有一个隐藏的字段:关联的用户表名_set   (等价于 定义了mtmt表的mtm字段)=====等价于第三张表
备注:
以上两种方式是从两个表开始取的,都取到的是第三张表
适用场景:
一个人负责那些业务线
一个业务线由那些人负责
2,
userinfo.mm.all()获取的是第三张表的所有内容
obj.mm.all() 获取的是和obj这个对象关联的第三张表的所有内容
 

 

 

 

作业:主机管理

 

用户表(id, user,pwd,email,mm)

业务线(id, name) # 用户表_set

主机表(id host port FK(业务线))

用户业务线关系表(id  uid  bid)   ******

 

 

1. 登录(Ajax POST,密码加密)

2. 用户登录基于Session

3. 装饰器

4. 主机列表

host  port  <a>业务线名称</a>

host  port  业务线名称

host  port  业务线名称

host  port  业务线名称

host  port  业务线名称

 

分页

 

PS: 模态对话框编辑

5. 新页面: 当前业务线所有的管理员列表

提示:

编程if判断的时候,简单的逻辑放上面

字符串转字节的两种方法:

一、b'fsdfsaf'

二、'fsdfsf'.encode('utf-8')

timedetla的参数,看源码

2. FBV & CBV

FBV就是之前一直用的,今天主要介绍CBV,CBV 在类中自动做了get和post的判断,进而执行对应的方法(通过类其中的dispatch方法中的反射找到并返回的)。而FBV需要自己判断

CBV示例如下:

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login.html/$', views.LoginView.as_view()), #LoginViewl类的as_view方法

]

views.py

from django.shortcuts import render, HttpResponse
from django.views import View


class LoginView(View):   #继承Django自带的View类
    # 执行父类的dispatch方法,***父类中的self指的不是父类self也是自身****,先找自身,再找父类
    # 父类的dispatch方法通过反射找get,post请求
    def dispatch(self, request, *args, **kwargs):
        print('before')
        response = super(LoginView,self).dispatch(request, *args, **kwargs)
        print('after')    #CBV可以自定义 before 和after,这样,不管是get还是post都会执行before和after,可以用在验证的时候,下面有例子,但是DRF的dispatch不会执行after语句,因为DRF返回的是Response
        return response

    def get(self,request,*args,**kwargs):
        print('GET')
        return render(request,'login.html')

    def post(self,request,*args,**kwargs):
        print('POST')
        return HttpResponse('OK')

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>

<form method="post" action="">
    {% csrf_token%}
    <input type="text" name="user">
    <input type="text" name="pwd">
    <input type="submit" value="提交">
</form>

</body>
</html>

备注:form 表单只能提交get和post方法,但是as_view()  还支持其他的请求方式,但是ajax可以提交任何请求

效果就是通过CBV的方式来处理get和post请求

关于类继承方式寻找dispatch中的反射,补充知识如下

class B:
    def f1(self):
        self.f2()

class A(B):
    def f2(self):
        print(a.f2)

#本质要搞清楚 self到底是谁?
正确的方式
obj=A()
obj.f1()  ==等价于== B.f1(obj)【obj是A()产生的对象】,因为self这时候指的是A()的对象,所以在f1中可以调用f2【如果此时A中没有f2,才会到B中去找】,不会报错


错误的方式
obj=B()
obj.f1()  报错,因为self这时候指的是B()的对象,这个对象中没有f2,所以报错。
总结:搞清楚self是谁,,,,如果没有要实用的方法,本身没有的时候,才会往父类找,但是绝对不会往下找=======


eg1:
class View:
    def dispach(self):
        fun = getattr(self,’get’)
        return func(……)

class LoginView(View):
    def dispach(self):
        respond = super(LoginView,self).dispach()
        return respone


obj = LoginView()
obj.dispach()
执行结果:报错,因为在LoginView 和父类 View中都找不到get方法【优先在obj所属的类( LoginView)中找,如果找不到就去父类(View)中找】

eg2:
class View:
    def dispach(self):
        fun = getattr(self,’get’)
        return func(……)
    def get():
        print 1

class LoginView(View):
    def dispach(self):
        respond = super(LoginView,self).dispach()   ###respone接收到的是父类返回的func()
        return respone    
    def get():
        print 2

obj = LoginView()
obj.dispach()
正确执行 ====输出2

CBV使用场景【验证是否已登录】

实现【CBV结合session验证用户是否登陆】

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login.html$', views.LoginView.as_view()),
    url(r'^index.html$', views.IndexView.as_view()),
]

views.py

from django.shortcuts import render,HttpResponse, redirect
from django.views import View


class LoginView(View):
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView,self).dispatch(request, *args, **kwargs) #注意这块要return,这块其实就是完全执行父类的dispatch方法

    def get(self, request, *args,**kwargs):
        return render(request,'login.html')

    def post(self,request, *args,**kwargs):
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if user == 'alex' and pwd == '123':
            # session_id = asdfasdfasdfasdf
            # asdfasdfasdfasdf = {username: 'alex'}
            request.session['username'] = user  # 这行代码做了如上两个操作
            return redirect('/index.html')
        return render(request, 'login.html',{'msg': '去你的'})


class IndexView(View):
    def dispatch(self, request, *args, **kwargs):
        if request.session.get('username'):
            response = super(IndexView,self).dispatch(request, *args, **kwargs)
            return response
        else:
            return redirect('/login.html')  # 如果认证不通过则重定向到登陆页面
        
    def get(self,request,*args,**kwargs): # 通过get方式,如果认证通过,则显示认证的用户名
        return HttpResponse(request.session['username'])

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!-- action不写,提交到当前所在的地址 -->
    <form method="POST" >
        <input type="text" name="user" />
        <input type="text" name="pwd" />
        <input type="submit" value="提交"/>{{ msg }}
    </form>
</body>
</html>

问题来了,如果有很多页面(类似index.html)都需要验证,怎么解决?

方式一:面向对象单继承的方式实现

urls.py 和 login.html 都同上

views.py

from django.shortcuts import render,HttpResponse, redirect
from django.views import View


# FBV、CBV
class LoginView(View):
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView,self).dispatch(request, *args, **kwargs)  #注意这块要return,同时这块其实就是完全执行父类的dispatch

    def get(self, request, *args,**kwargs):
        return render(request,'login.html')

    def post(self,request, *args,**kwargs):
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if user == 'alex' and pwd == '123':
            # session_id = asdfasdfasdfasdf
            # asdfasdfasdfasdf = {username: 'alex'}
            request.session['username'] = user  # 这行代码做了如上两个操作
            return redirect('/index.html')
        return render(request, 'login.html',{'msg': '去你的'})


# 单继承方式:
class BaseView(View):  # 继承自View,并重写dispatch方法
    def dispatch(self, request, *args, **kwargs):
        if request.session.get('username'):
            response = super(BaseView,self).dispatch(request, *args, **kwargs)
            return response
        else:
            return redirect('/login.html')


class IndexView(BaseView):  # 继承自自定义的BaseView
    def get(self,request,*args,**kwargs):
        return HttpResponse(request.session['username'])

如果需要做session验证,则CBV不要继承View,而是继承BaseView即可。

方式二:面向对象多继承的方式实现【推荐使用这种方式】

urls.py 和 login.html 都同上

views.py

from django.shortcuts import render,HttpResponse, redirect
from django.views import View


# FBV、CBV
class LoginView(View):
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView,self).dispatch(request, *args, **kwargs)

    def get(self, request, *args,**kwargs):
        return render(request,'login.html')

    def post(self,request, *args,**kwargs):
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if user == 'alex' and pwd == '123':
            # session_id = asdfasdfasdfasdf
            # asdfasdfasdfasdf = {username: 'alex'}
            request.session['username'] = user  # 这行代码做了如上两个操作
            return redirect('/index.html')
        return render(request, 'login.html',{'msg': '去你的'})


# 多继承方式:
class BaseView(object):
    def dispatch(self, request, *args, **kwargs):
        if request.session.get('username'):
            response = super(BaseView,self).dispatch(request, *args, **kwargs)  
# 这个self只的是继承的类(比如下面的IndexView类),这个dispatch最终还会找到另一个父类View中的dispatch
return response else: return redirect('/login.html') class IndexView(BaseView,View): # 通过多继承的方式,不影响原来的,要需要验证则继承两个;如果不验证,继承一个View即可 # 为什么要继承View, 因为比如as_view这个方法在BaseView中没有定义,根据继承的原理,要使用的方法会按广度优先查找所有父类(super也是这个道理) # tornado中没有集成session,实现类似的功能时,推荐使用多继承的方式,原因一:第三方开发者自己写类的时候继承object即可,原因二:调用方如果需要使用,则多继承一个类即可。 def get(self,request,*args,**kwargs): return HttpResponse(request.session['username'])

方式三:通过装饰器实现

urls.py 和 login.html 都同上

views.py

from django.shortcuts import render,HttpResponse, redirect
from django.views import View
from django.utils.decorators import method_decorator


# FBV、CBV
class LoginView(View):
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView,self).dispatch(request, *args, **kwargs)

    def get(self, request, *args,**kwargs):
        return render(request,'login.html')

    def post(self,request, *args,**kwargs):
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if user == 'alex' and pwd == '123':
            # session_id = asdfasdfasdfasdf
            # asdfasdfasdfasdf = {username: 'alex'}
            request.session['username'] = user  # 这行代码做了如上两个操作
            return redirect('/index.html')
        return render(request, 'login.html',{'msg': '去你的'})


# 定义装饰器函数
def auth(func):
    def inner(request,*args,**kwargs):
        if request.session.get('username'):
            obj = func(request,*args,**kwargs)
            return obj
        else:
            return redirect('/login.html')
    return inner


# @method_decorator(auth,name='post') #写在类上,则需要通过name参数,指定修饰那个方法 《==等价于==》 写在类内部指定的方法上
# Django 规定必须通过method_decorator这种方式调用装饰器
class IndexView(View):

    @method_decorator(auth)  # 写在类内部指定的方法上,则装饰指定方法
    # Django 规定必须通过method_decorator这种方式调用装饰器
    def get(self,request,*args,**kwargs):
        return HttpResponse(request.session['username'])

总结,可以定义在三个地方,定义在类上指定单个方法和定义在类内部单个方法效果一样,另一种是定义在dispatch上(下个例子中有,或者下面的小结中有)

【特殊情况】当时用csrf_exempt 装饰post方法的时候,只能写在dispatch上(定义在类上指定单个方法和定义在类内部单个方法都不可以),官方的一个bug

urls.py 和 login.html 都同上

views.py

from django.shortcuts import render,HttpResponse, redirect
from django.views import View
from django.views.decorators.csrf import csrf_exempt,csrf_protect
from django.utils.decorators import method_decorator


# FBV、CBV
class LoginView(View):
    @method_decorator(csrf_exempt)   # csrf_exempt 必须定义在dispatch方法上
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView,self).dispatch(request, *args, **kwargs)

    def get(self, request, *args,**kwargs):
        return render(request,'login.html')

    def post(self,request, *args,**kwargs):
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if user == 'alex' and pwd == '123':
            # session_id = asdfasdfasdfasdf
            # asdfasdfasdfasdf = {username: 'alex'}
            request.session['username'] = user  # 这行代码做了如上两个操作
            return redirect('/index.html')
        return render(request, 'login.html',{'msg': '去你的'})

FBV& CBV 小结:

    1. FBV & CBV
        a. 
            FBV -> 函数
            CBV ->- dispatch
                    - get获取/post提交
                    
        b. 应用:登录验证
            
            继承:
                  单继承:
                        # class BaseView(View):
                        #     def dispatch(self, request, *args, **kwargs):
                        #         if request.session.get('username'):
                        #             response = super(BaseView,self).dispatch(request, *args, **kwargs)
                        #             return response
                        #         else:
                        #             return redirect('/login.html')
                        #
                        # class IndexView(BaseView):
                        #
                        #     def get(self,request,*args,**kwargs):
                        #         return HttpResponse(request.session['username'])
                          
                  
                  多继承:
            
                        # 多继承方式:
                        # class BaseView(object):
                        #     def dispatch(self, request, *args, **kwargs):
                        #         if request.session.get('username'):
                        #             response = super(BaseView,self).dispatch(request, *args, **kwargs)
                        #             return response
                        #         else:
                        #             return redirect('/login.html')
                        #
                        # class IndexView(BaseView,View):
                        #
                        #     def get(self,request,*args,**kwargs):
                        #         return HttpResponse(request.session['username'])
        
            装饰器:
                
                def auth(func):
                    def inner(request,*args,**kwargs):
                        if request.session.get('username'):
                            obj = func(request,*args,**kwargs)
                            return obj
                        else:
                            return redirect('/login.html')
                    return inner

                
                # @method_decorator(auth,name='get')
                class IndexView(View):
                    
                    @method_decorator(auth)
                    def dispatch(self, request, *args, **kwargs):
                        if request.session.get('username'):
                            response = super(IndexView,self).dispatch(request, *args, **kwargs)
                            return response
                        else:
                            return redirect('/login.html')

                    @method_decorator(auth)
                    def get(self,request,*args,**kwargs):
                        return HttpResponse(request.session['username'])
                        
                    @method_decorator(csrf_exempt)  # 无效
                    def post(self,request,*args,**kwargs):
                        return HttpResponse(request.session['username'])

                
                特殊:CSRF
                    class IndexView(View):
                    
                        @method_decorator(csrf_exempt)
                        def dispatch(self, request, *args, **kwargs):
                            return super(LoginView,self).dispatch(request, *args, **kwargs)

                
                        def get(self,request,*args,**kwargs):
                            return HttpResponse(request.session['username'])
                            
                        
                        def post(self,request,*args,**kwargs):
                            return HttpResponse(request.session['username'])

 

3. 序列化

方式一:使用serializers

from django.core import serializers

user_list = models.UserInfo.objects.all()
data = serializers.serialize("json", user_list)
data数据如下
[
    {"model": "app01.userinfo", "pk": 1, "fields": {"username": "\u5174\u666e", "password": "123123"}}, 
    {"model": "app01.userinfo", "pk": 2, "fields": {"username": "\u94f6\u79cb\u826f", "password": "666"}}
]
缺点:数据格式比较混乱

方式二:使用json.dump的方式序列化 (数据库取值的时候前提是使用values或者values_list)

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^users.html$', views.users),
    url(r'^get_users.html$', views.get_users),
]

app01/models.py

from django.db import models

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)  #密码64位,因为MD5加密后的长度就是64

app01/views.py

from django.shortcuts import render,HttpResponse
import json
from app01 import models
from django.core import serializers


def users(request):
    return render(request,'users.html')


def get_users(request):
    response = {'status': True, 'data':None, 'msg': None}
    # 利用这种方式,将后台是否执行成功,返回的数据和错误信息分别放在字典中
    try:
        user_list = models.UserInfo.objects.values('id', 'username')
        response['data'] = list(user_list)  # 设置返回的数据
    except Exception as e:
        response['status'] = False  # 设置执行结果状态
        response['msg'] = str(e)    # 设置错误信息

    return HttpResponse(json.dumps(response))

templates/users.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <ul id="user_list_id">

    </ul>
    <script src="/static/jquery-3.2.1.js"></script>  <!-- 要放置在body的最下方 -->
    <script>
        $(function () {     <!-- 页面加载完后自动执行 -->
            initData();
        });

        function initData() {
            $.ajax({
                url: '/get_users.html',
                type: 'GET',
                dataType: 'JSON', // 等价于在success中执行 arg = JSON.parse(arg);
                success:function (arg) {
                    if(arg.status){  // 判断后台是否执行成功
                        /*
                        arg.data = [
                            {id: xxx,username: xxx}
                        ]
                        */
                        $.each(arg.data,function (index,row) {  //循环arg.data的内容,index是索引,row是每行数据
                            // row = {id: xxx,username: xxx}
                            // console.log(row.id,row.username)
                            // var tag = document.createElement('li'); //对象的方式创建标签,还得设置innerHtml
                            var tag = "<li>" + row.username + "</li>"; //字符串拼接的方式创建标签
                            $('#user_list_id').append(tag);
                        })
                    }else{   // 后台执行失败,则返回错误信息给前端
                        alert(arg.msg);
                    }
                }
            })
        }
    </script>

</body>
</html>

备注,渲染模板有两种方式:

1,模板引擎渲染render

2,ajax 取数据,然后js或jquery来添加到页面上【上面的例子采用的是这种方式】

结果:

【特别注意】但是有一点json.dumps不能序列化时间类型的数据,这个时候需要对json.dumps做定制化,如下:

import json
from datetime import date
from datetime import datetime


class JsonCustomEncoder(json.JSONEncoder):  # 对json.JSONEncoder做了一些扩展
    def default(self, field):
        if isinstance(field, datetime):
            return field.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(field, date):
            return field.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, field)


user_list = [
    {'id': 1, 'name': 'alex', 'ctime': datetime.now()},
    {'id': 2, 'name': 'eric', 'ctime': datetime.now()}
]

data = json.dumps(user_list, cls=JsonCustomEncoder)  # cls的默认值是JsonCustomEncoder继承的json.JSONEncoder
print(data)

如果遇到json.dumps遇到其他不能处理的情况,也可以按照上述的方式处理

执行结果

[{"id": 1, "name": "alex", "ctime": "2017-09-11 07:48:51"}, {"id": 2, "name": "eric", "ctime": "2017-09-11 07:48:51"}]

序列化小结

        方式一:
            from django.core import serializers

            user_list = models.UserInfo.objects.all()
            data = serializers.serialize("json", user_list)
            data数据如下
            [
                {"model": "app01.userinfo", "pk": 1, "fields": {"username": "\u5174\u666e", "password": "123123"}},
                {"model": "app01.userinfo", "pk": 2, "fields": {"username": "\u94f6\u79cb\u826f", "password": "666"}}
            ]
            缺点:数据格式比较混乱
            
        方式二:
            前提是使用values或者values_list从数据库中去数据
            user_list = models.UserInfo.objects.values('id','username')
            user_list = list(user_list)   是QuerySet类型变成列表类型
            data = json.dumps(user_list)
            data数据如下
            [
                {"username": "\u5174\u666e", "id": 1}, 
                {"username": "\u94f6\u79cb\u826f", "id": 2}
            ]
            
            方式二的问题是不能序列化时间类型数据,解决方案:对json.dumps做定制:
                
                    import json
                    from datetime import date
                    from datetime import datetime

                    class JsonCustomEncoder(json.JSONEncoder):
                        def default(self, field):
                            if isinstance(field, datetime):
                                return field.strftime('%Y-%m-%d %H:%M:%S')
                            elif isinstance(field, date):
                                return field.strftime('%Y-%m-%d')
                            else:
                                return json.JSONEncoder.default(self, field)


                    user_list = [
                        {'id':1,'name':'alex','ctime': datetime.now()},
                        {'id':2,'name':'eric','ctime': datetime.now()}
                    ]

                    data = json.dumps(user_list,cls=JsonCustomEncoder)
                    print(data)
                    
                    
        总结:
            - 模板渲染
            - Ajax
                - 后端json序列化内容传给前端
                - 前端:js添加到页面

4. Django Form表单

一、django form实现用户增、删、改、查

仅包含一对多

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login.html', views.LoginView.as_view()),
    url(r'^users.html$', views.UsersView.as_view()),
    url(r'^add_user.html', views.AddUserView.as_view()),
    url(r'^edit_user_(\d+).html$', views.EditUserView.as_view()),
    url(r'^del_user_(\d+).html$', views.DelUserView.as_view()),
]

views.py

from django.shortcuts import render,HttpResponse, redirect
from django.views import View
from django.views.decorators.csrf import csrf_exempt,csrf_protect
from django.utils.decorators import method_decorator
from  django.forms import Form
from  django.forms import fields
from  django.forms import  widgets
from app01 import models


class AuthView(object):
    def dispatch(self, request, *args, **kwargs):
        if request.session.get('user_info'):
            response = super(AuthView,self).dispatch(request, *args, **kwargs)
            return response
        else:
            return redirect('/login.html')


# FBV、CBV
class LoginView(View):
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView,self).dispatch(request, *args, **kwargs)

    def get(self, request, *args,**kwargs):
        return render(request,'login.html')

    def post(self,request, *args,**kwargs):
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
        if obj:
            request.session['user_info'] = {'id':obj.id,'username': obj.username}
            return redirect('/users.html')
        return render(request, 'login.html',{'msg': '去你的'})


class UsersView(AuthView,View):
    def get(self,request,*args,**kwargs):
        user_list = models.UserInfo.objects.all()
        return render(request,'users.html',{'user_list':user_list})


class UserForm(Form):
    '''
    user = fields.CharField(
        required=True,
        error_messages={'required':'用户名不能为空'}
    )   # user 要数据库字段名字一致,前端input的name字段
    email = fields.EmailField(
        required=True,
        error_messages={'required':'邮箱不能为空','invalid':'邮箱格式错误'}
    )
    ip = fields.GenericIPAddressField(
        required=True,
        error_messages={'required': 'ip不能为空', 'invalid': 'ip格式错误'}
    )
    user_type = fields.ChoiceField(choices=[(1,'上海'),(2,'北京'),(3,'广州')])
    '''
    username = fields.CharField(
        required=True,
        error_messages={'required': '用户名不能为空'}
    )  # user 要数据库字段名字一致,前端input的name字段
    password = fields.CharField(
        required=True,
        error_messages={'required': '邮箱不能为空', 'invalid': '邮箱格式错误'}
    )

    ut_id = fields.ChoiceField(choices=[])

    # 每次类实例化的时候会执行__init__方法,但是其他字段(比如上面的ut_id)和方法不会执行,只是在加载的时候执行
    # 所以需要将动态字段(比如ut_id的ChoiceField)放在__init__中
    def __init__(self,*args,**kwargs):
        super(UserForm,self).__init__(*args,**kwargs)
        self.fields['ut_id'].choices = models.UserType.objects.values_list('id', 'title')
        # self.fields 指的是类中定义的所有字段【django会把定义的所有字段拷贝到self.fields中】



class AddUserView(AuthView,View):
    def get(self,request,*args,**kwargs):
        form = UserForm()
        return render(request,'add_user.html',{'form':form})

    def post(self,request,*args,**kwargs):
        form = UserForm(data=request.POST)  # 传入用户输入的数据
        # 将用户提交的数据和UserForm中定义规则进行匹配
        if form.is_valid():
            # 把所有正确数据获取到
            print(form.cleaned_data)
            models.UserInfo.objects.create(**form.cleaned_data)
            # 可以这样使用cleaned_data的前提是:用户提交的数据的input中的name【这个和自定义的UserForm类中的字段对应的】和数据库的字段名一致
            return redirect('/users.html')
        else:
            print(form.errors)
            return render(request, 'add_user.html', {'form':form})


class EditUserView(AddUserView,View):
    def get(self, request, pk):
        obj = models.UserInfo.objects.filter(id=pk).first()
        form = UserForm(initial={'username':obj.username,'password':obj.password,'ut_id':obj.ut_id})
        return render(request,'edit_user.html',{'form':form})

    def post(self, request, pk):
        form = UserForm(data=request.POST)
        if form.is_valid():
            models.UserInfo.objects.filter(id=pk).update(**form.cleaned_data)
            return redirect('/users.html')
        else:
            print(form.errors)
            return render(request, 'edit_user.html', {'form': form})


class DelUserView(AuthView, View):
    def get(self, request, pk):
        models.UserInfo.objects.filter(id=pk).delete()
        return redirect('/users.html')

add_user.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>添加用户</h1>
    <form method="post" novalidate>  <!--novalidate不让浏览器做验证,这个验证功能只有个别浏览器才有-->
        {%  csrf_token %}
        <p>
            用户名:{{ form.username }} {{form.errors.username.0}}
        </p>
         <p>
            密码:{{ form.password }} {{form.errors.password.0}}
        </p>
         <p>
            用户类型:{{ form.ut_id }} {{form.errors.ut_id.0}}
        </p>


        <input type="submit" value="提交">
    </form>
</body>
</html>

edit_user.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>编辑用户</h1>
    <form method="post" novalidate>  <!--novalidate不让浏览器做验证,这个验证功能只有个别浏览器才有-->
        {%  csrf_token %}
        <p>
            用户名:{{ form.username }} {{form.errors.username.0}}
        </p>
         <p>
            密码:{{ form.password }} {{form.errors.password.0}}
        </p>
         <p>
            用户类型:{{ form.ut_id }} {{form.errors.ut_id.0}}
        </p>


        <input type="submit" value="提交">
    </form>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!-- action不写,提交到当前所在的地址 -->
    <form method="POST" >
        <input type="text" name="user" />
        <input type="text" name="pwd" />
        <input type="submit" value="提交"/>{{ msg }}
    </form>
</body>
</html>

users.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/add_user.html">添加</a>
    <table border="1">
        <thead>
            <tr>
                <th>ID</th>
                <th>用户名</th>
                <th>密码</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            {% for row in user_list %}
                <tr>
                    <td>{{ row.id }}</td>
                    <td>{{ row.username }}</td>
                    <td>{{ row.password }}</td>
                    <td><a href="/edit_user_{{row.id}}.html">编辑</a> <a>删除</a></td>
                    <!--<td><a href="/edit_user_" + {{row.id}} + ".html">编辑</a> <a>删除</a></td>-->
                    <!--href="/edit_user_" + {{row.id}} + ".html"   # 这种方式不可以,因为渲染后的不是a标签-->
                </tr>
            {% endfor %}

        </tbody>
    </table>
</body>
</html>

数据库表app01_userinfo

数据库表app01_type

最终效果如下【可添加、删除、修改】======【但是只有一对多的关系,比如用户和用户类型,没有多对多,下个例子介绍多对多】

 

添加页面

包含一对多和多对多

 urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login.html$', views.LoginView.as_view()),
    url(r'^users.html$', views.UsersView.as_view()),
    url(r'^add_user.html$', views.AddUserView.as_view()),
    url(r'^edit_user_(\d+).html$', views.EditUserView.as_view()),
    url(r'^del_user_(\d+).html$', views.DelUserView.as_view()),
    url(r'^register.html$', views.register),
]

views.py

from django.shortcuts import render, HttpResponse, redirect
from django.views import View
from django.views.decorators.csrf import csrf_exempt,csrf_protect
from django.utils.decorators import method_decorator
from app01 import models
from django.forms import Form
from django.forms import fields
from django.forms import widgets


class AuthView(object):
    def dispatch(self, request, *args, **kwargs):
        if request.session.get('user_info'):
            response = super(AuthView, self).dispatch(request, *args, **kwargs)
            return response
        else:
            return redirect('/login.html')


# CBV 方式
class LoginView(View):
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView, self).dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return render(request, 'login.html')

    def post(self,request, *args, **kwargs):
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
        if obj:
            request.session['user_info'] = {'id': obj.id, 'username': obj.username}
            return redirect('/users.html')
        return render(request, 'login.html', {'msg': '去你的'})


class UsersView(AuthView, View):
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all()
        return render(request,'users.html',{'user_list':user_list})


class UserForm(Form):
    # 定义了一个类
    username = fields.CharField(
        required=True,  #默认
        error_messages={'required':'用户名不能为空'},  # 自定义错误信息
        widget=widgets.TextInput(attrs={'class':'form-control'})
        # 通过这种方式可以将django自带的form和bootstrap的样式结合

    )
    password = fields.CharField(
        required=True,
        error_messages={'required': '邮箱不能为空','invalid':'邮箱格式错误'}, # 自定义错误信息,错误信息的key自己百度去
        widget = widgets.TextInput(attrs={'class': 'form-control'})
        # 通过这种方式可以将django自带的form和bootstrap的样式结合

    )
    # fields.EmailField()
    # fields.GenericIPAddressField(protocol='ipv4')

    ut_id = fields.ChoiceField(   # ut_id 是ForeignKey自动生成的字段
        choices=[],
        widget=widgets.Select(attrs={'class':'form-control'})  # widgets.Select和bootstrap的样式结合
    )

    role_id = fields.MultipleChoiceField(
        choices=[],
        widget=widgets.SelectMultiple(attrs={'class':'form-control'})  # widgets.SelectMultiple和bootstrap的样式结合
    )

    def __init__(self,*args,**kwargs):  # __new__ 方法,在__init__ 方法之前执行
        super(UserForm,self).__init__(*args,**kwargs)
        # self.fields已经有所有拷贝的字段,每次初始化的时候更新动态数据
        self.fields['ut_id'].choices = models.UserType.objects.values_list('id','title')
        self.fields['role_id'].choices = models.Role.objects.values_list('id','caption')


class AddUserView(AuthView, View):
    def get(self, request, *args, **kwargs):
        form = UserForm()  # 创建类对象,并传递给前端 get的时候不传值
        return render(request, 'add_user.html', {'form': form})  # 包含:form.对象和form.errors到前端

    def post(self, request, *args, **kwargs):
        form = UserForm(data=request.POST)  # post的时候传入请求数据
        # 将用户提交的数据和UserForm中定义规则进行匹配:
        if form.is_valid():   # is_valid   拿到request.POST中的数据和类中定义的字段规则校验
            # 把所有正确数据获取到
            # {'username': 'xxxxx', 'password': 'xxxxx', 'ut_id': '1'}
            # print(form.cleaned_data)
            # {'username': 'xxxxx', 'password': 'xxxxx', 'ut_id': '1',role_id:}
            role_id_list = form.cleaned_data.pop('role_id')  # [1,2]
            obj = models.UserInfo.objects.create(**form.cleaned_data)  # UserInfo表中没有role_id这个字段,所以pop后才可以添加
            obj.rl.add(*role_id_list)  # obj.rl拿到的是和obj关联的第三张表的信息(role_id属于的是第三种表的信息)
            return redirect('/users.html')
        else:
            print(form.errors)
            return render(request, 'add_user.html', {'form': form})  # 包含:form.对象和form.errors到前端


class EditUserView(AuthView, View):

    def get(self, request, pk):
        obj = models.UserInfo.objects.filter(id=pk).first()
        role_id_list = obj.rl.values_list('id')  # obj.rl是从UserInfo关联的第三种表中取数据
        # print('======role_id_list-1',role_id_list)  # <QuerySet [(2,), (3,)]>
        # print('======role_id_list-2',zip(*role_id_list))  # <zip object at 0x107fc2c48>
        # print('======role_id_list-3',list(zip(*role_id_list)))  # [(2, 3)]
        # print('======role_id_list-4',list(zip(*role_id_list))[0])  # (2, 3)
        v = list(zip(*role_id_list))[0] if role_id_list else []
        # 返回给前端的role_id应该是个列表,所以才有上面的zip操作
        form = UserForm(initial={'username': obj.username, 'password': obj.password, 'ut_id': obj.ut_id, 'role_id': v})
        return render(request, 'edit_user.html', {'form': form})

    def post(self,request,pk):
        form = UserForm(data=request.POST)
        if form.is_valid():
            # # {'username': 'xxxxx', 'password': 'xxxxx', 'ut_id': '1',role_id:}
            role_id = form.cleaned_data.pop('role_id')
            # 用户表更新
            query = models.UserInfo.objects.filter(id=pk)
            query.update(**form.cleaned_data)  # UserInfo表中没有role_id这个字段,所以pop后才可以update
            obj = query.first()
            obj.rl.set(role_id)  # set == clear + add 操作
            # obj.rl拿到的是和obj关联的第三张表的信息(role_id属于的是第三种表的信息)

            return redirect('/users.html')
        else:
            print(form.errors)
            return render(request, 'edit_user.html', {'form': form})


class DelUserView(AuthView,View):
    def get(self,request,pk):
        models.UserInfo.objects.filter(id=pk).delete()
        return redirect('/users.html')


# ########################### 注册功能 #################################

class RegisterForm(Form):
    user = fields.CharField(required=True, min_length=6, max_length=18)
    email = fields.EmailField(required=True, min_length=6, max_length=18)
    password = fields.CharField(min_length=12)

import json


def register(request):
    if request.method == 'GET':
        form = RegisterForm()
        return render(request, 'register.html', {'form': form})
    else:
        response = {'status': True, 'data': None,'msg': None}  # 通过这种方式,将后台是否执行成功,执行结果和错误信息一起返回给前端
        form = RegisterForm(request.POST)
        if form.is_valid():
            print(form.cleaned_data)
            # 数据库中添加一条数据
            # return redirect('/login.html') # ajax跳转,错!错!错!!,ajax 不认识 return redirect
            # return HttpResponse(json.dumps(response)) 而是应该用HttpResponse,写在了下面
        else:
            response['status'] = False
            response['msg'] = form.errors   # form.errors 是个对象,但是有__str__方法,所以打印出来是字符串
            # return HttpResponse(json.dumps(response)) 而是应该用HttpResponse,写在了下面
        return HttpResponse(json.dumps(response))

models.py

from django.db import models

class UserType(models.Model):
    """
    用户类型
    """
    title = models.CharField(max_length=32)

    def __str__(self):
        # return:如果不定义这个str方法,前端显示外键ForeignKey的是对象Object
        return self.title


class Role(models.Model):
    caption = models.CharField(max_length=32)

    def __str__(self):
        # return:如果不定义这个str方法,前端显示ManyToMany显示的是对象Object
        return self.caption


class UserInfo(models.Model):
    """
    用户表
    """
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    ut = models.ForeignKey(UserType,null=True,blank=True)
    rl = models.ManyToManyField(Role)

# obj = UserInfo()
# obj.r1.values('id','caption') # [Role,ROle]

templates/add_user.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>添加用户</h1>
    <form method="POST" novalidate>
        {% csrf_token %}
        <!--{{ form.as_p }}  生成form中所有的html标签,as_p 定制性差 -->
        <!--
        通过 {{from.user }}{{form.email}} 可以定制,
        但是没有错误信息(form.errors是所有的错误信息)
        form.errors.users 返回的是一个列表,字段的一个规则对应一个错误信息,因为可以有多个规则,所以会有多个错误信息
        form.errors.users.0 取第一个错误,只要有一个错误,即不可以通过
        -->
        <p>
            用户名: {{ form.username }} {{ form.errors.username.0 }}
        </p>
        <p>
            密码: {{ form.password }}  {{ form.errors.password.0 }}
        </p>
        <p>
            用户类型: {{ form.ut_id }}  {{ form.errors.ut_id.0 }}
        </p>
         <p>
            角色: {{ form.role_id }}  {{ form.errors.role_id.0 }}
        </p>

        <input type="submit" value="提交" />
    </form>
</body>
</html>

templates/edit_user.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>编辑用户</h1>
    <form method="POST" novalidate>
        {% csrf_token %}
        <!--{{ form.as_p }}  生成form中所有的html标签 -->
        <p>
            用户名: {{ form.username }} {{ form.errors.username.0 }}
        </p>
        <p>
            密码: {{ form.password }}  {{ form.errors.password.0 }}
        </p>
        <p>
            用户类型: {{ form.ut_id }}  {{ form.errors.ut_id.0 }}
        </p>
        <p>
            角色: {{ form.role_id }}  {{ form.errors.role_id.0 }}
        </p>

        <input type="submit" value="提交" />
    </form>
</body>
</html>

templates/login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!-- action不写,提交到当前所在的地址 -->
    <form method="POST" >
        <input type="text" name="user" />
        <input type="text" name="pwd" />
        <input type="submit" value="提交"/>{{ msg }}
    </form>
</body>
</html>

templates/register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form id="f1">
        {% csrf_token %}
        <p>用户名:{{ form.user }}</p>
        <!--不能再这块用{{form.errors}}渲染错误,因为ajax提交,页面不会被重新渲染{{form.errors.user.0}}永远为空-->
        <p>密码:{{ form.password }}</p>
        <p>邮箱:{{ form.email }}</p>
        <input type="button" value="提交" onclick="submitForm();" />
    </form>
    <script src="/static/jquery-3.2.1.js"></script>
    <script>
        function submitForm() {
            $('#f1 .error').remove();  //将之前的错误信息清除,否则错误信息会多次追加在后面

            $.ajax({
                url: '/register.html',
                type: 'POST',
                data: $('#f1').serialize(), //serserialize 把所有数据拿到 ,cdrf_token 也拿到
                dataType: 'JSON',
                success:function (arg) {
                    if(arg.status){
                        location.href = "/login.html"; //ajax中通过【location.href跳转到新的页面】
                    }else{
                        /*
                        arg.msg = {
                            email: ['xxxxx',]
                            password: ['xxxxx',]
                            user: ['xxxxx',]
                        }
                         */
                        $.each(arg.msg,function (k,v) {
                            var tag = document.createElement('span');
                            tag.innerHTML = v[0];
                            tag.className = "error"; //通过对象的方式创建标签【推荐】,当然也可以通过字符串拼接的方式
                            // <span class='error'>v[0]</span>
                            $('#f1 input[name="'+k+'"]').after(tag);
                        })
                    }
                }
            })
        }
    </script>
</body>
</html>

templates/users.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/add_user.html">添加</a>
    <table border="1">
        <thead>
            <tr>
                <th>ID</th>
                <th>用户名</th>
                <th>密码</th>
                <th>类型</th>
                <th>角色</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            {% for row in user_list %}
                <tr>
                    <td>{{ row.id }}</td>
                    <td>{{ row.username }}</td>
                    <td>{{ row.password }}</td>
                    <td>{{ row.ut }}</td> <!-- 显示外键ForeignKey,models中UserType定义__str__方法-->
                     <td>
                            {% for role in row.rl.all %}
                                {{ role }}  <!-- 显示ManyToMany,models中Role定义__str__方法-->
                            {% endfor %}
                       </td>
                    <td><a href="/edit_user_{{ row.id }}.html">编辑</a> <a href="/del_user_{{ row.id }}.html">删除</a></td>
                </tr>
            {% endfor %}

        </tbody>
    </table>
</body>
</html>

最终效果:

展示:

 添加:

编辑【注意用户id的传入方式】

删除,应该有个模态对话框的提醒,后续加上。

Django的Form表单小结

Form表单验证(功能1:用户请求验证 + 功能2:生成HTML标签)
    示例:用户管理
        a. 添加用户页面
            - 显示HTML标签
            - 提交:数据验证
            - 成功之后保存
            - 错误显示错误信息
    
    总结:
        1. 创建Form类(本质就是正则表达式的集合)
            
            from django.forms import Form
            from django.forms import fields
            from django.forms import widgets

            class UserForm(Form):
                username = fields.CharField(
                    required=True,
                    error_messages={'required':'用户名不能为空'},
                    widget=widgets.TextInput(attrs={'class':'form-control'})
                )
                password = fields.CharField(
                    required=True,
                    error_messages={'required': '邮箱不能为空','invalid':'邮箱格式错误'},
                    widget = widgets.TextInput(attrs={'class': 'form-control'})
                )
                # fields.EmailField()
                # fields.GenericIPAddressField(protocol='ipv4')

                ut_id = fields.ChoiceField(
                    choices=[],
                    widget=widgets.Select(attrs={'class':'form-control'})
                )

                role_id = fields.MultipleChoiceField(
                    choices=[],
                    widget=widgets.SelectMultiple(attrs={'class':'form-control'})
                )

                def __init__(self,*args,**kwargs):
                    super(UserForm,self).__init__(*args,**kwargs)
                    # self.fields已经有所有拷贝的字段
                    self.fields['ut_id'].choices = models.UserType.objects.values_list('id','title')
                    self.fields['role_id'].choices = models.Role.objects.values_list('id','caption')

        2. 只是生成HTML标签: 添加页面
            form = MyForm()
            
            {{form.xx}}
            
        3. 带默认值的HTML标签: 编辑页面
            form = MyForm(initial={'xx': xxx})
            
            {{form.xx}}
            
        4. 提交数据
            form = MyForm(data=request.POST)
            
            if form.is_valid():
                print(form.cleaned_data)
            else:
                print(form.errors)
                
        问题:下拉框数据无法实时更新
            class UserForm(Form):
                username = fields.CharField(
                    required=True,
                    error_messages={'required':'用户名不能为空'}
                )
                password = fields.CharField(
                    required=True,
                    error_messages={'required': '邮箱不能为空','invalid':'邮箱格式错误'}
                )

                ut_id = fields.ChoiceField(choices=[])

                def __init__(self,*args,**kwargs):
                    super(UserForm,self).__init__(*args,**kwargs)

                    self.fields['ut_id'].choices = models.UserType.objects.values_list('id','title')
                            
                
        
    示例:只用表单验证的功能(Ajax提交),注册&登录
    
    
    定律:
        【个数少,内容少】  
        页面摸态对话框:添加+删除+编辑 =》 ajax(无刷新) + Djaogo Form组件
                                                                    - 用:验证
                                                                    - 生成HTML(可用可不用,因为提交页面不刷新,所以即使是自己写的input,上次输入的数据也会保留)
                                             
        
        
        【适用于:数据个数多;博客】
             新URL方式:添加+删除+编辑 =》 Form标签提交(页面刷新) + + Djaogo Form组件
            
                                                                        - 用:验证功能
                                                                        - 用:生成HTML功能){{form.username}})(不要用自己输入input,无保留上次输入的内容)
    
        
        个人:
        
            - 删除利用模态对话框,确认
            - 添加+修改: 新URL方式

二、Form验证

forms中的CharField 和数据库中的CharField 没关系

forms中的CharField:前端验证的时候有用,要求用户提交的类型是字符串

models中的EmailField 只有在admin和modelForm,但是在数据库中都存放的是字符串

forms中的CharField 和 EmailField 就是django自带的默认的正则表达式方式验证

默认的验证规则

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^userinfo/', views.userinfo),
    url(r'^add_user/', views.add_user),
    url(r'^test/', views.test),
]

app01 views.py

from django.shortcuts import render, HttpResponse, redirect
from app01 import models
from app01.forms import UserInfoForm


def userinfo(request):
    user_list = models.UserInfo.objects.all()
    return render(request, 'userinfo.html', {'user_list':user_list})


def add_user(request):
    if request.method == 'GET':
        form = UserInfoForm()
        return render(request, 'add_user.html', {'form':form})
    else:
        form = UserInfoForm(data=request.POST)
        if form.is_valid():
            models.UserInfo.objects.create(**form.cleaned_data)
            return redirect('/userinfo/')
        return render(request, 'add_user.html', {'form': form})


def test(request):
    print('test......')
    return HttpResponse('OK666')

app01 models.py

# -*- coding: utf-8 -*-
from django.db import models


class Depart(models.Model):
    """
    部门表
    """
    title = models.CharField(max_length=32)  # 数据中的string类型


class UserInfo(models.Model):
    """
    用户表
    """
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)  # admin,ModelForm,数据中的string类型
    phone = models.CharField(max_length=32)
    dp = models.ForeignKey(to='Depart', to_field='id')

app01 forms.py

# -*- coding:utf-8 -*-
from django.forms import Form
from django.forms import fields
from app01 import models


class UserInfoForm(Form):
    name = fields.CharField(
        required=True, # 默认必须要填写
        min_length=6,  # 长度最小为6
        max_length=12  # 长度最大为12
    )    # 要求用户提交数据是字符串
    email = fields.EmailField()  # 要求用户提交数据是字符串,且满足邮箱正则
    phone = fields.CharField()
    dp_id = fields.ChoiceField(
        choices=[]
    )

    def __init__(self,*args,**kwargs):
        # super的init的作用:找到类中的所有静态字段拷贝并且赋值给self.fields
        super(UserInfoForm,self).__init__(*args, **kwargs)
        # 在类的构造方法中更新动态字段
        self.fields['dp_id'].choices = models.Depart.objects.values_list('id', 'title')

templates add_user.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="POST" novalidate>
        {% csrf_token %}
        <p> 用户名:{{ form.name }} {{ form.errors.name.0 }} </p>
        <p> 邮箱:{{ form.email }} {{ form.errors.email.0 }}</p>
        <p> 手机:{{ form.phone }} {{ form.errors.phone.0 }}</p>
        <p> 部门:{{ form.dp_id }} {{ form.errors.dp_id.0 }}</p>
        <input type="submit" value="提交" />
    </form>
</body>
</html>

templates userinfo.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/add_user/">添加</a>
    <ul>
        {% for item in user_list %}
            <li>{{ item }}</li>
        {% endfor %}
    </ul>
</body>
</html>

效果:

自定义验证规则

a. 对象

 

方式一:validators=[RegexValidator(正则表达式,错误信息),RegexValidator(正则表达式,错误信息)] 

 

缺点:只能支持正则表达式

 

特点:可以支持多个正则表达式,从前到后一个个进行验证

 

app01 forms.py

# -*- coding:utf-8 -*-
from django.forms import Form
from django.forms import fields
from app01 import models
from django.core.validators import RegexValidator

class UserInfoForm(Form):
    name = fields.CharField(
        required=True,
        min_length=6,
        max_length=12
    )    # 要求用户提交数据是字符串
    email = fields.EmailField()  # 要求用户提交数据是字符串,且满足邮箱正则
    phone = fields.CharField()
    # 方式一:RegexValidator对象
    phone = fields.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')]  # 自定义验证规则进行验证
    )
    dp_id = fields.ChoiceField(
        choices=[]
    )  #使用dp_id更方便,而不要使用dp,因为models中的外键数据库中存放的是dp_id

    def __init__(self,*args,**kwargs):
        # 找到类中的所有静态字段拷贝并且赋值给self.fields
        super(UserInfoForm,self).__init__(*args,**kwargs)
        self.fields['dp_id'].choices = models.Depart.objects.values_list('id', 'title')

效果:

====

b. 函数方法(了解即可,不用)

 

方式二:validators=[函数,]  ============》理解即可,不建议使用

 

原理:会把用户提交的数据传递给函数的形参,抛出异常内部会捕捉到并显示给前端页面

 

app01 forms.py 

# -*- coding:utf-8 -*-
from django.forms import Form
from django.forms import fields
from app01 import models
import re
from django.core.exceptions import ValidationError


def mobile_validata(value):
    mobile_re = re.compile(r'(13[0-9])|15[0123456789]|17[678]|18[0-9]|14[57][0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手机号格式错误')


class UserInfoForm(Form):
    name = fields.CharField(
        required=True,
        min_length=6,
        max_length=12
    )    # 要求用户提交数据是字符串
    email = fields.EmailField()  # 要求用户提交数据是字符串,且满足邮箱正则
    # 方式二:函数
    phone = fields.CharField(
        validators=[mobile_validata, ]
    )
    dp_id = fields.ChoiceField(
        choices=[]
    )  # 使用dp_id更方便,而不要使用dp,因为models中的外键数据库中存放的是dp_id

    def __init__(self,*args,**kwargs):
        # 找到类中的所有静态字段拷贝并且赋值给self.fields
        super(UserInfoForm,self).__init__(*args,**kwargs)
        self.fields['dp_id'].choices = models.Depart.objects.values_list('id', 'title')

效果

c. clean_字段名称 方法

 

方法三:

 

优点:不仅支持正则表达式,还支持其他验证方式(比如去数据库验证手机号是否存在等)

特点:**********方法中只能取当前字段的值 **********

 

app01 forms.py

 

# -*- coding:utf-8 -*-
from django.forms import Form
from django.forms import fields
from app01 import models
import re
from django.core.exceptions import ValidationError


class UserInfoForm(Form):
    name = fields.CharField(
        required=True,
        min_length=6,
        max_length=12
    )    # 要求用户提交数据是字符串
    email = fields.EmailField()  # 要求用户提交数据是字符串,且满足邮箱正则
    # 方法三:当前类的方法中,方法名称要求: clean_phone方法
    phone = fields.CharField()
    dp_id = fields.ChoiceField(
        choices=[]
    ) #使用dp_id更方便,而不要使用dp,因为models中的外键在数据库中存放的是dp_id

    def __init__(self,*args,**kwargs):
        # 找到类中的所有静态字段拷贝并且赋值给self.fields
        super(UserInfoForm,self).__init__(*args,**kwargs)
        self.fields['dp_id'].choices = models.Depart.objects.values_list('id', 'title')

    def clean_phone(self):
        """
        切勿瞎取值
        :return: 必须有返回值"""
        # 去取用户提交的其他值:可能是错误的,可能是正确,所以不要取,因为可能值还不存在,
        value = self.cleaned_data['phone']  # 此处的self代表的是form,所以可以去到cleaned_data,【钩子方法,只能对当前字段(phone)做操作】
# 但是只能取函数对应的字段!!只能取函数对应的字段!!只能取函数对应的字段
mobile_re
= re.compile(r'(13[0-9])|15[0123456789]|17[678]|18[0-9]|14[57][0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手机号格式错误666') if models.UserInfo.objects.filter(phone=value).count(): raise ValidationError('手机号码已经存在') return value #这个值被以后的clean_data 取到,【可以对返回值做自定义的操作,比如 return value + '6666'】

 

效果

备注:使用方法三,不要瞎取值(只能取当前字段的值),因为可能还没有存在(__dict__ 无序)

方法一和方法三可以同时存在,顺序:先走正则表达式验证,再走钩子方法【clean_字段名 方法】

if form.is_valid():

- 验证规则执行顺序

- 第一个字段的正则,钩子函数(方法中只能取当前字段的值)

- 第二个字段的正则,钩子函数

........

 

备注:第一个字段和第二个字段是哪个字段是无序的,这也是clean_字段名 这个钩子函数不能随意取值的原因。

整体验证【应用场景,确认两次输入的密码是否一致】

Django提供的一个兜底验证方法,即在每一个字段都验证过后,还会再对整体进行验证【钩子方法 :clean方法来实现(必须有返回值)】

重新定义clean方法【在源码(is_valid......error......full_clean....)中可以看到是clean函数是个Hook】,

所以Django整体的验证规则流程是@@@@@@@@@@@@@@@@@@@@@@@@

字段1 正则 + 钩子函数

字段2 正则 + 钩子函数

。。。

。。。

整体验证方法clean

 eg:

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^register/', views.register),
]

app01 views.py

from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from app01.forms import RegisterForm


def register(request):
    if request.method == "GET":
        # form = RegisterForm(initial={'city':[1,2],'name':'alex'})
        form = RegisterForm()
        return render(request,'register.html',{'form':form})
    else:
        form = RegisterForm(request.POST)
        if form.is_valid():
            print(form.cleaned_data['pwd'])
            print(form.cleaned_data['pwd_confirm'])
            # 当然这块也可以验证,两次输入的密码是否一致,但是比较后端,比较low逼,建议还是用clean验证

        return render(request, 'register.html', {'form': form})

app01 forms.py

# -*- coding:utf-8 -*-
from django.forms import Form
from django.forms import fields
from django.forms import widgets


class RegisterForm(Form):
    name = fields.CharField()
    email = fields.EmailField()
    phone = fields.CharField()
    city = fields.ChoiceField(
            choices=[(0,"上海"),(1,'北京')],
            widget=widgets.Select(attrs={'class': 'c1'})
    )
    pwd = fields.CharField()
    pwd_confirm = fields.CharField()

    def clean(self):
        pwd = self.cleaned_data['pwd']
        pwd_confirm = self.cleaned_data['pwd_confirm']
        if pwd == pwd_confirm:   ## 验证两次输入的密码是否一致
            return self.cleaned_data
        else:
            from django.core.exceptions import ValidationError
            # self.add_error('pwd', ValidationError('密码输入不一致'))
            self.add_error('pwd_confirm', ValidationError('密码输入不一致'))  # 给指定字段添加错误信息ValidationErrot()
            return self.cleaned_data

app01 models.py (这块的验证其实和后端数据库的表无关。。。。。)

from django.db import models

class Depart(models.Model):
    """
    部门表
    """
    title = models.CharField(max_length=32) # 数据中的string类型

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = "部门表"   #在admin里显示的时候不显示Depart object 而显示部门表

class Role(models.Model):
    name = models.CharField(max_length=32)

    def __str__(self):
        return self.name
class UserInfo(models.Model):
    """
    用户表
    """
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32) # admin,ModelForm,数据中的string类型
    phone = models.CharField(max_length=32)
    pwd = models.CharField(max_length=64)
    dp = models.ForeignKey(to='Depart',to_field='id')
    roles = models.ManyToManyField("Role")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "用户表"

效果:

看源码参考(查找顺序为:is_valid......error......full_clean....)

_clean_fields

_clean_form

_clean_post(不用,因为它的功能,下面的clean都可以实现)

clean

三、Form常用组件

class RegisterForm(Form):
    name = fields.CharField(
        widget=widgets.TextInput(attrs={'class': 'c1'})
    )

效果

    email = fields.EmailField(
        widget=widgets.EmailInput(attrs={'class':'c1'})
    )

效果

    phone = fields.CharField(
        widget=widgets.Textarea(attrs={'class':'c1'})
    )

效果

    pwd = fields.CharField(
        widget=widgets.PasswordInput(attrs={'class':'c1'})
    )
    pwd_confirm = fields.CharField(
        widget=widgets.PasswordInput(attrs={'class': 'c1'})
    )

效果

    # 单选:select
    city = fields.ChoiceField(
        choices=[(0,"上海"),(1,'北京')],
        widget=widgets.Select(attrs={'class': 'c1'})
    )

效果

    # 多选:select
    city = fields.MultipleChoiceField(
        initial=[1,3], # #默认1,3被选中
        choices=[(1,"上海"),(2,'北京'),(3,'广州'),(4,'深圳')],
        widget=widgets.SelectMultiple(attrs={'class': 'c1'})
    )

效果

    # 单选:checkbox
    city = fields.CharField(
        widget=widgets.CheckboxInput()
    )

效果

    # 多选:checkbox
    city = fields.MultipleChoiceField(
        initial=[1, 3],  # #默认1,3被选中
        choices=[(1, "上海"), (2, '北京'), (3, '广州'), (4, '深圳')],
        widget=widgets.CheckboxSelectMultiple
    )

效果

    # 单选:radio
    city = fields.CharField(
        initial=2, #默认哪个被选中
        widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
    )

效果

 

5. 中间件

1. 本质及应用

中间件的本质是类,类中包含函数。

- 中间件执行时机:请求到来,请求返回时
        - 中间件是一个类:
                def process_request(self,request):
                    print('m2.process_request')

                def process_response(self,request, response):
                    print('m2.prcess_response')
                    return response
                    
        - 应用:
            - 请求日志(如记录所有请求所有请求的url,来源ip等)
            - 用户登录认证(这样就不用再每个视图函数中添加认证的装饰器了!!!)

 

Django中的http请求大致流程示意图如下:

备注

所有的请求的进出都会经过中间件(所以当要对所有请求都做某一处理的时候,可以使用中间件来实现)

request中的META信息记录所有请求数据信息

2. process_request和process_response

如果一个中间件类只定义process_request和process_response,那么大致流程如下:

例子1(正常流程处理):

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^test/', views.test),
    url(r'^login', views.login),
]

app01 views.py

from django.shortcuts import HttpResponse,render


def test(request):
    int('fsfas')  # 主动触发一个异常
    print('test......')
    return HttpResponse('OK666')

def login(request):
    if request.method == 'GET':
        return render(request,'login.html')

md middleware.py

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = "wuxiaoyu"
# Date: 2017/9/17

from django.utils.deprecation import MiddlewareMixin
# deprecation中的类表示即将被废弃,新版Django中如果下面报导入md模块报错,需要下面自己定义这个类


# 当使用上面的from ... import ...报错的时候,在自定义这个类
class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):  # 新版本自定义类的话,这个名字可以随意,但是为了和老版本兼容,所以还这么起名字
            response = self.process_response(request, response)
        return response


class M1(MiddlewareMixin):  # 自定义的中间件要继承自MiddlewareMixin

    def process_request(self,request):
        print('m1.process_request')

    def process_response(self,request,response):
        print('m1.process_respone')
        return response

settings.py

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',
    'md.middleware.M1',  # 注意每行后面都要加逗号
]

当访问http://127.0.0.1:9000/test/的时候,后台打印的日志如下

说明了,请求处理流程是:中间件的process_request------> url路由映射------>视图函数-------->中间件的process_response

每个中间件至少包含两个方法是不对的(包含一个也是可以滴...),简单来说可以包含 process_request,process_respone【必须返回response】或者高级用法可以包含下面的process_view 、process_exception

也可以不写process_reponse,这样只进来的时候执行,出去的时候不执行,csrf就是只定义了process_request的方法,没有定义peocess_response


 

【特殊情况】下如果process_request也设置了返回值

process_request如果有返回值(默认返回None,继续交个下个中间件取处理),则会到此返回。。。。下图中的绿色箭头,所以process_request不能轻易加返回值

应用场景:

1,可以设置ip黑名单,如果访问来源是黑名单中的ip,中间件检测后返回。

2,设置登录验证,把不用登录就能访问的页面,要做一下特殊处理(否则会是死循环),见图下面的例子

例子2(process_request包含返回值的例子):

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^test/', views.test),
]

app01 views.py

from django.shortcuts import HttpResponse,render


def test(request):
    print('test......')
    return HttpResponse('OK666')

md middleware.py

from django.shortcuts import HttpResponse,redirect


class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response


class M1(MiddlewareMixin):

    def process_request(self,request):
        print('m1.process_request')
        return HttpResponse('')

    def process_response(self,request, response):
        print('m1.prcess_response')
        return response

settings.py

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',
    'md.middleware.M1',
]

效果

例子3(process_request 用做登陆验证的例子):

urls.py 和 views.py 同上

md middleware.py

from django.shortcuts import HttpResponse,redirect

class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response


class M1(MiddlewareMixin):

    def process_request(self,request):
        print('m1.process_request')

        if request.path_info == '/login':  # 把不用登录就能访问的页面,
            return None

        user_info = request.session.get('user_info')
        if not user_info:
            return redirect('/login')
            # 重新发起一个请求,又经过M1中间件,导致死循环,解决方法是添加上面的判断

    def process_response(self,request, response):
        print('m1.prcess_response')
        return response

效果

访问任何页面,都可以跳转到login页面,如果登陆后,可直接访问


当包含多个中间件的时候,安装settings.py中MIDDLEWARE定义的中间件顺序,依次向下处理,顺序为:

M1的process_request----->M2的process_request-----> url路由映射----->视图函数-----> M2的process_response ----->M1的process_response

例子3(包含多个中间件的时候):

urls.py 和 views.py同上

md middleware.py

from django.shortcuts import HttpResponse,redirect


class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response


class M1(MiddlewareMixin):

    def process_request(self,request):
        print('m1.process_request')
        # return HttpResponse('')

    def process_response(self,request, response):
        print('m1.prcess_response')
        return response

class M2(MiddlewareMixin):

    def process_request(self,request):
        print('m2.process_request')

    def process_response(self,request, response):
        print('m2.prcess_response')
        return response

settings.py

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',
'md.middleware.M1',
    'md.middleware.M2',
]

效果如下:

后台打印输出

如果此时 class M1 的process_request有返回值, 那么效果和例2一样。


 3. process_view

当包含process_view函数的时候

process_request没有返回值的时候,流程图如下:

例子4(包含process_view且没有返回值):

urls.py 和views.py 同上

md middleware.py

from django.shortcuts import HttpResponse,redirect

class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response


class M1(MiddlewareMixin):

    def process_request(self,request):
        print('m1.process_request')
        # return HttpResponse('')

    def process_view(self, request, callback, callback_args, callback_kwargs):        print('m1.process_view',callback)
        
    def process_response(self,request, response):
        print('m1.prcess_response')
        return response

class M2(MiddlewareMixin):

    def process_request(self,request):
        print('m2.process_request')

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print('m2.process_view',callback)

    def process_response(self,request, response):
        print('m2.prcess_response')
        return response

效果:

后台打印

如果process_view有返回值(下面的视图函数不再执行)  =======》 蓝色箭头指向

例子5(包含process_view且返回值):

urls.py 和views.py 和settings.py 同上

md middleware.py

from django.shortcuts import HttpResponse,redirect

class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response


class M1(MiddlewareMixin):

    def process_request(self,request):
        print('m1.process_request')
        # return HttpResponse('')

    def process_view(self, request, callback, callback_args, callback_kwargs):
        """
        如果有返回值,则不在继续执行,直接到最后一个中间件的response
        """
        print('m1.process_view',callback)
        return HttpResponse('Process View返回值')

    def process_response(self,request, response):
        print('m1.prcess_response')
        return response

class M2(MiddlewareMixin):

    def process_request(self,request):
        print('m2.process_request')

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print('m2.process_view',callback)

    def process_response(self,request, response):
        print('m2.prcess_response')
        return response 

效果

后台打印


 4. process_exception

加了exception后的执行流程【可以定义多个exception,但是只要有一个exception捕获到了错误,则直接执行response】

例子6(包含process_exception且返回值):

urls.py setting.py 同上

views.py

from django.shortcuts import HttpResponse,render


def test(request):
    int('fsfas')  # 主动触发一个异常
    print('test......')
    return HttpResponse('OK666')

md middleware.py

from django.shortcuts import HttpResponse,redirect

class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response


class M1(MiddlewareMixin):

    def process_request(self,request):
        print('m1.process_request')
        # return HttpResponse('')

    def process_view(self, request, callback, callback_args, callback_kwargs):  #callback 其实就是函数名func_name 
        """
        如果有返回值,则不在继续执行,直接到最后一个中间件的response
        """
        print('m1.process_view',callback)
        # return HttpResponse('Process View返回值')

    def process_exception(self,request,exception):
        print('m1.process_exception')

    def process_response(self,request, response):
        print('m1.prcess_response')
        return response

class M2(MiddlewareMixin):

    def process_request(self,request):
        print('m2.process_request')

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print('m2.process_view',callback)

    def process_response(self,request, response):
        print('m2.prcess_response')
        return response

    def process_exception(self,request,exception):
        print('m2.process_exception')
        return HttpResponse('500错误')

效果

可以自定义错误返回页面~~~~ 

中间件流程汇总(网上的图片)

Django中间件

用logging + Middleware记录Django API请求日志

django全局异常捕获保存和输出,logger配置

自己写的同时记录请求日志和异常错误信息,参考:https://www.cnblogs.com/robinunix/articles/11477895.html

6. 缓存

Local-memory caching


Note that each process will have its own private cache instance, which means no cross-process caching is possible. This obviously also means the local memory cache isn’t particularly memory-efficient, so it’s probably not a good choice for production environments. It’s nice for development.



The low-level cache API
To provide thread-safety, a different instance of the cache backend will be returned for each thread.

参考:

https://docs.djangoproject.com/en/2.2/topics/cache/

https://djangobook.com/low-level-cache-api/

https://kite.com/python/docs/django.core.cache.backends.locmem.LocMemCache

由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。

1. 配置

Django中提供了6种缓存方式:

  • 开发调试
  • 内存
  • 文件
  • 数据库
  • Memcache缓存(python-memcached模块)
  • Memcache缓存(pylibmc模块)

a、开发调试

# 此为开始调试用,实际内部不做任何操作
    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.dummy.DummyCache',     # 引擎
                'TIMEOUT': 300,                                               # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
                'OPTIONS':{
                    'MAX_ENTRIES': 300,                                       # 最大缓存个数(默认300)
                    'CULL_FREQUENCY': 3,                                      # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
                },
                'KEY_PREFIX': '',                                             # 缓存key的前缀(默认空)
                'VERSION': 1,                                                 # 缓存key的版本(默认1)
                'KEY_FUNCTION' 函数名                                          # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
            }
        }


    # 自定义key
    def default_key_func(key, key_prefix, version):
        """
        Default function to generate keys.

        Constructs the key used by all other methods. By default it prepends
        the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
        function with custom key making behavior.
        """
        return '%s:%s:%s' % (key_prefix, version, key)

    def get_key_func(key_func):
        """
        Function to decide which key function to use.

        Defaults to ``default_key_func``.
        """
        if key_func is not None:
            if callable(key_func):
                return key_func
            else:
                return import_string(key_func)
        return default_key_func

b、内存

# 此缓存将内容保存至内存的变量中
    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
                'LOCATION': 'unique-snowflake',
            }
        }

    # 注:其他配置同开发调试版本

本地缓存装饰器

import hashlib
import time
import ujson as json


def loc_mem_cache(ttl):
    def decorator(func):
        def wrapper(*args, **kwargs):
            arg = json.dumps({'args': args, 'kwargs': kwargs}, sort_keys=True)
            chksum = hashlib.blake2b(arg.encode('utf-8')).hexdigest()
            cache_data_key = '__loc_mem_cache__data_%s__' % chksum
            cache_ts_key = '__loc_mem_cache__ts_%s__' % chksum
            now = time.time()
            data = getattr(func, cache_data_key, None)
            timestamp = getattr(func, cache_ts_key, 0)
            if not data or timestamp + ttl < now:
                data = func(*args, **kwargs)
                setattr(func, cache_data_key, data)
                setattr(func, cache_ts_key, now)
            return data
        return wrapper
    return decorator


# usage
@loc_mem_cache(10)  #使用这个装饰器可以将返回结果缓存10s,注意这个的前提是在django的settings中开启LocMemCache
def slow_function():
    time.sleep(1.0)
    return 123

c、文件

# 此缓存将内容保存至文件
    # 配置:

        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
                'LOCATION': '/var/tmp/django_cache',
            }
        }
    # 注:其他配置同开发调试版本

d、数据库

# 此缓存将内容保存至数据库

    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
                'LOCATION': 'my_cache_table', # 数据库表
            }
        }

    # 注:执行创建表命令 python manage.py createcachetable

e、Memcache缓存(python-memcached模块)

# 此缓存使用python-memcached模块连接memcache

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': 'unix:/tmp/memcached.sock',
        }
    }   

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }

f、Memcache缓存(pylibmc模块)

# 此缓存使用pylibmc模块连接memcache
    
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': '/tmp/memcached.sock',
        }
    }   

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }

django 默认不支持redis,如果需要使用redis去网上找第三方插件

2、应用

a. 全站使用

使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存

    MIDDLEWARE = [
        'django.middleware.cache.UpdateCacheMiddleware',
        # 其他中间件...
        'django.middleware.cache.FetchFromCacheMiddleware',
    ]

    CACHE_MIDDLEWARE_ALIAS = ""
    CACHE_MIDDLEWARE_SECONDS = ""
    CACHE_MIDDLEWARE_KEY_PREFIX = ""

b. 单独视图缓存

方式一:
        from django.views.decorators.cache import cache_page

        @cache_page(60 * 15)
        def my_view(request):
            ...

    方式二:
        from django.views.decorators.cache import cache_page

        urlpatterns = [
            url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),   ###15*60s 参数是时间
        ]

c、局部视图使用

a. 引入TemplateTag

        {% load cache %}

    b. 使用缓存

        {% cache 5000 缓存key %}
            缓存内容
        {% endcache %}

更多:猛击这里 

缓存使用总结:

- 使用
    - 全局
        MIDDLEWARE = [
            'django.middleware.cache.UpdateCacheMiddleware',
            # 其他中间件
            'django.middleware.cache.FetchFromCacheMiddleware',
        ]
    - 视图函数
    
        from django.views.decorators.cache import cache_page

        @cache_page(10)
        def test1(request):
            import time
            ctime = time.time()
            return render(request,'test1.html',{'ctime':ctime})
    - 局部模板
        {% load cache %}
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
        </head>
        <body>
            <h1>TEST1 -> {{ ctime }}</h1>


            {% cache 10 "asdfasdfasdf" %}
                <h1>TEST1 -> {{ ctime }}</h1>
            {% endcache %}
        </body>
        </html>

 应用举例

备注以下三者独立使用,不要混合使用(个人建议)

1,全局缓存(所有的页面都会被缓存)

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^test1/', views.test1),
    url(r'^test2/', views.test2),
]

views.py

from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from django.views.decorators.cache import cache_page

def test1(request):
    models.Depart.objects.create(title='管公布')
    import time
    ctime = time.time()
    return render(request,'test1.html',{'ctime':ctime})

def test2(request):
    import time
    ctime = time.time()
    return render(request,'test2.html',{'ctime':ctime})

settings.py

MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
'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.cache.FetchFromCacheMiddleware',
]
CACHE_MIDDLEWARE_SECONDS = 5 #这个的优先级要比下面的TIMEOUT的优先级高

....
....
....

CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',# 引擎 'LOCATION': '/tmp/', 'TIMEOUT': 50, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) }, # 'KEY_PREFIX': '', # 缓存key的前缀(默认空) # 'VERSION': 1, # 缓存key的版本(默认1) # 'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】) } }







test1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>TEST1 -> {{ ctime }}</h1>
</body>
</html>

test2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>TEST2 - >{{ ctime }}</h1>
</body>
</html>

http://127.0.0.1:8001/test1/

http://127.0.0.1:8001/test2/

均被缓存5s

2,视图函数缓存

urls.py同上

views.py

from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from django.views.decorators.cache import cache_page

@cache_page(5) #缓存5s
def test1(request):
    models.Depart.objects.create(title='管公布')
    import time
    ctime = time.time()
    return render(request,'test1.html',{'ctime':ctime})


@cache_page(10) #缓存10s
def test2(request):
    import time
    ctime = time.time()
    return render(request,'test2.html',{'ctime':ctime})

settings.py(原生的不要加全局缓存的配置哦)

MIDDLEWARE = [
    #'django.middleware.cache.UpdateCacheMiddleware',
    '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.cache.FetchFromCacheMiddleware',
]

访问

http://127.0.0.1:8001/test1/   缓存5s

http://127.0.0.1:8001/test1/   缓存10s

3,局部模板缓存

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^test3/', views.test3),
]

views.py

from django.shortcuts import render,HttpResponse,redirect
from app01 import models

def test3(request):
    import time
    ctime = time.time()
    return render(request,'test3.html',{'ctime':ctime})

settings.py(原生的不要加全局缓存的配置哦)

MIDDLEWARE = [
    #'django.middleware.cache.UpdateCacheMiddleware',
    '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.cache.FetchFromCacheMiddleware',
]

test3.html

{% load cache %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>TEST1 -> {{ ctime }}</h1>


    {% cache 10 "asdfasdfasdf" %}
        <h1>TEST1 -> {{ ctime }}</h1>
    {% endcache %}
</body>
</html>

访问

http://127.0.0.1:8001/test3/

 

#############

可以在自定义中间件,继承这个类UPdateCacheMiddlewre,遇到某些url return None,则不缓存了(类似于登录的处理)???

##################

cache_middleware_seconds=10 比 下面全局定义的要高

 

流程

设置缓存的中间件位置

有process_request 的在最下面

有process_rresponse 的在最上面

 

7. 信号

问题:如何在数据库中做增加操作时,记录日志

答案:使用django的信号,参考博客:http://www.cnblogs.com/wupeiqi/articles/5246483.html

其他一些使用场景总结

以下情况不要使用signal:

  • signal与一个model紧密相关, 并能移到该model的save()时

  • signal能使用model manager代替时

  • signal与一个view紧密相关, 并能移到该view中时

以下情况可以使用signal:

  • signal的receiver需要同时修改多个model时

  • 将多个app的相同signal引到同一receiver中处理时

  • 在某一model保存之后将cache清除时

  • 无法使用其他方法, 但需要一个被调函数来处理某些问题时

###信号介绍

信号:赛道中障碍物上的信号(车到了障碍物,自动触发)

中间件的某些功能,信号也可以实现,因为有请求到来前的信号,请求结束后这样的信号.

写到和poroject 同名的__init__ 需要把函数注册到信号中..... pymsql的那个也写到这里了????

senser 哪个类执行的操作

中间件和信号的使用区别:中间件主要处理请求到来和结束的时候处理,信号来处理视图函数后面的逻辑

Django内置信号说明

  • django signal 的处理是同步的,勿用于处理大批量任务。

  • django signal 对程序的解耦、代码的复用及维护性有很大的帮助。

  • 通常放置于和project同名的__init__.py中,在使用的时候,仅需注册指定账号,则在被触发时,会自动执行,参考这里
  • 不同的信号有不同的参数,具体可参考:https://www.cnblogs.com/robinunix/articles/10614369.html
Model signals
    pre_init                    # django的modal执行其构造方法前,自动触发
    post_init                   # django的modal执行其构造方法后,自动触发
    pre_save                    # django的modal对象保存前,自动触发
    post_save                   # django的modal对象保存后,自动触发
    pre_delete                  # django的modal对象删除前,自动触发
    post_delete                 # django的modal对象删除后,自动触发
    m2m_changed                 # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
    class_prepared              # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
Management signals
    pre_migrate                 # 执行migrate命令前,自动触发
    post_migrate                # 执行migrate命令后,自动触发
Request/response signals
    request_started             # 请求到来前,自动触发
    request_finished            # 请求结束后,自动触发
    got_request_exception       # 请求异常后,自动触发
Test signals
    setting_changed             # 使用test测试修改配置文件时,自动触发
    template_rendered           # 使用test测试渲染模板时,自动触发
Database Wrappers
    connection_created          # 创建数据库连接时,自动触发

 

 文本示例:

from django.db.models.signals import pre_save, post_save


#1.普通方式绑定 def xxxxxxxx(sender,
**kwargs): # xxxxxxxx就是callback函数 print("post_save.xxxxxxxx") print(sender, kwargs) post_save.connect(xxxxxxxx)


#2.decorators方式绑定
from django.dispatch import receiver
from django.core.signals import request_finished
 
@receiver(request_finished, dispatch_uid="request_finished")
def my_signal_handler(sender, **kwargs):
    print("Request finished!================================")


#3.decirators方式绑定并且传参数
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.db.models.signals import pre_save
from django.dispatch import receiver


class User(AbstractUser):
display_name = models.CharField(max_length=255, default='', verbose_name='中文名')
org_id = models.CharField(max_length=63, default='', verbose_name='组织架构 id')
org_path = models.CharField(max_length=255, default='', verbose_name='组织架构路径')
username = models.CharField(max_length=255, default='', verbose_name='MIS账号')

def __str__(self):
return '%s %s' % (self.display_name, self.username)


@receiver(pre_save, sender=User) #我们可以注册只接收特定发送者发送的信号。比如这个例子,指定只需要接收的User这个模型发送的信号
def user_pre_save(sender, instance, **kwargs):
from accounts.utils.user import sync_user_info
sync_user_info(instance.username, instance)
 

pre_save和post_save使用示例

pre_delete与post_delete使用示例

 

 

自定义信号

除了默认的信号外,还可以自定义一些信号。需要以下三个步骤

1 定义信号

import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

2 注册信号

def callback(sender, **kwargs):
    print("callback")
    print(sender,kwargs)
  
pizza_done.connect(callback)

3 触发信号

from 路径 import pizza_done
  
pizza_done.send(sender='seven',toppings=123, size=456)

由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。即在任意位置,导入这个函数,然后进行方法调用  

 参考:https://segmentfault.com/a/1190000008455657

备注:

update方法它不会在模型实例上调用save(),因此不会发出pre_save和post_save信号.如果你想要你的信号接收器被调用,你应该循环查询,并且对于每个模型实例,进行你的更改并自己调用save(). 如下

参考:https://lxkaka.github.io/2017/07/17/django_signals/
update() 并不会触发pre_save, post_save信号 所以当有这样的操作: GroupSettings.objects.filter(business_group=business_group).update(**filterd) 想触发信号发送时,可以这样修改: group = GroupSettings.objects.filter(business_group=business_group).first() if group: for key, value in fileted.items(): setattr(group, key, value) group.save()  

当然也可以自定义信号,参考:https://www.jianshu.com/p/02e68fe74323

8. Admin

- Admin
参考博客:http://www.cnblogs.com/wupeiqi/articles/7444717.html

 

9. 作业

作业:主机管理

用户表(id, user,pwd,email,mm)
业务线(id, name) # 用户表_set
主机表(id host ip port FK(业务线))
用户业务线关系表(id uid bid) ******


1. 登录+注册(Ajax+form组价)
2. FBV&CBV
业务线管理(列表,增加,修改) # 删除对话框确认删除
主机管理(列表,增加,修改) # 删除对话框确认删除
用户管理(列表,增加,修改) # 删除对话框确认删除
3. 分页
4. BootStrap【可选】

如何做:
周一 + 周二: 课上讲的内容理解

周三 : 写作业

周六: 本周不太理解+以前不理解;下午预习

 

参考博客:

http://www.cnblogs.com/wupeiqi/articles/5246483.html

posted @ 2017-09-03 10:44  番茄土豆西红柿  阅读(376)  评论(0编辑  收藏  举报
TOP