Django的高级用法
一、视图(views)函数进阶
FBV和CBV:
FBV(function base view):FBV是Django中的一种路由映射方式;也是最长见的。从客户访问一个URL的时候,服务端接收到了URL进入urls.py进行解析,映射到一个views函数来处理这个请求。在FBV中在views函数中处理这段请求的是函数(也就是FBV中的function)
CBV(class base view):CBV是Django中另一种路由映射方式。与上面不同的是:FBV是映射到function中,而CBV映射的是class中。
配置方式:
urls.py配置:
views配置:
首先需要导入:from django.views import View
class Login(View): def dispatch(self, request, *args, **kwargs): #调用父类中的dispatch,通过dispatch调用get或者是post方法 result = super(Login,self).dispatch(request,*args,**kwargs) return result def get(self,request): print("get方式") return HttpResponse(".....False") def post(self,request): print("post方式") return HttpResponse("....True")
二、模板(Templates)进阶
在上次的模板语言中我们提了一次“过滤器”,过滤器的作用就是将从视图函数传入到模板中的变量进行二次加工。比如说视图传过来一个msg变量,这个时候我们可以用{{}}的方式来引用这个变量,而过滤器只需要在{{ msg|过滤器名称 }}就可以使用过滤器了。
自定义过滤器:
在Django中有内置的一写过滤器,但是这远远不能完成我们在日常Django中的操作,所以我们需要自己定义过滤器的功能。
定义过滤器的步骤:
- 首先需要在Django 的APP目录下创建一个名字只能为templatetags的文件夹
- 可以任意在此文件夹中创建一个xxxx.py的文件
- 在文件中需要导入模块 from django import template
- 在文件中创建一个名字只能是register的对象;register=template.Library()
- 这个时候可以在这个文件定义自己方法,但是需要给这个方法加上了一个名字为register.filter的装饰器
- 在模板中{% load xxxx %} 在模板中载入我们刚刚的文件,这里只需要写上文件名字即可
- 使用:使用的方法和Django内置的方法是一样的,这里需要了解向这一类的自定义过滤器只能传两个函数参数;第一个参数就是变量本身,第二位:冒号后面的字符串
from django import template register = template.Library() @register.filter def my_nb(arg1,value): a = arg1 + value return a
def my_tem(request): msg = "如果上天在给我一次机会" return render(request,'my_tem.html',{'msg':msg})
{% load my_tem %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>{{ msg|my_nb:"我一定会说我爱你" }}</h1> </body> </html>
最后的结果
自定义模板标签
通过上面的自定义过滤器我们知道了,原来视图函数中的数据还可以进一步被处理。但是我们过滤器也是有缺点的比如说:它之能传两个变量,当然你也可以在第二个变量中传一个有格式的字符串然后给他切边当做其他变量。
模板标签和过滤器非常相似,不同的地方在于它可以接收任意多的位置参数,使用方法和上面也完全一样只不过需要把定义的函数装饰器换成register.simple_tag,如果在模板中使用{%%}引用
from django import template from django.utils.safestring import mark_safe #可以将返回的字符串转换成HTML register = template.Library() @register.simple_tag def leon(arg1,arg2,arg3): return arg1 +arg2 + arg3
{% load my_tem %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>{{ msg|my_nb:"我一定会说我爱你" }}</h1> <h1>{% leon "曾经" "有一份真挚的爱情摆在我面前" "我没有珍惜" %}</h1> </body> </html>
结果
总结:
通过上面两种方式可以对HTML页面进行自定义的一些修改,但是filter还有一个simple_tag不具备的功能——可以当做条件语句。意思就是我们可以在模板中使用if判断来检查这个函数执行的结果是否为真,或者怎么样,然后进行下一步的操作。
模板继承
通常我们看到的一些网站上面都会有中文和英文两种页面,但是中英文的页面一般除了语言不通以外其他的样式都基本一样。难道是有人给这个网站做了两套不通的样式?如果是这样的话工作量是巨大的。又如果经理又让你修改下模板的样式,这两个页面你是都需要修改的。
模板继承就是为了解决类似上面这样:页面重复工作。
模板继承的使用方式。
1. 创建一个住模板(定义一个模板)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {# 类似下面这种{% block block_name %} {% endblock %} 就是命名了一个“空间”通俗的讲就是给子模板给一个写自己代码的地方,而非block的地方就是字模板公用的地方 #} <title>{% block title %}{% endblock %}</title> <link rel="stylesheet" href="/static/bootstrap-3/css/bootstrap.css" /> {% block mystyle %} {% endblock %} </head> <body> {% block body %}{% endblock %} </body> <script src="/static/jquery.js"></script> <script src="/static/bootstrap-3/js/bootstrap.js"></script> {% block myscript %}{% endblock %} </html>
2. 在子模板中引用主模板
{% extends 'master.html' %} {#extends为固定用法,意思就是导入 后面的主模板#} {% block title %} 中文国际 {% endblock %} {#引用了父模板中的title“空间”,子模板可以在这个空间写自己的功能#} {% block body %} {#引用了父模板中的body“空间”,子模板可以在这个空间写自己的功能#} <div class="progress" targer="one"> <div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <span class="sr-only">加载中</span> </div> </div> <div class="progress hidden" targer="two"> <div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 0.1%;"> 0% </div> </div> <div class="btn-group" role="group" aria-label="..."> <button type="button" class="btn btn-default" targer="edit">点击</button> </div> {% endblock %} {% block myscript %} {#引用了父模板中的myscript“空间”,子模板可以在这个空间写自己的功能#} <script> $("button[targer='edit']").click(function () { $(".progress[targer='one']").addClass('hidden'); $(".progress[targer='two']").removeClass('hidden'); }) </script> {% endblock %}
模板导入
页面导入就是可以在页面中导入一个公共的模板。
1. 创建一个工作的模板
<div> <a>1</a> <a>2</a> <a>3</a> <a>4</a> <a>5</a> <a>6</a> </div>
{% include 'includetem.html' %}
三、ORM中的操作
一、小示例(通过ORM对数据库进行连表操作)
#Django默认使用的数据库引擎是sqllite,如果想把数据看引擎替换成mysql 第一步需要在Django项目目录下找到__init__.py文件,打开文件,编辑 import pymysql pymysql.install_as_MySQLdb() 第二步需要修改settings.py文件,替换DATABASES为 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django_project', #数据库名称 'USER': 'leon', 'PASSWORD': '密码', 'HOST': '远程的ip地址', 'PORT': '3306', } }
通过上面的配置我们已经将Django的默认引擎设置成了mysql。下面就需要在moduls中将表建立出来
from django.db import models # Create your models here. class Books(models.Model): book_name = models.CharField(max_length=32) author = models.ManyToManyField('Author') price = models.IntegerField() pub_date = models.CharField(max_length=64) publiser = models.ForeignKey('Publiser') class Author(models.Model): name = models.CharField(max_length=32) email = models.EmailField(unique=True) class Publiser(models.Model): name = models.CharField(max_length=64) url = models.URLField(unique=True)
- 第一点在Django的ORM中models.ForeignKey就表示将次字段和其他表连接起来
- 第二点,Django的ORM中models.ManyToManyFiled就表示此字段和连接的表为多对多的关系;同时在数据中也会多出第三张表,除此之外Django设置多对多的关系就是完成了的
- 第三点,可以看出不管是ForeignKey还是ManyToManyField括号中都有一个字符串;其中字符串的内容表示关联的表,其中这两个方法中还有两个参数一个是to='需要关联到的表',另外一个就是to_field="需要关联到的字段”
- 第四点,字段方法中的一些参数,比如说models.CharField中有unique参数,如果为True的话,次字段在表中唯一;此外还有null和blank这都是表示该字段是否可以为空
通常如果是对数据库进行一些简单的调试我们是不会用到pycharm的,因为这样会很麻烦,这个时候我们就可以通过Django给我们提供的API接口来操作数据库了
首先我们需要切换到Django的项目 目录,然后“再次打开命令窗口”,输入Python manage.py shell 。这个时候我们就可以导入Django的一些文件了。
PS:在表ForeignKey字段的表中首先需要吧关联的表创建一些数据 models.Publiser.objects.create(name="HaFu",url="http://www.Hafu.com") models.Publiser.objects.create(name="BeiDaJingNiao",url="http://www.abtk.com") 这时候就可以创建有ForeignKey字段的表了。但是ManyToMany字段在数据创建的时候不可以添加字段 In [5]: models.Books.objects.create(book_name="python",price=99,pub_date="2018-02-03",publiser_id=1) Out[5]: <Books: Books object> In [6]: models.Books.objects.create(book_name="linux",price=99,pub_date="2015-02-03",publiser_id=1) Out[6]: <Books: Books object> In [7]: models.Books.objects.create(book_name="玄幻小说",price=99,pub_date="2016-02-23",publiser_id=2) Out[7]: <Books: Books object> 查看当前有多少书籍 In [8]: models.Books.objects.all() Out[8]: <QuerySet [<Books: Books object>, <Books: Books object>, <Books: Books object>]> 通过ManyToMany字段查看author表中的数据 b =models.Books.objects.all()[0] b.publiser.all() 此时为空 创建author表中的数据 In [12]: models.Author.objects.create(name="leon",email="123@126.com") Out[12]: <Author: Author object> In [13]: models.Author.objects.create(name="CHEN",email="456@126.com") Out[13]: <Author: Author object> 将author和books表进行关联(通过对象操作) In [16]: b1 = models.Books.objects.create(book_name="玄幻小说",price=99,pub_date="2016-02-23",publiser_id=2) In [18]: b1.author.add(1,2) In [19]: b1.save() 通过Books表查看author中的数据 models.Books.objects.all()[3].author.all()
ORM中Foreign Key操作
from app02 import models 正向Foreign Key操作 #返回结果是对象 b = models.Books.objects.all() for row in b: print(row.name,row.price,row,pub_date,row.publisher.name,publisher.weburl) #返回结果是字典 b = b = models.Books.objects.values("name","publisher__name") for row in b: print(row["name",row[publisher__name]]) #返回的是元祖 b = b = models.Books.objects.value_list("name","publisher__name") for row in b: print(row["name",row[publisher__name]])
#Foreign Key 反向操作 v = models.Publisher.objects.all() #queryset = [obj,obj,obj] for row in v: print(row.id,row.name,row.books_set.all()) 2. 字典 v = models.Publisher.objects.values('id','name','books__name','books__price') for row in v: print(row) 3. 元组 v = models.Publisher.objects.values_list('id','name','books__name','books__price') for row in v: print(row)
ORM中的ManyToMany操作
我们都知道对于ManyToMany字段,Django采用的是第三张中间表的方式。通过这第三张表,来关联ManyToMany的双方。下面我们根据一个具体的例子,详细解说中间表的使用。
默认中间表(创建表结构)
class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person) def __str__(self): return self.name
在Group模型中,通过members字段,以ManyToMany方式与Person模型建立了关系。
让我们到数据库内看一下实际的内容,Django为我们创建了三张数据表,其中的app1是应用名。
然后我在数据库中添加了下面的Person对象:
再添加下面的Group对象:
让我们来看看,中间表是个什么样子的:
首先有一列id,这是Django默认添加的,没什么好说的。然后是Group和Person的id列,这是默认情况下,Django关联两张表的方式。如果你要设置关联的列,可以使用to_field参数。
可见在中间表中,并不是将两张表的数据都保存在一起,而是通过id的关联进行映射。
自定义中间表
一般情况,普通的多对多已经够用,无需自己创建第三张关系表。但是某些情况可能更复杂一点,比如如果你想保存某个人加入某个分组的时间呢?想保存进组的原因呢,我们可以自己定义第三张表,并将其他两张表用外键关联上。
from django.db import models class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Membership(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) group = models.ForeignKey(Group, on_delete=models.CASCADE) date_joined = models.DateField() # 进组时间 invite_reason = models.CharField(max_length=64) # 邀请原因
在中间表中,我们至少要编写两个外键字段,分别指向关联的两个模型。在本例中就是‘Person’和‘group’。 这里,我们额外增加了‘date_joined’字段,用于保存人员进组的时间,‘invite_reason’字段用于保存邀请进组的原因。
关于ORM中更多查询内容的方法
还是以上面创建好的ORM对象为例子
#查看书籍(Books表中)id小于5的内容,其中地为Books表中id地段。一下所有获得到的都是queryset对象 models.Books.objects.filter(id__le=5)
#查看书籍(Books表中)包含Linux字样的数据,中那么为Books表中的字段名称
models.Books.object.filter(books_name__contains="Linux")
#查看当前作者(author表中)名字叫做Leon或者leon的(意思就是忽略大小写),其中name为表中的字段
models.Author.object.filter(name__icontains="leon")
#查看当前书籍中(Books表中),以L或者l开头的书籍(startwith是不忽略大小写的)
models.Books.object.filter(books_name__istartwith="l")
#查看书籍中某个时间段发布的书籍
models.Books.object.filter(pub_date__range("2012-01-01","2018-01-01"))
#查看所有数据的名字(books_name)和价格(price)列的数据
models.Books.object.values('books_name','price')
ORM中数据的聚合
在MySQL原生语句中我们经常会设计到计算,比如说计算出一个班级里面所有的学生,在ORM中我们也有对应的计算数据的方法
#首先我们需要导入模块 from django.db.models import Avg
#还是以上面的书籍关系为例,我们计算出平均的书籍价格
models.Books.objects.all().aggregate(Avg('price'))
from django.db.models import Sum
#计算出所有书籍的价格
models.Books.objects.all().aggregate(Sum('price')
ORM中支持的数据类型:
字符串,数字,时间,二进制
ORM中字段支持的参数
null --> db是否可以为空 default -->默认值 primary_key -->主键 db_colum -->列名,生成的表总列表为column的值 db_index -->索引 unique -->唯一索引,如果是True就表示该字段不可以在一张表中重复 unique_for_date -->支队时间做索引 unique_for_month auto_now -->更新时自动生成 auto_now_add -->创建时自动生成 choices -->给表中的其他列做选择,提过效率 user_type_choices = ( (1,'安全用户'), (2,'超级用户'), (3,'标准用户), ) user_type = models.IntegerFiled(choices=user_type_choices,default=1) blank -->Django admin中是否可以为空 verbose_name -->在Django admin将字段显示为verbose_name值的中文名称 editable -->Django admin是否可以被编辑 error_messages -->Django admin中自定制字段的错误信息 help_text -->Django admin 的提示 validators -->Django form,自定义错误信息
拿到过滤后的第一个结果
Obj = models.modle_name.objects.filter(条件语句).first()
拿到返回的个数
models.model_name.objects.filter(条件语句).count()
Django form表单
使用django form的原因:
在我们写的项目中一般都是会涉及到表单提交的。而后台通常都会对提交过来的表单进行一些验证。首先不谈我们自己写验证对服务器性能的开销有多大,大多时候写到一般的时候就会偷懒,将功能写的简单化。
Django form就是一种解决Django表单提交验证的一种方案。通过我们后端定义字段类型来验证前台提交过去的数据。从使用角度出发也是极其简单的。我们不必纠结于它是如何实现功能的。只需要想读书明书一样,知晓其功能使用方式即可。
基本使用:
1. from是Django中自带的一个包,如果要使用,就必须要从Django中导入:
from django import forms
from django.forms import fields
2. 定义一个forms表单验证类,名称可以随便写
class HostInfo(forms.Form): hostip = fields.GenericIPAddressField(required=True,error_messages={'required':"主机地址不能为空",'invalid':"提交的必须为IPV4或者IPV6地址"}) hosttype = fields.CharField(required=True,error_messages={'required':"主机类型不能为空"}) hostuser = fields.CharField(required=True,error_messages={'required':"主机用户不能为空"}) password = fields.CharField(required=True,error_messages={'required':"主机密码不能为空"})
-
required=True,为次字段为必填项
-
error_messages:自定义错误提示,其中required为字段没写提示错误的错误信息,invalid为格式错误提示的错误信息
- 在编写字段名称的时候需要注意需要和数据库字段以及前段input中name属性字段一致,这样forms中的字段才可以进行验证
3. 编写我们的验证逻辑代码
def add_host(request): if request.method == "GET": return render(request,'add_hosts.html') else: obj = HostInfo(request.POST) if obj.is_valid(): print("验证通过",obj.cleaned_data) models.HostInfo.objects.create(**obj.cleaned_data) else: print("错误",obj.errors["hostip"]) return render(request,'add_hosts.html',{'obj':obj})
在上面的代码中如果请求方式为GET 就返回一个页面;如果是POST方式我们就去验证这段信息,如果验证通过了,想获取数据,就通过obj.cleand_data方式,如果验证没有通过想要获取到错误的信息我们可以使用obj.errors来获取到所有的错误信息。如果想要获取某一个字段的错误信息,可以使用obj.errors.filed_name来获取该字段的错误信息。
4. 编写前段代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"/> </head> <body> <form action="{% url 'AddHost' %}" method="POST" style="width: 700px;margin: 0 auto;"> {% csrf_token %} <div class="form-group"> <label for="exampleInputEmail1">hostip</label> <input type="text" name="hostip" class="form-control" placeholder="ip地址"> <span>{{ obj.errors.hostip.0 }}</span> </div> <div class="form-group"> <label for="exampleInputPassword1">hosttype</label> <input type="text" name="hosttype" class="form-control" placeholder="主机类型"> <span>{{ obj.errors.hosttype.0 }}</span> </div> <div class="form-group"> <label for="exampleInputPassword1">hostuser</label> <input type="text" name="hostuser" class="form-control" placeholder="用户名"> <span>{{ obj.errors.hostuser.0 }}</span> </div> <div class="form-group"> <label for="exampleInputPassword1">password</label> <input type="text" name="password" class="form-control" placeholder="密码"> <span>{{ obj.errors.password.0 }}</span> </div> <button type="submit" class="btn btn-default">提交</button> </form> </body> <script src="/static/jquery.min.js"></script> <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.js"></script> </html>
上述代码中获取单个字段的错误信息,使用的obj.errors.字段名称的方式。如果我们需要获取更加详细的信息,可以使用.0的方式来获取第一个该字段第一个错误的信息
提交错误信息后保留信息的方法
方式一:
1. 第一种方式,我们在获取这个表单的时候需要在views函数中生成一个HostInfo的对象,但是这个里面不需要填写任何值,进行验证。
def add_host(request): if request.method == "GET": obj = HostInfo() return render(request,'add_hosts.html',{'obj':obj})
2. 需要在前端渲染表单。一般我们需要渲染单个input标签的时候只需要用HostInfo.字段名称,就可以渲染出表单,这个时候表单里面没有任何数据,因为我们生成的对象中没有添加任何值进去
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap-3/css/bootstrap.css"/> </head> <body> <form action="/app02/add_host" method="POST" style="width: 700px;margin: 0 auto;" novalidate> {% csrf_token %} <div class="form-group"> <label for="exampleInputEmail1">hostip</label> {# <input type="text" name="host_ip" class="form-control" placeholder="ip地址" value="{{ obj.changed_data.host_ip }}" /> 注释表单 #} {# 渲染出我们需要的input标签#} {{ obj.host_ip }} <span>{{ obj.errors.host_ip.0 }}</span> </div> <div class="form-group"> <label for="exampleInputPassword1">hosttype</label> {# <input type="text" name="host_type" class="form-control" placeholder="主机类型" value="{{ obj.changed_data.host_type }}" />#} {{ obj.host_type }} <span>{{ obj.errors.host_type.0 }}</span> </div> <div class="form-group"> <label for="exampleInputPassword1">hostuser</label> {# <input type="text" name="host_user" class="form-control" placeholder="用户名" value="{{ obj.changed_data.host_user }}" />#} {{ obj.host_user }} <span>{{ obj.errors.host_user.0 }}</span> </div> <div class="form-group"> <label for="exampleInputPassword1">password</label> {# <input type="text" name="host_password" class="form-control" placeholder="密码" value="{{ obj.changed_data.host_password }}" />#} {{ obj.host_password }} <span>{{ obj.errors.host_password.0 }}</span> </div> <button type="submit" class="btn btn-default">提交</button> </form> </body> <script src="/static/jquery.js"></script> <script src="/static/bootstrap-3/js/bootstrap.js"></script> </html>
3. 对表单进行测试。通过以上两步,我们就可以对提交错误后信息进行保留了;通过前端渲染我们得出的input标签中name属性的值和后端HostInfo类中字段名称一致,这也就表示,我们不需要担心前端提交过来的值我们无法验证了。
4. 通过上面的步骤,表单验证是没有问题了,但是样式过于丑陋,如果我们想给标签体检样式,需要在HostInfo中定义字段时添加一些属性,通过添加一些bootstrap的样式将表单处理。
导入一个库 from django.forms import widgets
class HostInfo(forms.Form):
host_ip = fields.GenericIPAddressField(required=True,
error_messages={'required':"主机地址不能为空",'invalid':"提交的必须为IPV4或者IPV6地址"},
widget=widgets.TextInput(attrs={'class':'form-control','placeholder':'IPV4'})
)
host_type = fields.CharField(required=True,
error_messages={'required':"主机类型不能为空"},
widget=widgets.TextInput(attrs={'class': 'form-control','placeholder':'主机类型'})
)
host_user = fields.CharField(required=True,
error_messages={'required':"主机用户不能为空"},
widget=widgets.TextInput(attrs={'class': 'form-control','placeholder':'主机用户'})
)
host_password = fields.CharField(required=True,
error_messages={'required':"主机密码不能为空"},
widget=widgets.TextInput(attrs={'class': 'form-control','placeholder':'主机密码'})
)
forms表单设置默认值:
def edit_host(request,uid): if request.method == "GET": host = models.HostList.objects.filter(id=uid).first() #通过Form中initial功能给表单设置默认值,initial中的值不会提交验证 obj = HostInfo(initial={'host_ip':host.host_ip,'host_type':host.host_type,'host_user':host.host_user,'host_password':host.host_password}) return render(request,'edit_host.html',{'obj':obj})
关于ORM跨表查询性能相关
方式一:
host_obj = models.HostList.objects.all() #后端配置
retrun render(requests,'hosts.html',{'host_obj':host_obj})
{% for row in host_obj %} #前段配置
{{ row.host_ip }} #未涉及到跨表操作
{{ row.host_type }} #未涉及到跨表操作
{{ row.host_user }} #未涉及到跨表操作
{{ row.host_password }} #未涉及到跨表操作
{{ row.admin.name }} #涉及跨表操作
{% endfor %}
如果我们假设有两张表其中第一张表有一个'ha'字段关联到admin表中;我们在前段渲染的时候将admin表中name属性查询 出来就必须涉及到跨表(具体ForeignKey操作在本博客上有详细介绍);但是我们在前端通过循环才查询到的name属性,也就代表着我循环几次就需要在后台进行多少次SQL查询,这样非常消耗服务器性能。我们的解决方案就是在后端返回host_obj的时候修改此对象的获取方式。具体如下
host_obj = models.HostLisst.objects.all().select_related('ha')
1. select_related主动跨表查询
2. select_related其原理就是使用SQL语句中的left join将ForeignKey表中的数据一次取回来,这样就减少的SQL查询的次数
forms跨表相关
forms表中ForeinKey字段设置
ha_id = fields.IntegerFiled(
required=True,
widget=widgets.Select(attrs{'class':'form-control'},choices=[(1,'普通用户')(2,'超级用户')])
)
cheng xu diyi ci zhixing de shihou values_list zhi zhixing yici
ha_id = fields.IntegerFiled(
required=True,
widget=widgets.Select(attrs{'class':'form-control'},choices=models.Admin.objects.values_list('id','name'))
)
def __init__(self,*args,**kwargs):
super(HostList,self).__init__(*args,**kwargs):
self.fields['ha_id'].widget.choices = models.HostList.objects.values_list('id','name')
Django 中的中间件
我之前花过客户端请求是怎么提交到django中处理的,然后如何返回。首先客户端的通过URLconf映射到一个视图函数中,通过视图的处理,如果有数据库交互操作就和数据库进行交互然后返回结果(如果是字符串直接返回,如果是涉及到模板就与和模板进行交互)。但是当我们请求在到视图函数中间还有一个步骤,那就是首先需要将我们的请求的数据交给“中间件”处理。下图中一堆为中间件
中间件定义
中间件是Django请求/响应处理的钩子框架。这是一个轻量级的低级“插件”系统,用于在全球范围内改变Django的输入或输出。
每个中间件组件都负责完成一些特定的功能。例如,Django包含一个中间件组件 AuthenticationMiddleware,它使用会话将用户与请求相关联。
什么时候使用 middleware
如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。
可能你还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。
中间件的生命周期
在客户端提交一个请求经过中间件的时候是从上往下经过的,首先会从上到下执行每一个中间件的process_request方法,等到达views函数的时候views函数会有一个return也就是返回值,的时候会将请求按照原路从下往上经过中间件。这个时候每一个中间件都会返回一个return一个值,依次往上,到达会前面的一个中间件的时候会将结果返回给用用户
自定义中间件
- 在项目目录下面创建一个任意文件夹
- 在该文件夹下面创建任意名称py文件
- 编写py文件
from django.utils.deprecation import MiddlewareMixin class M1(MiddlewareMixin): """ 中间件必须继承MiddlewareMixin,这是固定格式 """ def process_request(self,request): #process_request函数名是固定格式,request也是必须参数 print("M1 process_request") def process_response(self, request, response): # process_response函数名是固定格式,request,response也是必须参数 print("M1 process_response") return response class M2(MiddlewareMixin): """ 中间件必须继承MiddlewareMixin,这是固定格式 """ def process_request(self, request): # process_request函数名是固定格式,request也是必须参数 print("M2 process_request") def process_response(self, request, response): # process_response函数名是固定格式,request,response也是必须参数 print("M2 process_response") return response
4. 修改settings.py中MIDDLEWARE,添加我们创建的中间件到此列表中
5. 访问任意页面查看效果
6.在process_request函数中添加返回值,查看效果
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import render,HttpResponse class M1(MiddlewareMixin): """ 中间件必须继承MiddlewareMixin,这是固定格式 """ def process_request(self,request): #process_request函数名是固定格式,request也是必须参数 print("M1 process_request") return HttpResponse("直接从M1中返回") def process_response(self, request, response): # process_response函数名是固定格式,request,response也是必须参数 print("M1 process_response") return response class M2(MiddlewareMixin): """ 中间件必须继承MiddlewareMixin,这是固定格式 """ def process_request(self, request): # process_request函数名是固定格式,request也是必须参数 print("M2 process_request") def process_response(self, request, response): # process_response函数名是固定格式,request,response也是必须参数 print("M2 process_response") return response
PS:通过上面的中M1中不难看出,只要我们在类中把process_request函数添加一个返回值,django就会将这个函数返回给上一个中间件,连视图函数都不经过。
中间件中的其他方法
Django 按顺序执行中间件 process_view()
的方法,从上到下。 类似process_request()方法执行的顺序。
所以如果任何一个 process_view()
返回了HttpResponse对象,那么在它后面process_view()
将会被省略,不会被执行
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import render,HttpResponse class M1(MiddlewareMixin): """ 中间件必须继承MiddlewareMixin,这是固定格式 """ def process_request(self,request): #process_request函数名是固定格式,request也是必须参数 print("M1 process_request") def process_view(self,request,callback,callback_args,callback_kwargs): print("M1 process_view ") def process_response(self, request, response): # process_response函数名是固定格式,request,response也是必须参数 print("M1 process_response") return response class M2(MiddlewareMixin): """ 中间件必须继承MiddlewareMixin,这是固定格式 """ def process_request(self, request): # process_request函数名是固定格式,request也是必须参数 print("M2 process_request") def process_view(self,request,callback,callback_args,callback_kwargs): print("M2 process_view ") def process_response(self, request, response): # process_response函数名是固定格式,request,response也是必须参数 print("M2 process_response") return response
def process_exception方法:在程序出错的时候被执行
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import render,HttpResponse class M1(MiddlewareMixin): """ 中间件必须继承MiddlewareMixin,这是固定格式 """ def process_request(self,request): #process_request函数名是固定格式,request也是必须参数 print("M1 process_request") def process_view(self,request,callback,callback_args,callback_kwargs): print("M1 process_view ") def process_response(self, request, response): # process_response函数名是固定格式,request,response也是必须参数 print("M1 process_response") return response def process_exception(self,request,exception): print("M1 process_exception") class M2(MiddlewareMixin): """ 中间件必须继承MiddlewareMixin,这是固定格式 """ def process_request(self, request): # process_request函数名是固定格式,request也是必须参数 print("M2 process_request") def process_view(self,request,callback,callback_args,callback_kwargs): print("M2 process_view ") def process_response(self, request, response): # process_response函数名是固定格式,request,response也是必须参数 print("M2 process_response") return response def process_exception(self,request,exception): print("M1 process_exception")
Django 中的cookie和session
一、Cookie
cookie就是用户浏览器上面的键值对,当浏览器访问服务端的时候服务器会将用户名以key value的形式存储到浏览器上。
NB的分页效果
基于Django内置的分页方法:
def hosts(request): """ paginator对象中锁含有方法 pre_page : 每页显示条目数量 count:数据总个数 num_pages:总页数 page_range:总页数的索引范围 host_list:获取到数据库中host对象 current_page:在GET方式中获取当当前的页码 paginator:显示的数据 :param request: :return: """ host_list = models.HostInfo.objects.all() #数据类型是对象 current_page = request.GET.get('p') #获取到当前页 paginator = Paginator(host_list,10) #将对象传给paginator,10位每页显示多少条数据 try: page_obj = paginator.page(current_page) #在painator中将当前页传入,就可以定位到当前页 except EmptyPage as e: page_obj = paginator.page(1) except PageNotAnInteger as e: page_obj = paginator.page(1) """ has_next : 是否有下一页 next_page_number:下一页的页码 has_previous:是否有上一页 previous_page_number:上一页的页码 object_list:分页之后得数据列表 number:当前页 """ return render(request,'host_list.html',{"page_obj":page_obj})
基于Python语言适用于任何Python分页效果
class PageInfo(object): def __init__(self,current_page,per_page_num,all_count,base_url,page_range=7): try: current_page = int(current_page) except Exception as e: current_page = int(1) self.current_page = current_page self.per_page_num = per_page_num self.all_count = all_count a,b = divmod(all_count,per_page_num) if b != 0: #self.all_page 是总页数 self.all_page = a + 1 else: self.all_count = a self.base_url = base_url self.page_range = page_range def start(self): return (self.current_page - 1) * self.per_page_num def end(self): return self.current_page * self.per_page_num def page_str(self): """ 在HTML页面中显示页码信息 :return: """ page_list = [] if self.current_page <= 1: prev = '<li><a href="#">上一页</a></li>' else: prev = '<li><a href="%s?p=%s">上一页</a></li>'%(self.base_url, self.current_page-1) page_list.append(prev) if self.all_page <= self.page_range: start = 1 end = self.all_page + 1 else: if self.current_page > int(self.page_range / 2): if self.current_page + int(self.page_range / 2) > self.all_page: start = self.all_page - self.page_range + 1 end = self.all_page + 1 else: start = self.current_page - int(self.page_range / 2) end = self.current_page + int(self.page_range / 2) + 1 else: start = 1 end = self.page_range for i in range(start,end): tmp = '<li><a href="%s?p=%s">%s</a></li>'%(self.base_url,i,i) page_list.append(tmp) if self.current_page >= self.all_page: nex = '<li><a href="#">下一页</a></li>' else: nex = '<li><a href="%s?p=%s">下一页</a></li>'%(self.base_url, self.current_page+1) page_list.append(nex) # tmp = """ # <a href='/app02/page1?p=%s'>上一页</a> # <a href='/app02/page1?p=%s'>下一页</a> # """%(self.current_page-1,self.current_page+1) return " ".join(page_list)