杂七杂八

Posted standby

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了杂七杂八相关的知识,希望对你有一定的参考价值。

 

class day19:
	def __init__():
		pass

	def do_homework():
		pass

	def do_my_project():
		pass




‘‘‘Django 回顾‘‘‘

	- http请求周期
		浏览器(socket客户端)
			2.socket.connect(ip,port) 进行连接
			3.socket.send("http://www.qq.com/index.html...")    url + data
				遵循的规则:http协议
					请求头
					请求体


请求头和请求体使用 ‘\\r\\n\\r\\n‘ 分隔,前面是请求头,后面是请求体
GET请求: "GET /index.html?key=1... Http/1.1\\r\\nhost:www.qq.com\\r\\ncontent-type:application/json\\r\\n\\r\\n"

POST请求:"POST /index.html Http/1.1\\r\\nhost:www.qq.com\\r\\ncontent-type:application/json\\r\\n\\r\\nname=alex&pwd=123"

			6.获取响应
				响应头,响应体 = data.split("\\r\\n\\r\\n")

				响应头之间用 ‘\\r\\n‘ 分隔

			7.断开连接


		nginx(socket服务端)
			1.server.run(),监听IP和PORT
			4.server.recv()

				请求头,请求体 = data.split("\\r\\n\\r\\n")
				request.POST.get(‘name‘)  即是从 请求体 里取值
			5.服务端进行响应:
				conn.send(‘......‘)
				遵循的规则:http协议
					响应头
					响应体

			7.断开连接

	总结:
		a.Http请求中本质都是字符串
		b.Http请求是短连接(请求 -> 响应 -> 断开连接)
		c.请求和响应都有头和体
			请求:请求头‘\\r\\n\\r\\n‘请求体
			响应:响应头‘\\r\\n\\r\\n‘响应体





由于需要处理繁琐http的解析和封装处理工作,所以web框架应运而生


web框架
	- Django
		socket(wsgiref)   django没有自己写socket
		解析和封装http请求

	django-admin startproject mysite
	
	cd mysite
	python manage.py startapp app01

	coding...(*****)

	python manage.py runserver ip:port




‘‘‘写代码‘‘‘

	- 路由系统
		/login/   			func   	name=‘f1‘
		/login/\\d+/ 		func	name=‘f2‘
		/login/(?P<n>\\d+)/	func	name=‘f3‘
		/login/\\d+/			include(‘app01.urls‘)


	- 视图函数
		def index(request):

			request.GET
			request.body	    原生的请求体
			request.POST        转换后的请求体字典   如果请求头中content-type=urlencode-form... 才将request.body转换成字典
				- 可能有值
				- 也可能没有值
			request.method
			request.Meta

			request.GET.get()
			request.GET.getlist()        前端多选的情况,如多个作者
			request.POST.get()
			request.POST.getlist()



			return HttpResponse(‘字符串/字节‘)
			return render(request,"html路径",locals())
			return redirect("url")


	- 模板
		for if
		
		继承

		filter,simple_tag



	- Models操作
		- 创建表
		- models.xxx.objects.create(name="xxxx")
		- models.xxx.objects.create(**dic)
		- models.xxx.objects.filter(id__gt=1).delete()
		- models.xxx.objects.filter(id=1)
		- models.xxx.objects.exclude(id=5)     取出id不等于5的
		- models.xxx.objects.filter(id=1).update(name=‘ddd‘)
		- models.xxx.objects.filter(id=1).update(**dic)


		queryset --> [对象,对象...]
		objs = models.xxx.objects.all()

		queryset --> [{},{}...]
		objs = models.xxx.objects.all().values()

		queryset --> [(),()...]
		objs = models.xxx.objects.all().values_list()


demo1
	业务线表  bussiness_unit
		id name
	主机表 serverinfo
		id host port bs(业务线对象)


	objs = modesl.serverinfo.objects.all()
	for row in objs:
		print(row.id)
		print(row.host)
		print(row.port)
		print(row.bs.name)   外键,拿到业务线的名字

	objs = modesl.serverinfo.objects.all().values("id","host","port","bs__name")
	for row in objs:
		print(row[‘host‘])
		print(row[‘bs__name‘])



demo2 (userinfo 和 bussiness_unit 是多对多关系)
	用户表 userinfo
		id user pwd email mm(ManyToMany)
	业务线表  bussiness_unit
		id name
	主机表
		id host port bs(业务线对象)
	用户业务线关系表  *****
		id user_id bs_id

	obj = models.user.objects.filter(user=‘alex‘).first()
	obj.mm.add(1)
	obj.mm.add(11)


	- 通过用户对象查所负责的所有业务线对象
	obj = models.user.objects.filter(user=‘alex‘).first()
	queryset = obj.mm.all()          拿到alex负责的所有业务线  -> [业务线对象,业务线对象...]
	for row in queryset:
		print(row.id)
		print(row.name)


	- 通过业务线反查对应有哪些人负责? (***反查***    表名_set)
	obj = models.bussiness_unit.objects.filter(name=‘二手车‘).first()
	queryset = obj.userinfo_set.all()   拿到负责二手车业务线的用户对象  ->  [用户对象,用户对象...]
	for row in queryset:
		print(row.user)
		print(row.pwd)

总结:
	1.多对多关系建在哪张表上都可以;
	2.如果建在userinfo表上,那么通过用户对象查所负责的bs对象列表就直接用 obj.mm.all()
	方便了 userinfo对象,但是bs对象反查userinfo就得用 userinfi_set.all()





==================================================================

今日内容:

1.登录
	- 密码加密,对密码进行比较
	- 用户登录之后才能访问某些页面


2.cookie是什么?
	- 保存在客户端浏览器上的键值对 {k:v}
	- cookie依附在请求头或者响应头中
	- 浏览器发送请求时会自动携带所访问网站对应的cookie
	- 应用
		- 实现登录
		- 投票
		- 每页显示10条/20条...

	- 使用
		- 设置
			response =  redirect(‘/index/‘)
            response.set_cookie(‘my_cookie‘,md5.encrypt(‘xxx‘))
            return response

key, 
value=‘‘, 
max_age=None,		超时时间:秒数
expires=None, 		超时时间:截止日期
path=‘/‘,			cookie在哪个url里生效 :  访问指定url时才能读取到cookie,   ‘/‘  表示全部页面都可以
domain=None, 		当前域名或者二级域名
secure=False, 		https
httponly=False  	


response = redirect(‘/index/‘)
# 设置cookie
response.set_cookie(‘my_cookie‘,md5.encrypt(user))
return response
# 设置cookie过期时间
import datetime
deadline = datetime.datetime.utcnow() + datetime.timedelta(seconds=5)
response.set_cookie(‘my_cookie‘,md5.encrypt(user),expires=deadline)
response.set_cookie(‘my_cookie‘,md5.encrypt(user),max_age=5)



        - 获取
            ck = request.COOKIES.get(‘my_cookie‘)

# 详细代码如下:        
‘‘‘
# models.py

from django.db import models

# Create your models here.

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    email = models.EmailField(null=True)

‘‘‘    

‘‘‘
from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from common import md5


# 判断用户是否登录的装饰器(通过cookie判断)
def auth(func):
    def inner(request,*args,**kwargs):
        ck = request.COOKIES.get(‘my_cookie‘)
        if not ck:
            return redirect(‘/login/‘)
        return func(request,*args,**kwargs)
    return inner

@auth
def index(request):
    user = request.COOKIES.get(‘my_cookie‘)
    print(request.COOKIES)
    # return HttpResponse("登录成功")
    return render(request,‘index.html‘,locals())


def login(request):
    if "GET" == request.method:
        return render(request,‘login.html‘)
    else:
        user = request.POST.get(‘user‘)
        pwd = request.POST.get(‘pwd‘)
        obj = models.UserInfo.objects.filter(username=user,password=md5.encrypt(pwd)).first()
        if obj:

            response = redirect(‘/index/‘)
            # 设置cookie过期时间
            # import datetime
            # deadline = datetime.datetime.utcnow() + datetime.timedelta(seconds=5)
            # response.set_cookie(‘my_cookie‘,md5.encrypt(user),expires=deadline)
            # response.set_cookie(‘my_cookie‘,md5.encrypt(user),max_age=5)

            # 设置cookie
            response.set_cookie(‘my_cookie‘,user)
            return response
        else:
            return render(request,‘login.html‘,{‘msg‘:"用户名或密码错误"})
‘‘‘

‘‘‘
# login.html

...
<form action="/login/" method="POST">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="text" name="pwd">
    <input type="submit" value="提交"><span style="color: red;">{{ msg }}</span>
</form>
...


# index.html
...
<h1>{{ user }}</h1>
...
‘‘‘
‘‘‘
# md5.py

def encrypt(pwd):
    import hashlib
    obj = hashlib.md5()
    obj.update(pwd.encode(‘utf-8‘))
    data = obj.hexdigest()
    return data

if __name__ == ‘__main__‘:
    print(encrypt(‘123‘))
‘‘‘



3.session
	
	是保存在服务器端的键值对 {k:v}

	依赖cookie

	Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
	更多参考:http://www.cnblogs.com/wupeiqi/articles/5246483.html


	生成随机字符串,并将其当做cookie发送给客户端
	服务端设置随机字符串作为key,自己设置一些{}:request.session[‘my_session_key‘] = user


	- 设置session
        request.session[‘yyy‘] = user
        return redirect(‘/index/‘)


    - 获取session
    	# 装饰器
		def auth(func):
		    def inner(request,*args,**kwargs):
		        ck = request.session.get(‘yyy‘)
		        if not ck:
		            return redirect(‘/login/‘)
		        return func(request,*args,**kwargs)
		    return inner


    - 清空session
    	request.session.clear()


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

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,默认修改之后才保存(默认)



# 详细代码如下:
‘‘‘
from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from common import md5


# 判断用户是否登录的装饰器(通过session判断)
def auth(func):
    def inner(request,*args,**kwargs):
        ck = request.session.get(‘my_session_key‘)
        if not ck:
            return redirect(‘/login/‘)
        return func(request,*args,**kwargs)
    return inner



@auth
def index(request):
    user = request.session.get(‘my_session_key‘)
    return render(request,‘index.html‘,locals())


@auth
def order(request):
    return render(request,‘order.html‘)


# 登出view
def logout(request):
	# 用户登出后清空session
    request.session.clear()
    return redirect(‘/index/‘)


def login(request):
if "GET" == request.method:
    return render(request,‘login.html‘)
else:
    user = request.POST.get(‘user‘)
    pwd = request.POST.get(‘pwd‘)
    obj = models.UserInfo.objects.filter(username=user,password=md5.encrypt(pwd)).first()
    if obj:
        # 设置session
        request.session[‘my_session_key‘] = user
        return redirect(‘/index/‘)
    else:
        return render(request,‘login.html‘,{‘msg‘:"用户名或密码错误"})   
‘‘‘

‘‘‘
# 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/‘, views.login),
    url(r‘^logout/‘, views.logout),
    url(r‘^index/‘, views.index),
    url(r‘^order/‘, views.order),
]
‘‘‘


‘‘‘
# login.html

...
<form action="/login/" method="POST">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="text" name="pwd">
    <input type="submit" value="提交"><span style="color: red;">{{ msg }}</span>
</form>
...


# index.html
...
<h1>{{ user }}</h1>
...



# order.html
...
<body>
<h1>欢迎登录:{{ request.session.my_session_key }}</h1>
<a href="/logout/">注销</a>
</body>
...
‘‘‘







4.csrf 跨站请求伪造



<form action="/login/" method="POST">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="text" name="pwd">
    <input type="submit" value="提交"><span style="color: red;">{{ msg }}</span>
</form>

{% csrf_token %}  在浏览器里默认就是一个隐藏的input标签,如下所示:

<form action="/login/" method="POST">
    <input type=‘hidden‘ name=‘csrfmiddlewaretoken‘ value=‘T2Ub1TacecIsEsKJvoUvB3xNSwrEGT0NajwGeO6y58mp1IseYVLL3FBnXtOT3WgW‘ />
    <input type="text" name="user">
    <input type="text" name="pwd">
    <input type="submit" value="提交"><span style="color: red;"></span>
</form>

而 {{ csrf_token }} 这个就是这个隐藏标签的value值






跨站请求的漏洞:
<form method="POST" action="http://www.icbc.com.cn/icbc/">
	<input type="text" name="from" style="display: none;" value="A的卡号">
	<input type="text" name="to" style="display: none;" value="黑客的卡号">
	<input type="text" name="money" style="display: none;" value="1000000000">
	<input type="submit" name="" value="点我">
</form>

{% csrf_token %}




# 首先不提交 csrf_token 的情况:报错403,CSRF verification failed. Request aborted.
<form id="my_form" action="/login/" method="POST">
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:{
                ‘user‘:$(‘#my_form input[name="user"]‘).val(),
                ‘email‘:$(‘#my_form input[name="email"]‘).val()
            },
            success:function (data) {
                console.log(data);
            }
        })
    }
</script>

# 注意一点:
# 如果form表单不写action,则默认提交到当前页面


ajax提交csrf_token的几种方式:

# 方式1
<form id="my_form" action="/show/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:{
                ‘user‘:$(‘#my_form input[name="user"]‘).val(),
                ‘email‘:$(‘#my_form input[name="email"]‘).val(),
                ‘csrfmiddlewaretoken‘:$(‘input[name="csrfmiddlewaretoken"]‘).val()
            },
            success:function (data) {
                console.log(data)
            }
        })
    }
</script>


# 方式2   只能写在模板里
<body>
<form id="my_form" action="/show/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script>
    function ajaxSubmit() {
        $.ajaxSetup({
            data: {‘csrfmiddlewaretoken‘:‘{{ csrf_token }}‘}
        });
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:{
                ‘user‘:$(‘#my_form input[name="user"]‘).val(),
                ‘email‘:$(‘#my_form input[name="email"]‘).val()
            },
            success:function (data) {
{#                do something...#}
            }
        })
    }
</script>
</body>

# 后端得到的数据:
	类型:<class ‘django.http.request.QueryDict‘>
	数据:<QueryDict: {‘csrfmiddlewaretoken‘: [‘raZNrc77aQn7cr5Wr6gtTgOaTdNWZKF0HmAfN6kqlGzmyrr4Dw7DUcSVQ6ZHcFoQ‘], ‘email‘: [‘[email protected]
	om‘], ‘user‘: [‘borui‘]}>



# 方式3   只能写在模板里
<form id="my_form" action="/show/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:{
                ‘user‘:$(‘#my_form input[name="user"]‘).val(),
                ‘email‘:$(‘#my_form input[name="email"]‘).val(),
                ‘csrfmiddlewaretoken‘:"{{ csrf_token }}"
            },
            success:function (data) {
{#                do something...#}
            }
        })
    }
</script>

这种方法,循环form表单,把很多input值拿出来组成字典,
然而实际上POST请求最后是需要转换成字符串放到请求体中发给后端的,实际上的字符串如下:
‘csrfmiddlewaretoken=ouyWxV86TJWMttyLwzRkORIcqXjInlDREG9oTPlp4z81PtUTIZIuPNMXnQvtAgmH&user=love&email=love%40qq.com‘

所以,如果ajax里的data字段如果写成一个字典,那么就需要一个转成字符串的过程;
如果直接写成字符串,也是可以的;
$(‘#my_form‘).serialize()  这个方法就可以把form表单里所有的值(包含隐藏的csrf标签)拼接成一个字符串;



# 方法4:
<form id="my_form" action="/show/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:$(‘#my_form‘).serialize(),
            success:function (data) {
                console.log(data)
            }
        })
    }
</script>


‘‘‘ 以上4中方法都是把csrf_token放入 请求体 里传递给后端 ‘‘‘


# 方法5   把csrftoken对应的值方到 请求头 里,传递给后端,这样也可以通过csrf验证
	首先引入 jquery.cookie.js 插件
	然后通过 $.cookie(‘csrftoken‘) 则可以获取到csrftoken对应的值

这种方法,是把csrftoken对应的值放到请求头里,必须按照这个格式:headers:{‘X-CSRFToken‘: $.cookie(‘csrftoken‘)}
这种情况,请求体的内容可以随便写;

<form id="my_form" action="/show/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/jquery-3.2.1.min.js" %}"></script>
<script src="{% static "js/jquery.cookie.js" %}"></script>
<script src="{% static "js/bootstrap.js" %}"></script>

<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:{‘k1‘:‘v1‘,‘k2‘:‘v2‘},
            headers:{‘X-CSRFToken‘: $.cookie(‘csrftoken‘)},
            success:function (data) {
                console.log(data)
            }
        })
    }
</script>



总结:
	基于ajax提交form表单,发送csrf验证的方式里
	最常用的就是 data:$(‘#my_form‘).serialize() 和 headers:{‘X-CSRFToken‘: $.cookie(‘csrftoken‘)}


另外:
	关于csrf有两个装饰器:from django.views.decorators.csrf import csrf_exempt,csrf_protect
	‘django.middleware.csrf.CsrfViewMiddleware‘, 
	# 开启则表示全站都使用csrf验证,而csrf_exempt这个装饰器则表示哪些view可以不使用csrf
	# 如果不开启,则表示全站都不使用csrf验证,而csrf_protect这个装饰器则表示哪些view可以使用csrf







5.牛逼的自定义分页



request.path_info





‘‘‘ day20 分界线 ‘‘‘

上节回顾
	1.http请求周期
		请求头和请求体使用 ‘\\r\\n\\r\\n‘ 分隔

	2.Models操作
		单表
			[obj,obj,obj...] = models.xxx.objects.all()
			[{},{},{}...] = models.xxx.objects.values(xx,xx,xx...)
			[(),(),()...] = models.xxx.objects.values_list(xx,xx,xx...)

		一对多
			dep 部门表:标题
			user 员工表:用户,邮箱,部门id(外键)

			qs = [obj,obj,obj...] = models.user.objects.all()
			for row in qs:
				row.id,row.name,row.email,row.xx.title

			user 查 dep
				[{},{},{}...] = models.user.objects.values(‘id‘,‘name‘,‘email‘,‘xx__title‘)
				[(),(),()...] = models.user.objects.values_list(‘id‘,‘name‘,‘email‘,‘xx__title‘)

			dep 反查 user :   user_set (在dep表里隐含的字段,多对多同样)

		多对多

			业务线  id name M2M

			用户表  id name 

			django给创建第三张表(因为manytomany)



	3.自定义分页模块

	4.csrf



	5.cookie 
		保存在客户端浏览器上的键值对

	6.session
		保存在服务器端的键值对,依赖cookie






day20今日内容
	1. FBV 和 CBV
		FBV:function basic view
			/index/    func(request)
						if "GET" == request.method:
							...

		CBV:class basic view
			/index/    class(object)
						- 判断:
							get请求  就执行类里的get方法
							post请求  就执行类里的post方法


		form表单只能提交 ‘get‘ 和 ‘post‘ 方法
		ajax 则可以提交:‘get‘, ‘post‘, ‘put‘, ‘patch‘, ‘delete‘, ‘head‘, ‘options‘, ‘trace‘
			ajax 遵循resful规范

/index/    obj = 类()

找到类之后,执行完 __init__ 就执行 dispatch()方法,自己不写的话就执行父类的 dispatch()
‘‘‘
class B:
	def f1(self):
		self.f2()

class A(B):
	def f2(self):
		print("---> A")


obj = A()
obj.f1()  --->  B.f1(obj)  --> obj.f2()
‘‘‘
	
    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)


	POST   obj.post   是通过反射 getattr()
	GET    obj.get    是通过反射 getattr()

			‘‘‘执行super,调用父类的dispatch方法‘‘‘
		    def dispatch(self, request, *args, **kwargs):
		    	print(‘before‘)
		        # 执行父类的 dispatch方法
		        response = super(LoginView,self).dispatch(request,*args, **kwargs)
		        print(‘after‘)
		        return response

		    ‘‘‘或者重写父类的dispatch‘‘‘
		    def dispatch(self, request, *args, **kwargs):
		        method = request.method.lower()
		        # print(‘+++++++++++++‘, self.http_method_names)
		        if hasattr(LoginView,method):
		            func = getattr(LoginView,method)
		            return func(self, request, *args, **kwargs)
		        else:
		            return HttpResponse(‘err‘)

		    def get(self,request,*args,**kwargs):
		        print(‘------->> get‘)
		        return render(request,‘login.html‘)

		    def post(self,request,*args,**kwargs):
		        print(‘------->> post‘)
		        return HttpResponse(‘ok‘)


    注意:django的CBV里装饰器不能直接用,需要使用: from django.utils.decorators import method_decorator

    	FBV -> 函数
    	CBV -> 类
    		- dispatch
    		- get/post

    	应用:
    		登录验证的几种实现方法:
    			- 继承
    				- 单继承实现
    				‘‘‘
from django.shortcuts import render,redirect,HttpResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt,csrf_protect
from django.core import serializers
from django.utils.decorators import method_decorator

# Create your views here.

class Login(View):
    def dispatch(self, request, *args, **kwargs):
        return super(Login,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 ‘alex‘ == user and ‘123‘ ==pwd:
            request.session[‘user‘] = user
            return render(request,‘private.html‘,{‘user‘:user})
        return render(request,‘login.html‘,{‘msg‘:‘用户名或密码错误!‘})

class Base(View):
    def dispatch(self, request, *args, **kwargs):
        if request.session.get(‘user‘):
            response = super(Base,self).dispatch(request, *args, **kwargs)
            return response
        else:
            return redirect(‘/login/‘)
class Index(Base):
    def get(self, request,*args, **kwargs):
        return render(request, ‘index.html‘, {‘user‘: request.session.get(‘user‘)})
    				‘‘‘
    				- 多继承实现
‘‘‘
class Base(object):
    def dispatch(self, request, *args, **kwargs):
        if request.session.get(‘user‘):
            response = super(Base,self).dispatch(request, *args, **kwargs)
            return response
        else:
            return redirect(‘/login/‘)
        
class Index(Base,View):
    def get(self, request,*args, **kwargs):
        return render(request, ‘index.html‘, {‘user‘: request.session.get(‘user‘)})
‘‘‘

    			- 装饰器
    				auth 是装饰器函数
    				 - 加载类上 		@method_decorator(auth,name=‘post‘)

‘‘‘
# 装饰器:验证用户是否已经登录
def auth(func):
    def wrapper(request, *args, **kwargs):
        if request.session.get(‘user‘):
            obj = func(request, *args, **kwargs)
            return obj
        else:
            return redirect(‘/login/‘)
    return wrapper

@method_decorator(auth,‘get‘)
class Index(View):
    def dispatch(self, request, *args, **kwargs):
        return super(Index, self).dispatch(request, *args, **kwargs)
    def get(self, request, *args, **kwargs):
        return render(request, ‘index.html‘, {‘user‘: request.session.get(‘user‘)})
‘‘‘
    				 - 加在dispatch上 	@method_decorator(auth)

‘‘‘
# 装饰器:验证用户是否已经登录
def auth(func):
    def wrapper(request, *args, **kwargs):
        if request.session.get(‘user‘):
            obj = func(request, *args, **kwargs)
            return obj
        else:
            return redirect(‘/login/‘)
    return wrapper

class Index(View):
    @method_decorator(auth)
    def dispatch(self, request, *args, **kwargs):
        return super(Index, self).dispatch(request, *args, **kwargs)
    def get(self, request, *args, **kwargs):
        return render(request, ‘index.html‘, {‘user‘: request.session.get(‘user‘)})
‘‘‘


    				 - 加在具体方法上 	@method_decorator(auth)
    				 ‘‘‘
# 装饰器:验证用户是否已经登录
def auth(func):
    def wrapper(request, *args, **kwargs):
        if request.session.get(‘user‘):
            obj = func(request, *args, **kwargs)
            return obj
        else:
            return redirect(‘/login/‘)
    return wrapper

class Index(View):
    @method_decorator(auth)
    def get(self, request, *args, **kwargs):
        return render(request, ‘index.html‘, {‘user‘: request.session.get(‘user‘)})
    				 ‘‘‘


    				 from django.views.decorators.csrf import csrf_exempt,csrf_protect
    				 - csrf_exepmt 加在POST上例外,只能加在 dispatch上有效,官网bug。






	2. 序列化  (Json是不能处理 queryset的)

		- 模板渲染
		- ajax
			- from django.core import serializers
			- serializers 的局限性:
			   	queryset里必须是obj才可以
			  	如果是models.xxx.objects.values(xx,xx,xx) 这样得到的queryset是不能用serializers进行序列化的!!!
			  	只能采用在后台自己拼接好字典等形式,自己搞成json.dumps然后传给ajax
			- json 序列化 (有局限性,需自定义dumps)
				因为json能处理的数据类型有限,比如datetime类型就不能处理,需要自定义dumps函数

		方式一:
			user_list = models.UserInfo.objects.all()
			data = serializers.serialize("json", user_list)
			[
				{"model": "app01.userinfo", "pk": 1, "fields": {"username": "\\u5174\\u666e", "password": "123123"}}, 
				{"model": "app01.userinfo", "pk": 2, "fields": {"username": "\\u94f6\\u79cb\\u826f", "password": "666"}}
			]
			
		方式二:
			
			user_list = models.UserInfo.objects.values(‘id‘,‘username‘)
			user_list = list(user_list)
			data = json.dumps(user_list)
			[
				{"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.parse()  可以被 dataType: ‘JSON‘ 这一行替换:

# 后端代码:
...
import json
return HttpResponse(json.dumps({‘flag‘:True,‘msg‘:‘ok‘}))
...

示例1:使用JSON.parse()的情况,返回的是 object类型
<form id="my_form" action="/login/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="password" name="pwd">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/jquery-3.2.1.min.js" %}"></script>
<script src="{% static "js/jquery.cookie.js" %}"></script>
<script src="{% static "js/bootstrap.js" %}"></script>

<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/login/‘,
            type:‘POST‘,
            data:{
                ‘user‘:$(‘#my_form input[name="user"]‘).val(),
                ‘pwd‘:$(‘#my_form input[name="pwd"]‘).val()
            },
            headers:{‘X-CSRFToken‘: $.cookie(‘csrftoken‘)},
            success:function (data) {
                var info = JSON.parse(data);
                console.log(info);
                console.log(typeof info);
            }
        })
    }
</script>


示例2:使用dataType: ‘JSON‘的情况,返回的是string类型
<form id="my_form" action="/login/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="password" name="pwd">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/jquery-3.2.1.min.js" %}"></script>
<script src="{% static "js/jquery.cookie.js" %}"></script>
<script src="{% static "js/bootstrap.js" %}"></script>

<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/login/‘,
            type:‘POST‘,
            datatype:‘JSON‘,
            data:{
                ‘user‘:$(‘#my_form input[name="user"]‘).val(),
                ‘pwd‘:$(‘#my_form input[name="pwd"]‘).val()
            },
            headers:{‘X-CSRFToken‘: $.cookie(‘csrftoken‘)},
            success:function (info) {
                console.log(info);
                console.log(typeof info);
            }
        })
    }
</script>



	3. Django的Form组件验证(用户请求的验证 + 生成HTML标签)


		示例:用户管理
		添加用户页面:
			- 显示HTML标签
			- 提交:数据验证
			- 成功之后保存
			- 错误的话则提示错误信息

		from django.forms import widgets  样式


		总结:
			1. 创建类 MyForm(本质就是正则表达式的集合),继承Form类
			2. 只是生成HTML标签:form = MyForm()    添加页面
			3. 带默认值的HTML标签:form = MyForm(initial={‘xx‘:‘xx‘,...})   编辑页面
			4. 提交数据:form = Form(data=request.POST)
			             if form.is_valid():
			             	print(form.cleaned_data)
			             else:
			             	print(form.errors)
			5. 存在的问题:下拉框有更新时无法实时更新到前端页面

				解决办法:重写下 __init__(self,*args,**kwargs)

一对多


多对多





三元运算:v = xxx if xxx else []



找name等于user的input标签
$(‘#f1 input[name="user"]‘).val(‘xxxxx‘)



$(‘#f1 .error‘).remove() 

input标签后提示错误信息:
$.each(arg.msg, function(k,v){
	var tag = document.createFlement(‘span‘);
	tag.innerHTML = v[0];
	tar.className = "error"
    # <span class=‘error‘>v[0]</span>

	$(‘#f1 input[name="‘ + k + ‘user"]‘).after(tag)
})



定律:
	如果用模态对话框进行添加或者修改等操作,则前端提交数据需要用ajax,后端可以用django的form组件
	因为ajax不刷新,所以可以利用form组件的验证功能,生成HTML的功能可用可不用

	如果用新的页面进行添加或者修改等操作,则用ajax和form表单提交都可以;后端可以用django的form组件
	因为form有刷新,生成HTML的功能要使用,不可以自己手写HTML标签,避免之前的数据因为刷新导致丢失;





	4. 缓存

	5. 中间件

	6. 信号(scrapy爬虫框架里有用)

	7. Django的 Admin


JS跳转页面的方法:location.href = ‘/index.html‘





day20作业:
	还是主机管理
	1. 登录 + 注册(ajax+form组件)

	FBV / CBV 都可以
	2. 业务线管理(列表、增加、修改  页面跳转) 模态确认删除    单表
	3. 主机管理(列表、增加、修改  页面跳转) 模态确认删除      一对多
	4. 用户管理(列表、增加、修改  页面跳转) 模态确认删除      多对多

	5. 自定义分页

	6. Bootstrap


把课上讲的内容搞明白,还有几个之前忘记的点,今天老师写的;
然后开始做作业;
周六上午,整理出遇到的问题;










# shell in a box  插件





# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

day21课上笔记 .. 2017-09-17

课前回顾:
	- Form组件验证
		参考:http://www.cnblogs.com/wupeiqi/articles/6144178.html
		- 先写好类:
			class LoginForm(Form):
				user = fields.CharField(...)
				emial = fields.EmailField(...)
				...
		- 使用之添加用户
			- 先实例化一个对象:form = LoginForm()
				前端:{{ form.user }} -> <input type=‘text‘ name=‘user‘ />
			- 等待用户输入,提交
			-添加,POST
				form = LoginForm(data=request.POST)
				form.is_valid()
				form.cleaned_data
				form.errors


		"""这里需要再练习下!!!"""
		- 修改用户:先发一个GET请求,/edit_user/user_id
			obj = models.UserInfo.objects.get(id=user_id)
			form = LoginForm(initial={‘user‘:obj.user})

			{{form.user}} -> <input type=‘text‘ name=‘user‘ value=‘数据库中的用户名‘ />

			等待用户输入内容,提交

		- 修改用户:再发一个POST请求,/edit_user/user_id
			form = LoginForm(data=request.POST)
			form.is_valid()
			form.cleaned_data:
				models.UserInfo.objects.filter(id=user_id).update(**cleaned_data)


		- 下拉框无法自动刷新的问题:
			‘‘‘
				dp_id = fields.ChoiceField(
			        required=True,
			        choices = [],
			    )
			    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‘)
			‘‘‘



	- FBV & CBV(用到了反射)
	- 序列化
		- Django内置的 serializers
		- json.dumps(xxx,cls=JsonCustomEncoder)
			- JsonCustomEncoder 在这个函数里自定义一些规则






day21今日内容
	- Form组件进行验证之进阶
	‘‘‘
		import re
		from django.forms import Form
		from django.forms import fields
		from django.forms import widgets
		from app01 import models
		from django.core.validators import RegexValidator
		from django.core.exceptions import ValidationError
	‘‘‘
		- 自定义验证规则方式一:使用RegexValidator对象
			‘‘‘
				自定义验证规则方式一:通过RegexValidator对象
			    phone = fields.CharField(
			        required=True,
			        validators=[RegexValidator(r‘^[0-9]+$‘, ‘请输入数字‘), RegexValidator(r‘^159[0-9]+$‘, ‘数字必须以159开头‘)],
			    )
			‘‘‘
		- 自定义验证规则方式二:自定义验证函数
			‘‘‘
				# 自定义验证规则函数,优点是可以有数据库操作
				def phone_validate(value):
				    mobile_re = re.compile(r‘^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$‘)
				    if not mobile_re.match(value):
				        raise ValidationError(‘手机号码格式错误‘)
				    if models.UserInfo.objects.filter(phone=value).count():
			            raise ValidationError(‘手机号码已经存在‘)

			    phone = fields.CharField(validators=[phone_validate,]) 
			‘‘‘
		- 自定义验证规则方式三:在当前类中使用钩子函数,函数名称必须符合 "clean_字段名称"
			‘‘‘
				phone = fields.CharField()
			    # 钩子方法
			    def clean_phone(self,):
			        """
			        只能取当前字段的值,切勿取其他的值
			        必须得有返回值
			        :return: 
			        """
			        # 需要要验证的值,自己写正则进行验证
			        # 去取用户提交的值
			        value = self.cleaned_data[‘phone‘]
			        mobile_re = re.compile(r‘^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$‘)
			        if not mobile_re.match(value):
			            raise ValidationError(‘手机号码格式错误‘)
			        if models.UserInfo.objects.filter(phone=value).count():
			            raise ValidationError(‘手机号码已经存在‘)
			        return value
			‘‘‘


		- 是否可以共存?   可以
		- 顺序是怎样的?
		# 看源码		

		1. form.is_valid()
		2. self.errors
		3. self.full_clean()
			- self._clean_fields()  # 即对 self.fields 进行循环验证,依次验证每一个字段:
				先执行自己字段的正则,再执行钩子函数
				先执行自己字段的正则,再执行钩子函数
				...

		    - self._clean_form()    # 字段都验证完后,再对整个form进行验证:
		    	- self.clean()
		    	- self.add_error(self, field, error)  # 如果有错误则把错误加到add_error里

		‘‘‘
			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‘})
			    )
			    
			    # 写在RegisterForm类里作用于 RegisterForm
			    # 以用户注册为例:用户输入密码,再次输入密码,这时候需要对两次密码进行比对
			    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(‘密码输入不一致‘))
			            return self.cleaned_data
		‘‘‘



		    - self._post_clean()  	# 等同于 self._clean_form() ,可忽略


		‘‘‘
		# 源代码如下:
			def is_valid(self):
		        """
		        Returns True if the form has no errors. Otherwise, False. If errors are
		        being ignored, returns False.
		        """
		        return self.is_bound and not self.errors

			@property
		    def errors(self):
		        "Returns an ErrorDict for the data provided for the form"
		        if self._errors is None:
		            self.full_clean()
		        return self._errors

	        def full_clean(self):
		        """
		        Cleans all of self.data and populates self._errors and
		        self.cleaned_data.
		        """
		        self._errors = ErrorDict()
		        if not self.is_bound:  # Stop further processing.
		            return
		        self.cleaned_data = {}
		        # If the form is permitted to be empty, and none of the form data has
		        # changed from the initial data, short circuit any validation.
		        if self.empty_permitted and not self.has_changed():
		            return

		        self._clean_fields()
		        self._clean_form()
		        self._post_clean()

			def _clean_fields(self):
		        for name, field in self.fields.items():
		            # value_from_datadict() gets the data from the data dictionaries.
		            # Each widget type knows how to retrieve its own data, because some
		            # widgets split data over several HTML fields.
		            if field.disabled:
		                value = self.get_initial_for_field(field, name)
		            else:
		                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
		            try:
		                if isinstance(field, FileField):
		                    initial = self.get_initial_for_field(field, name)
		                    value = field.clean(value, initial)
		                else:
		                    value = field.clean(value)
		                self.cleaned_data[name] = value
		                if hasattr(self, ‘clean_%s‘ % name):
		                    value = getattr(self, ‘clean_%s‘ % name)()
		                    self.cleaned_data[name] = value
		            except ValidationError as e:
		                self.add_error(name, e)

		    def _clean_form(self):
		        try:
		            cleaned_data = self.clean()
		        except ValidationError as e:
		            self.add_error(None, e)
		        else:
		            if cleaned_data is not None:
		                self.cleaned_data = cleaned_data

		    def clean(self):
		        """
		        Hook for doing any extra form-wide cleaning after Field.clean() has been
		        called on every field. Any ValidationError raised by this method will
		        not be associated with a particular field; it will have a special-case
		        association with the field named ‘__all__‘.
		        """
		        return self.cleaned_data
		‘‘‘

		# 添加新用户demo
		‘‘‘
		# models.py
			class Depart(models.Model):
			    """部门表"""
			    title = models.CharField(max_length=32) # 数据库里就是string类型

			    class Meta:
			        verbose_name_plural = "部门表"

			class UserInfo(models.Model):
			    """用户表"""
			    name = models.CharField(max_length=32)
			    email = models.CharField(max_length=32)
			    phone = models.CharField(max_length=32)
			    pwd = models.CharField(max_length=64)
			    dp = models.ForeignKey(to=‘Depart‘,to_field=‘id‘)


		# forms.py
			import re
			from django.forms import Form
			from django.forms import fields
			from django.forms import widgets
			from app01 import models
			from django.core.validators import RegexValidator
			from django.core.exceptions import ValidationError

			class UserInfoForm(Form):
			    name = fields.CharField(
			        required=True,
			        min_length=2,
			        max_length=12,
			        error_messages={‘required‘: ‘用户名不能为空‘},
			        widget=widgets.TextInput(attrs={‘class‘: ‘form-control‘})
			    )   # 用户提交数据是字符串
			    pwd = fields.CharField(
			        required=True,
			        min_length=2,
			        max_length=12,
			        error_messages={‘required‘: ‘密码不能为空‘},
			        widget=widgets.PasswordInput(attrs={‘class‘: ‘form-control‘})
			    )
			    pwd_confirm = fields.CharField(
			        required=True,
			        min_length=2,
			        max_length=12,
			        error_messages={‘required‘: ‘确认密码不能为空‘},
			        widget=widgets.PasswordInput(attrs={‘class‘: ‘form-control‘})
			    )
			    email = fields.EmailField(
			        required=True,
			        error_messages={‘required‘: ‘邮箱不能为空‘, ‘invalid‘: ‘邮箱格式错误‘},
			        widget=widgets.TextInput(attrs={‘class‘: ‘form-control‘})
			    )
			    

			    # 自定义验证规则方式三:在当前类的方法中:clean_字段名称
			    dp_id = fields.ChoiceField(
			        choices=[],
			        error_messages={‘required‘: ‘请选择归属部门‘},
			        widget=widgets.Select(attrs={‘class‘: ‘form-control‘})
			    )
			    phone = fields.CharField(error_messages={‘required‘: ‘手机号不能为空‘})
			    # 钩子方法
			    def clean_phone(self,):
			        """
			        只能取当前字段的值,切勿取其他的值
			        必须得有返回值
			        :return: 
			        """
			        # 需要要验证的值,自己写正则进行验证
			        # 去取用户提交的值
			        value = self.cleaned_data[‘phone‘]
			        mobile_re = re.compile(r‘^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$‘)
			        if not mobile_re.match(value):
			            raise ValidationError(‘手机号码格式错误‘)
			        if models.UserInfo.objects.filter(phone=value).count():
			            raise ValidationError(‘手机号码已经存在‘)
			        return value

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

			    def clean(self):
			        print(self.cleaned_data,‘=====================‘)
			        pwd = self.cleaned_data.get(‘pwd‘)
			        pwd_confirm = self.cleaned_data.get(‘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(‘密码输入不一致‘))
			            return self.cleaned_data

		# add_user.html
			<form method="POST" novalidate>
		        {% csrf_token %}
		        <p> 用户名:{{ form.name }} {{ form.errors.name.0 }} </p>
		        <p> 密码:{{ form.pwd }} {{ form.errors.pwd.0 }} </p>
		        <p> 确认密码:{{ form.pwd_confirm }} {{ form.errors.pwd_confirm.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>

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




		- 常用插件
			参考:http://www.cnblogs.com/wupeiqi/articles/6144178.html

			from django.forms import widgets

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

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

		    # 多选:checkbox
		    # city = fields.MultipleChoiceField(
		    #     choices=((1, ‘上海‘), (2, ‘北京‘),),
		    #     widget=widgets.CheckboxSelectMultiple
		    # )

		    # 单选:radio
		    # city = fields.CharField(
		    #     initial=2,
		    #     widget=widgets.Radioselect(choices=((1,‘上海‘),(2,‘北京‘),))
		    # )

		- 初始化的时候赋值,包括单值和多值

		注意:写默认值时,多选的值对应一个列表



	- 中间件
		- 中间件的执行时机:请求到来和请求返回时执行
		- 中间件就是一个类,里面有2个方法(也可以没有):
			process_request(self,request)
				默认不写return,这本质上是 return None
			process_reponse(self,request,response)
				必须要写 return response

			另外还有:
			# 依次处理完所有request之后就进行路由匹配,然后再跳转到开头依次进行process_view函数处理
			process_view(self, request, callback, callback_args, callback_kwargs)

			# 捕获异常,自定义返回页面
			process_exception(self,request,exception)


		# Django中间件处理流程
		"""
			1. 循环中间件MIDDLEWARE列表里的中间件,依次执行每个中间件类的process_request(self,request)
			2. 进行路由匹配,拿到对应的视图函数
			3. 再次循环中间件MIDDLEWARE列表里的中间件,再从头依次执行每个中间件类的process_view(self, request, callback, callback_args, callback_kwargs)
			4. 执行对应的视图函数
			5. 逆序循环MIDDLEWARE列表里的中间件,依次执行每个中间件类的process_exception(self,request,exception)
			6. 再次逆序循环MIDDLEWARE列表里的中间件,依次执行每个中间件类的process_template_response(...)
			7. 再次逆序循环MIDDLEWARE列表里的中间件,依次执行每个中间件类的process_reponse(self,request,response)
		"""


		- 应用:
			- 记录访问日志
			- 判断用户是否登录

		# 中间件要继承 MiddlewareMixin类
		‘‘‘
		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
		‘‘‘



		‘‘‘
		HttpRequest.META

		一个标准的Python 字典,包含所有的HTTP 头部。具体的头部信息取决于客户端和服务器,下面是一些示例:

		CONTENT_LENGTH 			—— 请求的正文的长度(是一个字符串)。
		CONTENT_TYPE 			—— 请求的正文的MIME 类型。
		HTTP_ACCEPT 			—— 响应可接收的Content-Type。
		HTTP_ACCEPT_ENCODING	—— 响应可接收的编码。
		HTTP_ACCEPT_LANGUAGE 	—— 响应可接收的语言。
		HTTP_HOST 				—— 客服端发送的HTTP Host 头部。
		HTTP_REFERER 			—— Referring 页面。
		HTTP_USER_AGENT 		—— 客户端的user-agent 字符串。
		QUERY_STRING 			—— 单个字符串形式的查询字符串(未解析过的形式)。
		REMOTE_ADDR 			—— 客户端的IP 地址。
		REMOTE_HOST 			—— 客户端的主机名。
		REMOTE_USER 			—— 服务器认证后的用户。
		REQUEST_METHOD			—— 一个字符串,例如"GET" 或"POST"。
		SERVER_NAME 			—— 服务器的主机名。
		SERVER_PORT 			—— 服务器的端口(是一个字符串)。
		‘‘‘

		# 中间件示例1
		‘‘‘
		# settings.py
		MIDDLEWARE = [
			...
		    ‘middle.middleware.my_middleware1‘,
		    ‘middle.middleware.my_middleware2‘,
		]



		# ..\\middle\\middleware.py
		from django.shortcuts import render,HttpResponse,redirect
		from django.utils.deprecation import MiddlewareMixin

		class my_middleware1(MiddlewareMixin):
		    def process_request(self,request):
		        print(‘my_middleware1.process_request...‘)
		        print(request.path)
		        print(request.path_info)
		        print(request.method)
		        print(request.META[‘REMOTE_ADDR‘])
		        print(request.META[‘REMOTE_HOST‘])
		        print(request.META[‘REQUEST_METHOD‘])
		        print(request.META[‘HTTP_USER_AGENT‘])
		        print(request.META[‘HTTP_HOST‘])

		    def process_response(self,request,response):
		        print(‘my_middleware1.process_response...‘)
		        return response


		class my_middleware2(MiddlewareMixin):
		    def process_request(self,request):
		        print(‘my_middleware2.process_request...‘)

		    def process_response(self,request,response):
		        print(‘my_middleware2.process_response...‘)
		        return response
		‘‘‘
		
		# 中间件示例2 : 代替登录验证装饰器
		‘‘‘
		class my_middleware2(MiddlewareMixin):
		    def process_request(self,request):
		        if ‘/login/‘ == request.path_info:
		            return None
		        user_info = request.session.get(‘user_info‘)
		        if not user_info:
		            return redirect(‘/login/‘)

		    def process_response(self,request,response):
		        print(‘my_middleware2.process_response...‘)
		        return response
		‘‘‘




	- Django的缓存
	参考:http://www.cnblogs.com/wupeiqi/articles/5246483.html
		- 配置(默认不支持redis)
			- 开发调试,相当于没有
			- 本机内存中
			‘‘‘
				# 此缓存将内容保存至内存的变量中
			    # 配置:
			        CACHES = {
			            ‘default‘: {
			                ‘BACKEND‘: ‘django.core.cache.backends.locmem.LocMemCache‘,
			                ‘LOCATION‘: ‘unique-snowflake‘,
			            }
			        }
			‘‘‘
			- 本地文件中
			- 数据库中
			- Memcached
		- 使用
			- 全局 
				MIDDLEWARE = [
			        ‘django.middleware.cache.UpdateCacheMiddleware‘,
			        # 其他中间件...
			        ‘django.middleware.cache.FetchFromCacheMiddleware‘,
			    ]

			    CACHE_MIDDLEWARE_SECONDS = 10 # 设置缓存时间

			- 视图函数
				 from django.views.decorators.cache import cache_page

        		@cache_page(60 * 15)
        		def xxx(request):
        			pass

			- 局部模板
				a. 引入TemplateTag
			        {% load cache %}

			    b. 使用缓存
			        {% cache 5000 缓存key %}
			            缓存内容
			        {% endcache %}






	- 信号
		参考:http://www.cnblogs.com/wupeiqi/articles/5246483.html
		Django中提供了“信号调度”,用于在框架执行操作时解耦。
		通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。

		需求:在对数据库做增加操作时,记录操作日志


		推荐把信号的注册写到 项目目录下的 __init__()函数里

	‘‘‘
	# Django内置信号

	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          # 创建数据库连接时,自动触发
	‘‘‘

	‘‘‘
	信号模块引入位置:
	在项目文件加下的 __init__.py 文件里引入,并注册相关操作


	信号模块引入方式:

	from django.core.signals import request_finished
	from django.core.signals import request_started
	from django.core.signals import got_request_exception

	from django.db.models.signals import class_prepared
	from django.db.models.signals import pre_init, post_init
	from django.db.models.signals import pre_save, post_save
	from django.db.models.signals import pre_delete, post_delete
	from django.db.models.signals import m2m_changed
	from django.db.models.signals import pre_migrate, post_migrate

	from django.test.signals import setting_changed
	from django.test.signals import template_rendered

	from django.db.backends.signals import connection_created

	‘‘‘


	# 信号示例:旨在对数据库执行插入数据之前和之后触发各自的操作
	‘‘‘
		from django.db.models.signals import pre_save, post_save

		def pre_save_callback(sender, **kwargs):
		    print(">>>>>>>>>>> pre_save")
		    print(sender)
		    print(kwargs)

		def post_save_callback(sender, **kwargs):
		    print("################# post_save")
		    print(sender)
		    print(kwargs)

		pre_save.connect(pre_save_callback)
		post_save.connect(post_save_callback)
	‘‘‘

	# 执行结果:
	‘‘‘
	[18/Sep/2017 22:20:32] "POST /add_user/ HTTP/1.1" 200 1268
	>>>>>>>>>>> pre_save
	<class ‘app01.models.UserInfo‘>
	{‘signal‘: <django.db.models.signals.ModelSignal object at 0x0000000002CE79E8>, ‘instance‘: <UserInfo: UserInfo object>, ‘using‘: ‘default‘, ‘raw‘: False, ‘update_fields‘: None}
	################# post_save
	<class ‘app01.models.UserInfo‘>
	{‘signal‘: <django.db.models.signals.ModelSignal object at 0x0000000002CE7A90>, ‘instance‘: <UserInfo: UserInfo object>, ‘update_fields‘: None, ‘using‘: ‘default‘, ‘created‘: True, ‘raw‘: False}
	[18/Sep/2017 22:20:49] "POST /add_user/ HTTP/1.1" 302 0
	‘‘‘



	- Admin
		Django内置的Admin是对于model中对应的数据表进行增删改查提供的组件

		# 创建超级用户
		‘‘‘
			D:\\soft\\work\\Python_17\\day21\\form_demo>python manage.py createsuperuser
			Username (leave blank to use ‘liulixin‘): root
			Email address:
			Password:
			Password (again):
			This password is too short. It must contain at least 8 characters.
			Password:
			Password (again):
			>>>>>>>>>>> pre_save
			<class ‘django.contrib.auth.models.User‘>
			{‘signal‘: <django.db.models.signals.ModelSignal object at 0x0000000002CEA7F0>, ‘using‘: ‘default‘, ‘instance‘: <User: root>, ‘
			raw‘: False, ‘update_fields‘: None}
			################# post_save
			<class ‘django.contrib.auth.models.User‘>
			{‘signal‘: <django.db.models.signals.ModelSignal object at 0x0000000002CEA898>, ‘created‘: True, ‘update_fields‘: None, ‘instan
			ce‘: <User: root>, ‘using‘: ‘default‘, ‘raw‘: False}
			Superuser created successfully.

			D:\\soft\\work\\Python_17\\day21\\form_demo>
		‘‘‘

		更多admin做好的接口参考:http://www.cnblogs.com/wupeiqi/articles/7444717.html
		Django提供的对数据库的操作,admin.py 和 models.py 配合
		‘‘‘
			class Depart(models.Model):
			    """部门表"""
			    title = models.CharField(max_length=32) # 数据库里就是string类型

			    def __str__(self):
			        return self.title

			    class Meta:
			        verbose_name_plural = "部门表"

			class UserInfo(models.Model):
			    """用户表"""
			    name = models.CharField(max_length=32)
			    # email = models.EmailField(max_length=32)
			    email = models.CharField(max_length=32)
			    phone = models.CharField(max_length=32)
			    pwd = models.CharField(max_length=64)
			    dp = models.ForeignKey(to=‘Depart‘,to_field=‘id‘)

			    def __str__(self):
			        return self.name

			    class Meta:
			        verbose_name_plural = "用户表"
		‘‘‘


		在admin.py中只需要将Mode中的某个类注册,即可在Admin中实现增删改查的功能,如:
		‘‘‘
		# admin.py
			from django.contrib import admin
			from app01 import models

			# Register your models here.

			admin.site.register(models.UserInfo)
			admin.site.register(models.Depart)
		‘‘‘

		但是,这种方式比较简单,如果想要进行更多的定制操作,需要利用ModelAdmin进行操作,如:
		‘‘‘
		# admin.py
			from django.contrib import admin
			from app01 import models

			# Register your models here.

			class DepartAdmin(admin.ModelAdmin):
			    list_display = [‘id‘,‘title‘]
			    search_fields = [‘title‘, ]
			admin.site.register(models.Depart,DepartAdmin)


			class UserInfoAdmin(admin.ModelAdmin):
			    list_display = [‘id‘,‘name‘,‘email‘,‘phone‘,‘dp‘]
			    search_fields = [‘name‘, ]
			admin.site.register(models.UserInfo,UserInfoAdmin)
		‘‘‘


	- ModelForm

	- BBS项目练习
		- 前端知识捡一下
		- 各个小功能的设计和实现






用户表
	用户名
	密码
	邮件(可为空)

新闻表
	标题
	简介
	url
	新闻类型
	发布者
	发布时间
	新闻图表

	点赞个数	点赞时要更新字段,Django的F实现自增1
	评论个数


新闻类型表
	类型名


点赞记录表

评论记录表








"""
day22 
"""

"""
十一作业:CMDB

	- 采集资产,就是一个py文件,执行一个命令,通过正则取结果:{...}
	- API: http://www.qiyi.domain/xxx.html
	- POST请求,request.post(API,data={...})

	- Django程序
		- url:/assert.html 	-->  assert
		- def assert(request):
			request.POST
			写到数据库
		- index.html    -->  index
		  展示数据

	- 动态的左侧菜单:多级评论

"""


"""
day22 今日内容:

	- 新闻列表
	- 点赞(2次数据库操作,所以有可能存在脏数据)
		- 事物
		- 点赞+1的小动画
	- 多级评论
		- Python中的引用(指针)
		- 递归:深度优先搜索
	- 上传文件
		- 基于FormData对象
		- 伪ajax,兼容性更好
	- 分页

	- request.GET
		- 添加完/修改完 跳转回原来的页面

"""

今日内容:

- 反查
	- related_name  用于定义反向关联是的名称
	- related_query_name


	class A:
		title = models.CharField()

	class B:
		xxxx = xxxx
		fk1 = models.ForeignKey(related_name=‘xxx‘)
		fk2 = models.ForeignKey(related_query_name=‘xxx‘)
		fk3 = models.ManyToMany(related_name=‘xxx‘)


- ManyToMany 的 througth_fields 字段是有顺序要求的





- 点赞
	- 事物 from django.db import transaction
		   with transaction.atomic():
		   		pass
	- 返回值
		- 面向对象
		- base + 继承
		- json.dumps(对象.__dict__)

		- +/-动画
		- 在ajax操作时候,回调函数中的 $(this) 已经不是原来的 $(this)
		- css 
			- position: fixed,absolute,relative
			- setInterval定时器
			- setTimeout 只执行一次
			- JS的闭包
			"""
				var obj = setInterval(function () {
		            fontSize =+ 5;
		            top -= 5;
		            right -= 5;
		            opacity -= 0.1;
		            tag.style.fontSize = fontSize + "px";
		            tag.style.top = top;
		            tag.style.right = right;
		            tag.style.opacity = opacity;
		            // 终止定时器
		            if(opacity <= 0){
		                clearInterval(obj);
		                tag.remove();
		            }
		        },100);
			"""

- 字典和列表的结合查找

li = [
	{‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:1},
	{‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:2},
	{‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:3},
	{‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:4},
]

dic = {}
for item in li:
	dic[item[‘id‘]] = item





"""
练习题:
li = [
    {‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:1,"children":[],‘parent_id‘:None},
    {‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:2,"children":[],‘parent_id‘:None},
    {‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:3,"children":[],‘parent_id‘:1},
    {‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:4,"children":[],‘parent_id‘:2},
    {‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:5,"children":[],‘parent_id‘:1},
    {‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:6,"children":[],‘parent_id‘:3},
]
#结果:
result = [
    {‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:1,"children":[{‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:3,"children":[ {‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:6,"children":[],‘parent_id‘:3},],‘parent_id‘:1},{‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:5,"children":[],‘parent_id‘:1},],‘parent_id‘:None},
    {‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:2,"children":[{‘user‘:‘xxx‘,‘pwd‘:‘xxx‘,‘id‘:4,"children":[],‘parent_id‘:2},],‘parent_id‘:None},
]
"""







- 多级评论
{{ comment_list|safe }}

	- 字典,列表   通过引用赋值,一个修改,全部都改;
	- 递归


和左侧菜单原理是一样的


给评论绑定点击事件
	- 发送ajax请求
	- 把新闻ID传给后台,查询当前新闻下面的所有评论(models.xxx.objects.values(...))
	- 后台渲染HTML,返回给前端  或者  把数据字典返回给前端,让前端来递归生成多级结构


应用:菜单




上传文件并预览
	- 基于FormData 
		- 缺点:兼容性不好
		- 优点:ajax直接发送
	- 伪造发送ajax
		- iframe标签  天生局部刷新  向其他网址发数据,页面不刷新
		- form表单  天生整体刷新



	- 如果是普通的POST:
		- ajax
		- 伪ajax 
	- 如果是上传文件:
		- 伪ajax


a标签包含input的file标签,同时把input标签的透明度置为0



其他:
	- 如何通过Python代码发送POST数据?

		import requests
		requests.get()
		requests.post()

	- 两种发送数据的方式(区别是请求头里的content-type不一样):
		- data
		- json  ->  content-type:application/json  (django 不处理这个类型,所以request.POST 是空的,需要去request.body)



‘‘‘
import requests


# response = requests.get(‘http://127.0.0.1:8003/asset.html‘)
# print(response.text)
data_dict = {
    ‘k1‘:‘v1‘,
    ‘k2‘:‘v2‘
}

# content-type: application/x-www-form-urlencoded
# response = requests.post(‘http://127.0.0.1:8003/asset.html‘,data=data_dict)
# print(response.text)

# 采集资产并且汇报
# content-type: appcation/json
response = requests.post(‘http://127.0.0.1:8003/asset.html‘,json=data_dict)
print(response.text)
‘‘‘




	- 如何保留原来页面的条件
		request.GET
		request.GET.urlencode()

		from django.http.request import QueryDict

		"""mutable这个值默认是False,是不允许修改的"""
		obj = QueryDict(mutable=True)
		obj[‘xxxxxx‘] = request.GET.urlencode()
		url_param = obj.urlencode()


		request.GET.get(‘xxxxxx‘)


		要点:发送POST请求的时候不要写action,还会提交到当前页面




作业:
	- 评论用js递归构造实现多级
	- 尝试去做CMDB
		- agent    收集服务器相关数据,发送到API;用json汇报
		- server   提供API给agent,接收数据,然后存储,最后页面展示(表的增删改查,要保留原来地址)

		- 展示的时候加上左侧菜单

		- 菜单扩展:
			- 默认展开






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

"""
day23	-	爬虫部分
"""

爬虫相关
	- 基本操作
		- 概要
			- 发送http请求	requests模块
			- 提取指定信息 	正则	Beautifulsoup模块
			- 数据持久化

		- Python的2个模块
			- requests
			- Beautifulsoup

		- Http请求相关知识
			- 请求
				- 请求头 
					- cookie
				- 请求体 
					- 发送的内容
			- 响应 
				- 响应头 
					- 浏览器读取
				- 响应体
					- 看到的内容

			- 特殊
				- cookie
				- csrf_token
				- content-type 用来指定客户端按照哪种格式进行解析


	- 性能相关
		- 进程
		- 线程
		- 协程

		- 【协程】异步非阻塞:充分利用系统资源


	- scrapy框架
		- 学习scrapy的规则


	- redis&scrapy组件:完成一个简单的分布式爬虫



内容详细
	- 基本操作	Python伪造浏览器发送请求

		pip3 install requests
		pip3 install Beautifulsoup4

		import requests
		from bs4 import BeautifulSoup


		response = requests.get("http://www.baidu.com")
		response.text  ->  网页内容

		soup = Beautifulsoup(response.text,‘html.parse‘)

		# 从上到下第一个 <h3 class=‘t‘> 标签
		soup.find(name=‘h3‘,attrs={‘class‘:‘t‘})
		# 查找全部 <h3>标签
		soup.find_all(name=‘h3‘)

		...

	模块
		requests
			response = requests.get(url=‘url路径‘)
			# 解决乱码问题
			response.encoding = response.apparent_encoding

			GET请求:
				requests.get(url=‘www.baidu.com‘)
				data = "http GET / ...."
				requests.get(url=‘www.baidu.com?page=1‘)
				data = "http GET page=1 ...."
				requests.get(url=‘www.baidu.com‘,params={‘page‘:1})


			POST请求:
				requests.post(url=‘www.baidu.com‘,data={‘name‘:‘alex‘,‘age‘:18}) # 默认携带请求头类型:application/x-www-form-urlencoded

				requests.post(url=‘www.baidu.com‘,json={‘name‘:‘alex‘,‘age‘:18}) # 默认携带请求头类型:application/json

				# POST请求既可以在请求体里传参,又可以在url里传参
				requests.post(url=‘www.baidu.com‘,params={‘page‘:1},json={‘name‘:‘alex‘,‘age‘:18})



				补充:
					django里的 request.POST 里的值是django根据请求体里的数据转换过来的
						所以,如果body里的数据格式不对,那么就转换不了,导致request.POST里面没有值
					django里的 request.body 里永远有值
					django里的 request.POST 可能没有值



		BeautifulSoup
			soup = BeautifulSoup(‘html格式字符串‘,‘html.parser‘)
			tag = soup.find(name=‘div‘,attrs={...})
			tag = soup.find_all(name=‘div‘,attrs={...})

			tag.find(‘h3‘).text
			tag.find(‘h3‘).content 
			tag.find(‘h3‘).get(‘属性名称‘)
			tag.find(‘h3‘).attrs[‘属性名称‘]







服务器端不能主动给客户端发消息
但是websocket可以

- 【轮询】     	http协议,客户端轮询(每秒1次)请求服务端;一次请求,服务端收到后不管有没有新消息都立即返回
- 【长轮询】 	http协议,客户端发来请求,服务器把客户端给hang住,直到服务端收到新消息并发送给所有客户端、才断开连接;
				客户端收到消息后,再立即发请求到服务端进行下一次hang住。
				hang住,有一个超时时间,web微信超时时间是25s
				应用:web微信
- 【WebSocket】	不是http协议,建立在tcp之上
				一次连接不断开,双工通道,可以互相发送消息
				但是浏览器兼容性不太好,以后将会应用的更广泛




浏览器有同源策略
ajax发送跨域请求是接收不到结果的





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




#!/usr/bin/python
# -*- coding:utf-8 -*-

import requests

requests.request()

requests.get(url=‘xxx‘)
# 本质上就是:
requests.request(method=‘get‘,url=‘xxx‘)

import json
requests.post(url=‘xxx‘,data={‘name‘:‘alex‘,‘age‘:18}) # content_type: application/x-www-form-urlencoded
requests.post(url=‘xxx‘,data="name=alex&age=18")   # content_type: application/x-www-form-urlencoded
# 不伦不类
requests.post(url=‘xxx‘,data=json.dumps({‘name‘:‘alex‘,‘age‘:18}))  # content_type: application/x-www-form-urlencoded
# 利用headers参数重写 Content_type
requests.post(url=‘xxx‘,data=json.dumps({‘name‘:‘alex‘,‘age‘:18}),headers={‘Content_type‘:‘application/json‘})  # content_type: application/x-www-form-urlencoded
requests.post(url=‘xxx‘,json={‘name‘:‘alex‘,‘age‘:18})  # content_type: application/json


"""
1.method
2.url
3.params
4.data
5.json
6.headers
7.cookies

8.files
9.auth
10.timeout
11.allow_redirects
12.proxies
13.stream
14.cert

=================== session,保存请求相关信息  ==================
session = requests.Session()
session.get(url=‘xxx‘)
session.post(...)
"""

"""
8.files 用作文件上传
"""
file_dict = {
    ‘f1‘: open(‘readme‘, ‘rb‘)
}
requests.post(url=‘xxx‘,file=file_dict)
# 发送文件,定制文件名
# file_dict = {
#   ‘f1‘: (‘test.txt‘, open(‘readme‘, ‘rb‘))
# }
# requests.request(method=‘POST‘,
# url=‘http://127.0.0.1:8000/test/‘,
# files=file_dict)

# 发送文件,定制文件名
# file_dict = {
#   ‘f1‘: (‘test.txt‘, "hahsfaksfa9kasdjflaksdjf")
# }
# requests.request(method=‘POST‘,
# url=‘http://127.0.0.1:8000/test/‘,
# files=file_dict)



"""
9.auth  基本认证    路由器登录
"""
from requests.auth import HTTPBasicAuth,HTTPDigestAuth

requests.get(‘https://api.github.com/user‘,auth=HTTPBasicAuth(‘gypsying‘,‘password‘))


"""
timeout     (连接超时,响应超时)
"""
requests.get(‘http://google.com‘,timeout=3)
requests.get(‘http://google.com‘,timeout=(5,1))


"""
allow_redirects
"""

"""
proxies 应对IP被封的情况
"""
proxyDict = {
    "http": "61.172.249.96:80",
    "https": "http://61.185.219.126:3128",
}
proxies = {‘http://10.20.1.128‘: ‘http://10.10.1.10:5323‘}

"""
stream
"""
from contextlib import closing
with closing(requests.get(‘xxx‘,stream=True)) as f:
    for i in f.iter_content():
        print(i)




requests.put()
requests.delete()





BeautifulSoup
	- find()
	- find_all()
	- get()
	- attrs
	- text

soup = BeautifulSoup(‘html格式字符串‘,‘html.parser‘)
soup = BeautifulSoup(‘html格式字符串‘,features=‘lxml‘)	第三方,需额外安装,但是速度比‘html.parser‘更快


soup = BeautifulSoup(‘html格式字符串‘,‘html.parser‘)
tag = soup.find(attrs={‘class‘:‘c1‘})
tag.name  ->  标签名字

tag = soup.find(attrs={‘class‘:‘c1‘})
等价于:
tag = soup.find(class_=‘c1‘)

print(tag.attrs)

tag.attrs[‘id‘] = 1
del tag.attrs[‘class‘]
# attrs 进行增删改查都可以


tag.children  	所有孩子
tag.descendants	所有后代
tag.find_all()	包含的所有标签,并且递归了
tag.find_all(recursive=False)	包含的所有标签,不递归

tag.clear()		清空内部元素,保留自己
tag.decompose()	递归删除所有标签,包含自己
res = tag.extract()	相当于字典的pop,其余同decompose()


tag = soup.find(class_=‘c1‘)	# 对象
tag.decode()	# 对象变成字符串
tag.encode()	# 对象变成字节

tag.find(‘a‘)
# tag = soup.find(‘a‘)
# print(tag)
# tag = soup.find(name=‘a‘, attrs={‘class‘: ‘sister‘}, recursive=True, text=‘Lacie‘)
# tag = soup.find(name=‘a‘, class_=‘sister‘, recursive=True, text=‘Lacie‘)
# print(tag)

find_all()
# tags = soup.find_all(‘a‘)
# print(tags)
 
# tags = soup.find_all(‘a‘,limit=1)
# print(tags)
 
# tags = soup.find_all(name=‘a‘, attrs={‘class‘: ‘sister‘}, recursive=True, text=‘Lacie‘)
# # tags = soup.find(name=‘a‘, class_=‘sister‘, recursive=True, text=‘Lacie‘)
# print(tags)
 
 
# ####### 列表 #######
# v = soup.find_all(name=[‘a‘,‘div‘])
# print(v)
 
# v = soup.find_all(class_=[‘sister0‘, ‘sister‘])
# print(v)
 
# v = soup.find_all(text=[‘Tillie‘])
# print(v, type(v[0]))
 
 
# v = soup.find_all(id=[‘link1‘,‘link2‘])
# print(v)
 
# v = soup.find_all(href=[‘link1‘,‘link2‘])
# print(v)
 
# ####### 正则 #######
import re
# rep = re.compile(‘p‘)
# rep = re.compile(‘^p‘)
# v = soup.find_all(name=rep)
# print(v)
 
# rep = re.compile(‘sister.*‘)
# v = soup.find_all(class_=rep)
# print(v)
 
# rep = re.compile(‘http://www.oldboy.com/static/.*‘)
# v = soup.find_all(href=rep)
# print(v)
 
# ####### 方法筛选 #######
# def func(tag):
# return tag.has_attr(‘class‘) and tag.has_attr(‘id‘)
# v = soup.find_all(name=func)
# print(v)
 
 
# ## get,获取标签属性
# tag = soup.find(‘a‘)
# v = tag.get(‘id‘)
# print(v)


from bs4.element import Tag

tag.has_attr()
tag.text  等价于 tag.get_text()


v = tag.index(tag.find(‘div‘))


tag.text
tag.string 也可以获取内容,并扩展了修改内容
tag.string = "xxxx"
tag.stripped_strings 相当于join给分割成了list 
tag.children
for item in tag.children:
	print(item,type(item))




from bs4.element import Tag
tag= Tag(name=‘i‘,attrs={‘id‘:‘it‘})
tag.string = "asasasasasasazxzxzx"


soup.find(id=‘xxx‘).append(tag)




""" 扩展copy模块 """
import copy
copy.deepcopy()
...



tag.wrap(tag1)
tag.unwrap()

++++++++++++++++++++++++++++++++++++


内容梳理:
	- 汽车之间新闻爬取示例
	- github和抽屉自动登录  以及 登陆后的操作
	- requests 和 Beautifulsoup 基本使用
	- 轮训和长轮询
	- Django 里 content-type问题
		request.POST 
		request.body






作业:web微信
	1. 二维码显示
	2. 长轮询 check_login() :ajax递归  (js递归没有层数限制)
	3. 检测是否已经扫码
		- 扫码之后201:替换头像 base64:...
		src="img_path"
		或者
		src="base64:xxxxxxxx...."
		- 扫码之后继续轮训,等待用户点击确认
		- 点击确认之后,返回200 
			response.text redirect_url-....
		- 获取最近联系人信息






下节课前安装
	twsited
	scrapy框架







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

day24

	Web微信

	高性能

	scrapy


requests.post(data=xxx)   ->   Form Data

requests.post(json=xxx)   ->   Request Payload


HttpResponse()  参数可以是字符串也可以是字节

response.text         字符串
response.content 	  字节


# 获取最近联系人然后进行初始化



# 获取头像



# 拿联系人列表
response = requests.get(url,cookies=xxx)
response.encoding = ‘utf-8‘
print(json.loads(response.text))



Web微信总结
	- 头像防盗链
		- headers
		- cookies

	- 检测请求:
		- tip | pass_ticket | ...
		- redirect_url和真实请求的url是否一致

	- session 保存关键点的cookies和关键变量值
		- qrcode 和 ctime
		- login_cookie_dict
		- ticket_dict_cookie
		- ticket_dict
		- init_cookie_dict
		- init_dict 

		- all_cookies = {}
		- all_cookies.update(...)




json序列化的时候是可以加参数的:

data = {
	‘name‘:‘alex‘,
	‘msg‘:‘中文asa‘
}
import json
print(json.dumps(data))
按Unicode显示

print(json.dumps(data,ensure_ascii=False))
按中文显示



json.dumps() 之后是字符串
requests.post() 默认是按照 latin-1 编码,不支持中文

所以改成直接发bytes:
requests.post(data=json.dumps(data,ensure_ascii=False).encode(‘utf-8‘))


发送消息需带上cookies






发送消息


检测是否有新消息到来
接收消息










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

高性能相关


100张图片,下载




使用一个线程完成并发操作
是配合IO多路复用完成的


爬虫:
	- 简单的爬虫
		- requests+bs4+twsited+asyncio
	- scrapy框架
		- 下载页面:twsited
		- 解析页面:自己的HTML解析
		- 可限速
		- 去重
		- 递归 一层一层的爬,成倍速的增长,还可以限制层数
		- 代理
		- https
		- 中间件 
		...




scrapy
	- scrapy startproject sp1
	- cd sp1
	- scrapy genspider baidu baidu.com 
	- scrapy genspider chouti chouti.com 
	- scrapy crawl chouti --nolog


	- name 
	- allow_dimains
	- start_urls
	- parse(self,response)
		- yield Item   持久化
		- yield Request(url,callback)  把url放到调度器队列里



本周作业:
1.Web微信
	自己去写,写完跟老师的对比

2.高性能总结文档(源码示例+文字理解)

3.任意找一个网站,用Scrapy去爬
	- 煎蛋
	- 拉钩
	- 知乎
	- 抽屉
	- ... 

不遵循爬虫规范:
ROBOTSTXT_OBEY = False

可能会失败:没有带请求头


起始url:自定义start_requests(self):















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

day25

内容回顾:
	- scrapy
		- 创建project
		- 创建爬虫
		- 编写:
			- 类
				- start_urls = [‘xx‘]
				- def pasrse(self,response):
					yield Item 对象     --->  进入Pipeline做持久化存储
					yield Request对象   --->  进入调度中心,递归...

				# 面向对象的封装
				- 初开始引擎执行的是父类的 start_requests()方法,可自己重写:
					def start_requests(self):
						for url in self.start_urls:
							yield Request(url=url,callback=self.parse)
							或者:
							return [Request(url=url,callback=self.parse).]

					"""要么返回一个可迭代对象,要么返回一个生成器对象!!!"""

				- Pipeline
					- process_item
					- open_spider
					- close_spider
					...

				- request对象("地址",回调函数)
		- 执行 

	- 高性能相关
		- 多线程【适用于IO密集型】和多进程【适用于计算密集型】
			- GIL锁 锁的是进程,单个进程只能有一个线程被CPU调度,即使这个CPU有多个核
		- 尽可能利用线程:
			- 一个线程,基于协程:
				- 协程,greenlet
				- 遇到IO就切换
				- 代表就是 Gevent
			- 一个线程,基于事件循环:
				- IO多路复用 
				- Socket,setBlocking(False)
				- 代表:Twisted 、tornado 、、、




今日内容:
	- scrapy
		- Cookie操作
		- Pipeline
		- 中间件
		- 扩展(信号。。。)
		- 自定义命令
		- 其他配置文件
		- 扩展:scrapy-redis

	- Tornado 和 Flash 简单实用(轻量级框架)
		- 学习基本流程和规则



内容详细:
	- Scrapy

		- 创建项目:
			D:\\soft\\work\\Python_17\\day25>scrapy startproject day25spider
			New Scrapy project ‘day25spider‘, using template directory ‘d:\\\\soft\\\\work\\\\pyth
			on35\\\\lib\\\\site-packages\\\\scrapy\\\\templates\\\\project‘, created in:
			    D:\\soft\\work\\Python_17\\day25\\day25spider

			You can start your first spider with:
			    cd day25spider
			    scrapy genspider example example.com

			D:\\soft\\work\\Python_17\\day25>cd day25spider

			D:\\soft\\work\\Python_17\\day25\\day25spider>scrapy genspider chouti chouti.com
			Created spider ‘chouti‘ using template ‘basic‘ in module:
			  day25spider.spiders.chouti

			D:\\soft\\work\\Python_17\\day25\\day25spider>


		- 执行
			- scrapy crawl chouti
			- scrapy crawl chouti --nolog


		# 面向对象的封装
		- 初开始引擎执行的是父类的 start_requests()方法,可自己重写:
			def start_requests(self):
				for url in self.start_urls:
					yield Request(url=url,callback=self.parse)
					或者:
					return [Request(url=url,callback=self.parse).]

			"""要么返回一个可迭代对象,要么返回一个生成器对象!!!"""
			- 因为内部会有 iter() 的过程,代码见:
				- from scrapy.crawler import Crawler 
				- 里面的 crawl() 方法,如下:
				    def crawl(self, *args, **kwargs):
				        assert not self.crawling, "Crawling already taking place"
				        self.crawling = True

				        try:
				            self.spider = self._create_spider(*args, **kwargs)
				            self.engine = self._create_engine()
				            start_requests = iter(self.spider.start_requests())
				            yield self.engine.open_spider(self.spider, start_requests)
				            ...


		- 引擎拿到url之后放入到调度器里
		- 下载器去调度器拿url,进行下载,下载完成之后执行 callback()

		- 筛选器
			hxs = HtmlXPathSelector(response)
			hxs.xpath(‘//div[@id="i1"]‘)
			hxs.xpath(‘//div[@id="i1"]/text()‘)
			hxs.xpath(‘//div[@id="i1"]/@href‘)
			hxs.xpath(‘//div[@id="i1"]/@href‘).extract()
			hxs.xpath(‘//div[@id="i1"]/@href‘).extract_first()
			for url in hxs.xpath(‘//div[@id="i1"]/@href‘).extract():
				yield Item(name=url)


		- cookie
			- 登录抽屉,自动点赞
			from scrapy.http.cookies import CookieJar
			cookie_jar = CookieJar()
        	cookie_jar.extract_cookies(response, response.request)


		- Pipeline
			- 可以写5个方法
			- procsss_item()
				- return item 
				- raise DropItem()
			- 多个pipeline时,跳过下一个pipeline:
				from scrapy.exceptions import DropItem()
				if ENV==‘debug‘:
					raise DropItem()
				else:
					return Item


		- url去重规则 (利用set集合)
			"""
			默认是有去重规则的:
			def start_requests(self):
				for url in self.start_urls:
					yield Request(url=url,callback=self.parse,dont_filter=False)
			"""
			- settings.py 里面加上自己写的插件
				DUPEFILTER_CLASS = ‘scrapy.dupefilter.RFPDupeFilter‘  # 默认的去重规则
				DUPEFILTER_CLASS = ‘xxx.yyy.‘  # 自定义过滤插件
			- 模仿RFPDupeFilter写自己的插件
			- url 做 类 md5 操作 (fingerprint...)

			"""
				class RepeatUrl:
				    def __init__(self):
				        self.visited_url = set()

				    @classmethod
				    def from_settings(cls, settings):
				        """
				        初始化时,调用
				        :param settings: 
				        :return: 
				        """
				        return cls()

				    def request_seen(self, request):
				        """
				        检测当前请求是否已经被访问过
				        :param request: 
				        :return: True表示已经访问过;False表示未访问过
				        """
				        if request.url in self.visited_url:
				            return True
				        self.visited_url.add(request.url)
				        return False

				    def open(self):
				        """
				        开始爬去请求时,调用
				        :return: 
				        """
				        print(‘open replication‘)

				    def close(self, reason):
				        """
				        结束爬虫爬取时,调用
				        :param reason: 
				        :return: 
				        """
				        print(‘close replication‘)

				    def log(self, request, spider):
				        """
				        记录日志
				        :param request: 
				        :param spider: 
				        :return: 
				        """
				        print(‘repeat‘, request.url)
			"""




		- 自定义命令
			- spiders同级目录新建 commands 目录
			- 创建 crawlall.py 

				from scrapy.command import ScrapyCommand
				class Foo(ScrapyCommand):
					...
					def run(self):
						...

			- 把 commands 注册到 setting.py



		- 可迭代对象:具有 __iter__() 方法,并且执行后可以返回迭代器
		- 迭代器:具有 __next__() 方法 并且逐一向后取值
		- 生成器:函数中具有yield关键字
			- 具有 __iter__() 返回的还是自己本身
			- 具有 __next__()




		- 下载中间件
			- 方法:
				- process_request()

				- process_response()

				- process_exception()


			- process_request() 应用
				- 自定义下载模块,而不是用scrapy自带的下载模块
				- 定制请求头/cookie  避免每次请求都带上同样的重复代码
					request.headers[‘Content-Type‘] = "application/x-www-form-urlencoded; charset=UTF-8"
				- 设置代理
					- os.environ

				- 默认的代理规则:
					from scrapy.downloadermiddlewares.httpproxy import HttpProxyMiddleware
					from urllib.request import getproxies

				- HTTPS 

				- 设置代理需注意:
					- 默认代理是使用的环境变量
						os.environ[‘xxxxxx_proxy‘]
						程序启动之前,先设置 os.environ[‘xxx_proxy‘] = "xxxxxxxx"

					- 自定义代理中间件
						"""
							def to_bytes(text, encoding=None, errors=‘strict‘):
						        if isinstance(text, bytes):
						            return text
						        if not isinstance(text, six.string_types):
						            raise TypeError(‘to_bytes must receive a unicode, str or bytes ‘
						                            ‘object, got %s‘ % type(text).__name__)
						        if encoding is None:
						            encoding = ‘utf-8‘
						        return text.encode(encoding, errors)
						        
						    class ProxyMiddleware(object):
						        def process_request(self, request, spider):
						            PROXIES = [
						                {‘ip_port‘: ‘111.11.228.75:80‘, ‘user_pass‘: ‘‘},
						                {‘ip_port‘: ‘120.198.243.22:80‘, ‘user_pass‘: ‘‘},
						                {‘ip_port‘: ‘111.8.60.9:8123‘, ‘user_pass‘: ‘‘},
						                {‘ip_port‘: ‘101.71.27.120:80‘, ‘user_pass‘: ‘‘},
						                {‘ip_port‘: ‘122.96.59.104:80‘, ‘user_pass‘: ‘‘},
						                {‘ip_port‘: ‘122.224.249.122:8088‘, ‘user_pass‘: ‘‘},
						            ]
						            proxy = random.choice(PROXIES)
						            if proxy[‘user_pass‘] is not None:
						                request.meta[‘proxy‘] = to_bytes("http://%s" % proxy[‘ip_port‘])
						                encoded_user_pass = base64.encodestring(to_bytes(proxy[‘user_pass‘]))
						                request.headers[‘Proxy-Authorization‘] = to_bytes(‘Basic ‘ + encoded_user_pass)
						                print "**************ProxyMiddleware have pass************" + proxy[‘ip_port‘]
						            else:
						                print "**************ProxyMiddleware no pass************" + proxy[‘ip_port‘]
						                request.meta[‘proxy‘] = to_bytes("http://%s" % proxy[‘ip_port‘])
						"""

			- 自定义下载中间件
			"""
				class DownMiddleware1(object):
				    def process_request(self, request, spider):
				        """
				        请求需要被下载时,经过所有下载器中间件的process_request调用
				        :param request: 
				        :param spider: 
				        :return:  
				            None,继续后续中间件去下载;
				            Response对象,停止process_request的执行,开始执行process_response
				            Request对象,停止中间件的执行,将Request重新调度器
				            raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception
				        """
				        pass



				    def process_response(self, request, response, spider):
				        """
				        spider处理完成,返回时调用
				        :param response:
				        :param result:
				        :param spider:
				        :return: 
				            Response 对象:转交给其他中间件process_response
				            Request 对象:停止中间件,request会被重新调度下载
				            raise IgnoreRequest 异常:调用Request.errback
				        """
				        print(‘response1‘)
				        return response

				    def process_exception(self, request, exception, spider):
				        """
				        当下载处理器(download handler)或 process_request() (下载中间件)抛出异常
				        :param response:
				        :param exception:
				        :param spider:
				        :return: 
				            None:继续交给后续中间件处理异常;
				            Response对象:停止后续process_exception方法
				            Request对象:停止中间件,request将会被重新调用下载
				        """
				        return None
			"""


		- 爬虫中间件



		- 扩展
			- scrapy 已经给埋好点了 
			- from scrapy import signals
			"""
				engine_started = object()
				engine_stopped = object()
				spider_opened = object()
				spider_idle = object()
				spider_closed = object()
				spider_error = object()
				request_scheduled = object()
				request_dropped = object()
				response_received = object()
				response_downloaded = object()
				item_scraped = object()
				item_dropped = object()
			"""
			"""
				from scrapy import signals

				class MyExtension(object):
				    def __init__(self, value):
				        self.value = value

				    @classmethod
				    def from_crawler(cls, crawler):
				        val = crawler.settings.getint(‘MMMM‘)
				        ext = cls(val)

				        crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
				        crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)

				        return ext

				    def spider_opened(self, spider):
				        print(‘open‘)

				    def spider_closed(self, spider):
				        print(‘close‘)
			"""






		- HTTPS证书 
			- 默认 
				DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
        		DOWNLOADER_CLIENTCONTEXTFACTORY = "scrapy.core.downloader.contextfactory.ScrapyClientContextFactory"

			- 自定义HTTPS证书
				DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
        		DOWNLOADER_CLIENTCONTEXTFACTORY = "step8_king.https.MySSLFactory"

        		"""
					from scrapy.core.downloader.contextfactory import ScrapyClientContextFactory
			        from twisted.internet.ssl import (optionsForClientTLS, CertificateOptions, PrivateCertificate)
			        
			        class MySSLFactory(ScrapyClientContextFactory):
			            def getCertificateOptions(self):
			                from OpenSSL import crypto
			                v1 = crypto.load_privatekey(crypto.FILETYPE_PEM, open(‘/Users/wupeiqi/client.key.unsecure‘, mode=‘r‘).read())
			                v2 = crypto.load_certificate(crypto.FILETYPE_PEM, open(‘/Users/wupeiqi/client.pem‘, mode=‘r‘).read())
			                return CertificateOptions(
			                    privateKey=v1,  # pKey对象
			                    certificate=v2,  # X509对象
			                    verify=False,
			                    method=getattr(self, ‘method‘, getattr(self, ‘_ssl_method‘, None))
	            """




	        - 设置请求头的方法:
	        	- 每次一次 yield Request(headers={...})
	        	- 在下载中间件里分门别类的自定义headers
	        	- 在settings.py里粗暴的统一定义好一个headers



	        - 多个爬虫的情况:
	        	- 去重规则写在哪?
	        	- 深度优先/广度优先 规则写在哪?
	        
	        	调度器


	        - 其他 : http://www.cnblogs.com/wupeiqi/articles/6229292.html





	- 分布式爬虫:scrapy-redis组件
		- 存储
			- 调度器		
			- 去重规则 

		# 是调度器触发了去重规则

		- 流程
			- 连接redis
			- 指定调度器时,调用了去重规则的 request_seen()方法


		- scrapy-redis参考:http://www.cnblogs.com/wupeiqi/articles/6912807.html







	- Django 自己没有写socket,而是使用的 WSGI协议 (代表是 wsgiref)




	- Flask Web框架
	参考:http://www.cnblogs.com/wupeiqi/articles/7552008.html

		- Web框架
			- 路由
			- 视图 
			- 模板渲染

		- Flask 自己也没写socket,而是依赖实现WSGI协议的模块:Werkzeug

		"""
			from flask import Flask
			app = Flask(__name__)
			 
			@app.route(‘/‘)
			def hello_world():
			    return ‘Hello World!‘
			 
			if __name__ == ‘__main__‘:
			    app.run()
		"""

		- @app.route(‘/‘) 返回了一个函数 func
		- 执行 func(hello_world)

		- url两种添加方式:
			- 方式一
				@app.route(‘/‘)
				def hello_world():
				    return ‘Hello World!‘
			- 方式二
				def index():
					return ‘Hello World!‘
				add_url_rule(‘/index‘,view_func=index)

		- 路由系统
			- 固定
				@app.route(‘/xxx/‘)
				def hello_world():
				    return ‘Hello World!‘
			- 不固定
				"""
				Flask只支持如下几种不固定的url匹配:
					@app.route(‘/user/<username>‘)
					@app.route(‘/post/<int:post_id>‘)
					@app.route(‘/post/<float:post_id>‘)
					@app.route(‘/post/<path:path>‘)
					@app.route(‘/login‘, methods=[‘GET‘, ‘POST‘])
				"""

				@app.route(‘/xxx/<int:uid>‘)
				def hello_world(uid):
				    return ‘Hello World!‘ + str(uid)


			- Flask本身不支持正则匹配,需要自定制正则规则



			- 反向生成url:
				url_for(‘index‘)
				
				@app.route(‘/index.htm‘)
				def index():
					return "首页"


			- session加密之后放到浏览器的cookie里,server端不保存
			- 第三方插件: Flask-Session


			- obj() 就是调用类的 __call__() 方法


			- 蓝图:文件夹的堆放






	""" 重要 """
	- HTTP method 有多少种?分别是什么?
	看django的CBV里面的源码






	- Tornado







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

day26

xxx公司的CRM项目
	- 项目概要
		- 权限管理 (写成一个公共的组件/app,适用于Django项目)       *****
		- low的增删改查												***
		- 增删改查的组件 (写成公共组件/app)							****



内容回顾:
	- .all  .values  .values_list
		models.xxx.objects.all()      				--> [obj,obj,obj,...]
		models.xxx.objects.values(‘id‘,‘name‘)      --> [{‘id‘:1,‘name‘:‘alex‘},{‘id‘:2,‘name‘:‘standby‘},...]
		models.xxx.objects.values_list(‘id‘,‘name‘) --> [(1,‘alex‘),(2,‘standby‘,....)]


	- 中间件是什么?有什么用?
		可以拦截所有的请求和响应,进行一些处理
		中间件执行时有序的,从上到下执行,如果当前中间件不允许过,那么就不会再执行后面的中间件
		比如说:对用户的请求做下黑名单

		process_request() 默认没有返回值,即返回None; 则继续往下执行
		但是如果有返回值,则不再继续往下执行,直接返回;

		每个中间件就是一个类:
			class Md1:
				def process_request(self,request):
					pass
				def process_response(self,request,response):
					return response
			class Md2:
				def process_request(self,request):
					pass
				def process_response(self,request,response):
					return response

			settings.py里配置

				MIDDLEWARE = [
					"XXXXXX.XXXXX.Md1",
					"XXXXXX.XXXXX.Md2",
				]

	- ORM创建表

		- ForeignKey常用操作
			class A(Model):
				name = models.CharField(...)

			class B(Model):
				name = models.CharField(...)
				fk = models.ForeignKey(A)

			a. B表中有几列:
				id
				name
				fk_id

			b. 跨表操作
				b_list = models.B.objects.all()
				for item in b_list:
					item.id
					item.name
					item.fk
					item.fk_id
					item.fk.id
					item.fk.name

			c. 跨表操作
				b_list = models.B.objects.values(‘id‘,‘name‘,‘fk_id‘,‘fk__name‘)
				for item in b_list:
					item[‘id‘]
					item[‘name‘]
					item[‘fk_id‘]
					item[‘fk__name‘]

			d. 跨表操作
				b_list = models.B.objects.vlaues_list(‘id‘,‘name‘,‘fk_id‘,‘fk__name‘)
				for item in b_list:
					item[0]
					item[1]
					item[2]
					item[3]

			e. 找A表名称等于"alex"的所有B表中的数据  		*****
				models.B.objects.filter(fk__name="alex")  good

				models.B.objects.filter(fk.name="alex")   这个是不可以,需要验证!!!!!!



			外键跨表关联补充:

			from django.db import models

			class Department(models.Model):
				"""
				部门表
				"""
				title = models.CharField(verbose_name="部门名称", max_length=16)

				"""
					id
					title
					userinfo_set
				"""


			class UserInfo(object):
				"""
				员工表
				"""
				username = models.CharField(verbose_name="用户名", max_length=16)
				password = models.CharField(verbose_name="密码", max_length=16)
				depart = models.ForeignKey(verbose_name="所属部门", to="Department")

				"""
				id
				username
				password
				depart_id
				"""

			正向关联:从 UserInfo 跨到 Department 查询相关数据
			    from crm import models
			    objs = models.UserInfo.objects.filter(depart__title=‘IT部‘)
			反向关联:从 Department 跨到 UserInfo 查询相关数据    
			    from crm import models
			    obj = models.Department.objects.filter(title=‘IT部‘).first()
			    print(obj.userinfo_set)


			"""
			# 获取model的字段,还包含FK和choice,但是没有多对多的字段
	        fields1 = [obj.name for obj in self.model_class._meta.fields]

	        # 只获取获取多对多的字段
	        fields2 = [obj.name for obj in self.model_class._meta.many_to_many]

	        # 这个是最全的,还包含了反向关联字段
	        fields3 = [obj.name for obj in self.model_class._meta._get_fields()]
			"""

			"""
			_field = self.model_config.model_class._meta.get_field(某个字段)
            
            多对多:
            option.field_or_func   course                    咨询的课程
            _field                 crm.Customer.course              type  <class ‘django.db.models.fields.related.ManyToManyField‘>
            _field.rel             <ManyToManyRel: crm.customer>    type  <class ‘django.db.models.fields.reverse_related.ManyToManyRel‘>
            
            多对一:
            option.field_or_func   consultant                课程顾问
            _field                 crm.Customer.consultant          type  <class ‘django.db.models.fields.related.ForeignKey‘>
            _field.rel             <ManyToOneRel: crm.customer>     type  <class ‘django.db.models.fields.reverse_related.ManyToOneRel‘>
           
			"""



		- M2M常用操作
			class A(Model):
				name = models.CharField(...)

			class B(Model):
				name = models.CharField(...)
				m = models.ManyToMany(A)

			a. B表中有几列?
				id
				name

			PS:ManyToMany 会自动生成第三张表,字段m用于间接的对第三张表进行操作


			b. 在B表中插入3条数据,A表中插入2条数据

				models.A.objects.create(name=‘alex1‘)
				models.A.objects.create(name=‘alex2‘)

				models.B.objects.create(name=‘egon1‘)
				models.B.objects.create(name=‘egon2‘)
				models.B.objects.create(name=‘egon3‘)

			c. 让 egon1 和 [alex1,alex2] 创建关系
				obj = models.B.objects.filter(‘name‘=‘egon1‘)
				obj.m.add(*[1,2])

			d. 查找和egon1有关系的人
				obj = models.B.objects.filter(‘name‘=‘egon1‘)
				obj.m.all()  # 拿到了queryset对象集合,都是A表的对象


	- Session是什么? 和cookie有什么区别?
		- session依赖cookie而存在
		  - session是存储在服务端的键值对
		  - cookie是存储在客户端浏览器里的键值对

		- 用户第一次来访问主页,服务端生成一个随机字符串通过cookie返回给客户端
		  同时服务端把这个字符串当做key存储在数据库表里(也可以存在其他地方),值可以为空

		- 用户第二次发起登录请求则带上刚才的cookie和用户信息
		  服务端则把用户信息(比如用户名密码)用某种方式存储在刚才随机字符串对应的值里
		  这样就表示用户登录过了





示例程序:pro_crm
	- 员工模块
		- 权限:根据 含有正则表达式的url 进行分配的

		- 操作:通过配置文件进行定制

	- 学生模块
		- 问卷



今日内容:
	- 第一版
		- permission权限表(含有正则表达式的url)
			id 			url                              title
			1           /userinfo/                       用户列表 
			2			/userinfo/(\\d+)/delete/          删除用户
			3 			/userinfo/(\\d+)/change/          修改用户
			4           /userinfo/add/                   添加用户 

		- userinfo用户表
			id     username   password    
			1      alex       123
			2      egon       123
			3      standby    123

		- 用户和权限的关系表
			id     user_id    permission_id
			1      1          1
			2      1          2
			3      2          1
			4      3          1
			5      3          2
			6      3          3


		问题:用户分配权限时,很麻烦,不方便后面的维护


	- 第二版
		- permission权限表(含有正则表达式的url)
			id 			url                              title
			1           /userinfo/                       用户列表 
			2			/userinfo/(\\d+)/delete/          删除用户
			3 			/userinfo/(\\d+)/change/          修改用户
			4           /userinfo/add/                   添加用户 

		- userinfo用户表
			id     username   password    
			1      alex       123
			2      egon       123
			3      standby    123

		- role 角色表
			id      title
			1		销售员
			2		销售经理
			3		市场专员
			4		市场经理
			5		IT 
			6		CTO 
			7		CEO

		- 用户和角色关系表
			id      user_id		role_id
			1		1			1
			2		2			1
			3		2			2
			4		3			1
			5		3			2
			6		3			7

		- 角色和权限的关系表
			id      role_id		permission_id
			1		1			1
			2		2			1
			3		2			4
			4		6			1
			5		6			3
			6		6			4
			7		7			1
			8		7			2
			9		7			3
			10		7			4	


		列出standby所具有的所有权限,可能会重复,所以需要去重;
		注意:根据用户找权限
			- 根据用户找角色
			- 根据角色找权限


		- 增加权限组表group
			id 			title
			1			用户组
			2			订单组


		- permission权限表 增加一个代号code字段、权限组字段以及 是否是菜单字段(涉及到在前端页面展示):
			- permission权限表(含有正则表达式的url)
			id 			url                           title 	     code     group_id    is_menu
			1           /userinfo/                    用户列表       list     1           true
			2			/userinfo/(\\d+)/delete/       删除用户       del      1           false
			3 			/userinfo/(\\d+)/change/       修改用户       edit     1           false
			4           /userinfo/add/                添加用户       add      1           true
			5           /order/                       订单列表       list     2           true
			6			/order/(\\d+)/delete/          删除订单       del      2           false
			7 			/order/(\\d+)/change/          修改订单       edit     2           false
			8           /order/add/                   添加订单       add      2           true



	- 中午作业
		- 填充数据:Django admin
		- 找到id=1的用户
			- 具有的角色
			- 具有的权限
			- 权限去重




	- 自定义配置
		Django的所有配置(既包含自定义的也包含Django内置的):
		from django.conf import settings
				

	- 找到id=1的用户
		- 具有的角色
		- 具有的权限
		- 权限去重

	- 自动生成权限菜单
		- 获取数据
		- 创建两级菜单

		@register.simple_tag()
		@register.inclusion_rag("menu_tpl.html")




总结:
	- 表的设计(很重要)
		4个类  6张表

	- Django admin 录入数据
	  
	- 用户登录处理
  		- 获取角色
  		- 获取权限
  		- 对权限去重
	  	- 然后把权限数据存储到session

	- 登录中间件
		- 白名单
		- request.path_info
		- 从session获取权限,进行验证

	- 自动生成菜单 
		- 跨表取出相关数据,转换字典结构
		- 视图中生成好字典返回给前端
		- 前端模板渲染

		- simple_tag
		- inclusion_rag
		- 模板layout.html



- 权限app包含哪些内容
	- 中间件
	- init_permission
	- models
	- admin 
	- templatetags

	依赖配置文件

		PERMISSION_DICT = "permission_dict"
		URL_FORMAT = "^{0}$"
		RBAC_LOGIN_URL = "/login/"
		VALID_URL_LIST = [
		    "^/login/$",
		    "^/admin.*",
		]


- 使用rbac权限管理
	- 创建CMDB项目
	- 把rbac拷贝到项目中
	- 在settings.APP中注册 rbac 
	- 在settings中配置相关变量:
		PERMISSION_DICT = "permission_dict"
		URL_FORMAT = "^{0}$"
		RBAC_LOGIN_URL = "/login/"
		VALID_URL_LIST = [
		    "^/login/$",
		    "^/admin.*",
		]
	- 开发CMDB
	- 开启中间件验证
	- 利用tmplatetags和模板动态生成菜单
	- 在Django admin中操作权限信息





day26作业
	- 权限系统补充完整,搞清楚每一行代码  [4天]
	- 自己创建project,比如主机管理系统
		- 主机
		- 主机组
		- 部门
		- 机房
		- ...
	- 使用rbac权限系统 [10天]



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

day27    
	- 自己写点东西
	- 计划:
		- 立项 (让项目来驱动复习知识点,把知识点串起来)
		- 业务
			- 数据库设计
			- 登录
				- cookies
				- session
				- Form组件验证
			- 列表
				- 数据ORM操作
			...





内容回顾:
	- 权限管理
		- 基于用户的权限管理
		- 基于角色的权限管理
	- 数据库(4个类6张表)
		- 用户
		- 角色
		- 权限
			- 含有正则表达式的url
			- code 用来控制是否在前端显示添加、删除、编辑这些 button
			- is_menu 用来控制一般含有正则的权限不能显示在菜单里
		- 权限菜单组
			- is_group 用来区分组和菜单

	- session

	- 中间件

	- inclusion_tag




今日内容:
	- 权限【修bug】
		- 删除和编辑时 左侧菜单无法默认展开(无法保持展开状态)
		- 在权限表增加一个字段:访问时,默认被选中的组内权限ID
	- 学习django admin的用法 自定义admin组件

	目标:整合 权限组件 + 自定义admin组件 + crm业务



- 权限【修bug】
- 控制页面是否显示 【增加】【编辑】【删除】 这些button





############# 自定义CURD组件 #################

- 学习 django-admin (解决基本的增删改查)
http://www.cnblogs.com/wupeiqi/articles/7444717.html




‘‘‘
from django.contrib import admin
from . import models
admin.site.register(models.UserInfo)
‘‘‘
- 注册一个类就生成了4个URL:
	- /admin/app01/userinfo/
	- /admin/app01/userinfo/add/
	- /admin/app01/userinfo/1/change/
	- /admin/app01/userinfo/1/delete/

结论:循环注册所有的类,每个类增加4个url


from django.shortcuts import HttpResponse
return HttpResponse("ModelAdmin...")



from django.contrib import admin
from django.contrib.admin import ModelAdmin
class UserInfoModelAdmin(ModelAdmin):
	list_display [‘name‘,‘pwd‘] # 定义显示列表页面显示哪些列
	list_display_links[‘pwd‘]   # 定义列表页面哪些列可以点击进入编辑页面
	list_filter = [‘ut‘]        # 右侧显示所有用户的用户类型,用于快速搜索
	list_per_page = 10          # 定义列表页面每页显示几条数据
	list_editable = [‘name‘]    # 字段编辑
	search_fields = [‘name‘]    # 模糊搜索框
	date_hierarcy
	save_on_top = True          # save按钮在上面显示


	def func(self,request,queryset):
		pass
	func.short_description = "批量初始化"
	action = [func,]            # 添加一个自定义的action

	raw_id_fields = [‘ut‘,]     # 让外键和多对多以input框的形式显示

	fields = [‘name‘]           # 定义显示哪些字段
	exclude = [‘name‘]          # 排除name

	readonly_fields = [‘name‘]  # 只读


	@admin.register(models.UserInfo)
	class UserAdmin(admin.ModelAdmin):
    	filter_vertical = ("m2m字段",) # 或filter_horizontal = ("m2m字段",)


    ordering = [‘id‘,]
    ordering = [‘-id‘,]


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


- 重要
	- admin原理
	- 自定义admin组件


- 启动文件执行顺序
	- app01/admin.py
	- project/urls.py

	- 什么admin.py先执行呢?
		from django.utils.module_loading import autodiscover_modules
		def autodiscover():
		    autodiscover_modules(‘admin‘, register_to=site)


		from django.contrib import admin
		class AdminConfig(SimpleAdminConfig):
		    """The default AppConfig for admin which does autodiscovery."""

		    def ready(self):
		        super(AdminConfig, self).ready()
		        self.module.autodiscover()


	- 制作启动文件
		- 创建APP
		- 找到app.py
			def ready(self):
				pass
		- settings.py注册
			‘arya.apps.AryaConfig‘

		程序启动会自动去所有app下找 arya文件并执行


	- 单例模式 (基于Python文件导入特性)
		多文件下要验证下


	- urls.py 里的 include 的本质 (路由分发的本质)
	- 自动生成 





- 自定义
	- 根据配置文件的修改,完成CURD定制任务







注意:
	在rbac中间件里可以通过如下方式把数据复制给request
	权限code   request.permission_code_list = xxx









本周任务:
	- 初级:自己手动写 CURD 

	- 权限管理修bug	【*****】

	- Arya补充完整	【*****】
		- 制作启动文件
		- 生成URL分发
		- 增加 list_display 来控制页面的显示
			- [‘name‘,‘pwd‘,func,...]
			- 继承
			- 多个class之间的兼容

	- 下周会用到:
		- yield
		- 面向对象的封装
		- __iter__ 的使用

	- 创建project,整合rabc和arya

	- Django Admin 












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

day28

内容回顾:
	- 路由系统
		- /index/    				def func1(request)
		- /index/(\\d+)/change    	def func2(request,arg)
		- /xxx/						include(‘app01.urls‘)
			/index/			(
								[yyy		def func3(request,xxxxxxxx)]
								none,
								none
							)


反向生成url
from django.urls import reverse

‘‘‘
url(r‘^/index/$‘, self.changelist_view, name="x1"),
url(r‘^/index/(\\d+)/edit$‘, self.change_view, name="x2"),
url(r‘^/index/(?P<uid>\\d+)/edit/$‘, self.change_view, name="x3"),


from django.urls import reverse
url = reverse(viewname=‘x1‘)    					-->   /index/
url = reverse(viewname=‘x2‘,args=(10,)    			-->   /index/10/edit/
url = reverse(viewname=‘x3‘,kwargs={‘uid‘:9})    	-->   /index/9/edit   


以上三中是基础的三种方式,另外如果url经过分发了:
- /index/		([
					xxx/, name=‘x4‘, def func4(...),
				],None,None)
这种情况下,反向生成:url = reverse(viewname=‘x4‘)


- /index/		([
					xxx/, name=‘x5‘, def func4(...),
				],None,‘some_namespace‘)

这种情况下,反向生成:url = reverse(viewname=‘some_namespace:x5‘)

‘‘‘



封装的思想


‘‘‘
class Foo(object):
    def func(self):
        print(‘方法‘)

方法和函数的区别:
    # - 如果被对象调用,则self不用传值
        # obj = Foo()
        # obj.func()

    # - 如果被类  调用,则self需要主动传值
        # obj = Foo()
        # Foo.func(obj)
‘‘‘




后端循环 + 前端循环 -->  后端使用yield




from django.forms import ModelForm
from django.forms import widgets 
ModelForm
	生成HTML
		默认样式
		定制样式
		定制错误信息 
	验证




ModelForm里面的钩子函数:def clean_字段名() ...
和Form的使用是一样的
需要自定义认证规则的时候可以使用钩子函数:
def clean_username():
	pass



search_list = []
search_list.extends(...)



model里的Q查询和F查询
多条件 或关系 查询:
from django.db.models import Q 
condition = Q()

可以自己定义模糊/精确查询

F查询和Q查询
‘username__contains‘ --> like模糊匹配
‘username‘ --> 完全匹配


批量删除操作:
self.model.objects.filter(id__in=item_list).delete()


外键:
‘school__title__contains‘


前端:xx|safe
后端:mark_safe


request = request.GET
QueryDict
QueryDict.urlencode()


data = [
	{‘value‘:‘multi_delete‘,‘text‘:"批量删除"}
	...
]
def func():
	pass
func.text = "批量删除"
func.__name__
func.text

批量删除
pk_list = request.POST.getlist(‘pk‘)   -->  能获取到checkbox里勾选的行的obj
self.model_class.object.filter(id__in=pk_list).delete()


ManyToMany 的字段需要写方法拼接:
teacher_list = []
for item in request.POST.get(‘teacher‘):
	teacher_list.append(item.name)

return ‘,‘.join(teacher_list)


models.Field
gender_chioces = [(1,‘男‘),(2,‘女‘)]
row.get_gender_display()   # 1  ->  男
即 obj.get_字段_display() 


- arya
	- 列定制
	- 关键字搜索框
	- 分页
	- action/批量操作 
	- 增加、删除、修改

- 基于CRM的业务实现






select   udp 
__iter__()

arya 被阉割的那部分视频+代码


数据结构 + 算法

  

 

以上是关于杂七杂八的主要内容,如果未能解决你的问题,请参考以下文章

GitHub管理代码-随笔

新博客路标

lombok踩坑与思考

PyPython基础——杂七杂八的用法

python杂七杂八知识点

杂七杂八的博客