标签:mysql数据库 还需 cfile 允许 cep 开发人员 增加 语法 obj
框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构。
使用框架可以帮你快速开发特定的系统。
简单地说,就是你用别人搭建好的舞台来做表演。
尝试搭建一个简单的web框架:
因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。
这个接口就是WSGI:Web Server Gateway Interface。
#---------------------myweb.py------------------------ from wsgiref.simple_server import make_server def foo1(request): f=open("alex.html","rb") data=f.read() f.close() return [data] def foo2(request): f=open("egon.html","rb") data=f.read() f.close() return [data] def reg(request): f=open("register.html","rb") data=f.read() f.close() return [data] def login(request): f=open("login.html","rb") data=f.read() f.close() return [data] def auth(request): print("++++++",request) user_union,pwd_union=request.get("QUERY_STRING").split("&") _,user=user_union.split("=") _,pwd=pwd_union.split("=") if user=="yuan" and pwd=="123": return [b"login successful"] else: return [b"user or password exists errors"] def routers(): URLpattern=( ("/login",login), ("/auth",auth), ("/alex",foo1), ("/egon",foo2), ("reg",reg) ) return URLpattern def application(environ,start_response): print("environ",environ) path=environ.get("PATH_INFO") print("path",path) start_response("200 ok",[("content-type","text/html")]) urlpattern=routers() func=None for item in urlpattern: if path==item[0]: func=item[1] break if func: return func(environ) else: return [b"404"] t=make_server("",8880,application) t.serve_forever() # 开始监听t请求:
#------------------------login.html-------------------------- <body> <h1>登录页面</h1> <form action="http://127.0.0.1:8880/auth"> <p>姓名<input type="text" name="user"></p> <p>密码<input type="password" name="pwd"></p> <input type="submit" value="提交"> </form> </body>
#---------------------egon.html-------------------------------- <body> <h1>welcome to egon‘s home</h1> </body> #----------------------alex.html---------------------------------- <body> <h1>welcome to alex‘s home</h1> </body>
注意:
application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。
Python内置了一个WSGI服务器,这个模块叫wsgiref
application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:
//environ:一个包含所有HTTP请求信息的dict对象;
//start_response:一个发送HTTP响应的函数。
在application()函数中,调用:
start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)])
就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。
start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每
个Header用一个包含两个str的tuple表示。
通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。
然后,函数的返回值b‘<h1>Hello, web!</h1>‘将作为HTTP响应的Body发送给浏览器。
有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,
通过start_response()发送Header,最后返回Body。
此外,Django还有一个url分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
下面进入正式部分。
主要知识点框架罗列:
路由配置系统(URLconf)
pip install django
(双等号可以指定安装版本,比如个人安装1.11.4版本,就可以使用:pip install django==
1.11.4)
说明:使用pip install django命令进行安装时,会自动删除旧版本,再安装新版本
进入python shell,运行如下代码:
import django django.get_version()
django-admin startproject project_name
项目结构:
例如创建一个名为test1的项目,结构如下:
说明:
项目与应用关系:
一个项目有多个应用
一个应用可以被多个项目拥有
命令:
python manage.py startapp blog
python manage.py runserver IP PORT
这样我们的django就启动起来了!当我们访问:http://127.0.0.1:8080/时就可以看到:
注意:Django 1.7.1 及以上的版本需要用以下命令 python manage.py makemigrations python manage.py migrate
python manage.py flush 此命令会询问是 yes 还是 no, 选择 yes 会把数据全部清空掉,只留下空表。
python manage.py shell
python manage.py dbshell
python manage.py
查看所有的命令,忘记子名称的时候特别有用。
在settings.py文件中,通过DATABASES项进行数据库设置。
Django默认使用SQLite数据库,同时支持MySQL等主流数据库。
<1> sqlite django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3 在settings里有如下设置:
<2> mysql 引擎名称:django.db.backends.mysql
mysql驱动程序 MySQLdb(mysql python) mysqlclient MySQL PyMySQL(纯python的mysql驱动程序)
如果我们想要更改为MySQL数据库,需要修改如下:
DATABASES = { ‘default‘: { ‘ENGINE‘: ‘django.db.backends.mysql‘, ‘NAME‘: ‘books‘, #你的数据库名称 ‘USER‘: ‘root‘, #你的数据库用户名 ‘PASSWORD‘: ‘‘, #你的数据库密码 ‘HOST‘: ‘‘, #你的数据库主机,留空默认为localhost ‘PORT‘: ‘3306‘, #你的数据库端口 } }
NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建 USER和PASSWORD分别是数据库的用户名和密码。 设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。 然后,启动项目,会报错:no module named MySQLdb 这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL 所以,我们只需要找到项目名文件下的__init__,在里面写入: import pymysql pymysql.install_as_MySQLdb() 问题解决!
settings文件中static的配置如下:
STATIC文件还可以配置STATICFILES_DIRS,指定额外的静态文件存储位置。
#注意1: #为了后端的更改不会影响前端的引入,避免造成前端大量修改 STATIC_URL = ‘/static/‘ #引用名 STATICFILES_DIRS = ( os.path.join(BASE_DIR,"statics") #实际名 ,即实际文件夹的名字 ) #django对引用名和实际名进行映射,引用时,只能按照引用名来,不能按实际名去找
例如:我们写一个模板文件,一般会需要引入JS文件
我们常写的格式为:
#<script src="/statics/jquery-3.2.1.js"></script>
#--------------错误--------------------------
正确引用方式:
必须用STATIC_URL = ‘/static/‘:
#<script src="/static/jquery-3.2.1.js"></script>
#注意2(statics文件夹写在不同的app下,静态文件的调用): STATIC_URL = ‘/static/‘ STATICFILES_DIRS=( (‘hello‘,os.path.join(BASE_DIR,"app01","statics")) , ) #<script src="/static/hello/jquery-3.2.1.js"></script>
#注意3: STATIC_URL = ‘/static/‘ {% load staticfiles %} # <script src={% static "jquery-3.2.1.js" %}></script>
应用场景:对于每次创建一个对象,想显示对应的raw sql,需要在settings加上日志记录部分:
LOGGING = { ‘version‘: 1, ‘disable_existing_loggers‘: False, ‘handlers‘: { ‘console‘:{ ‘level‘:‘DEBUG‘, ‘class‘:‘logging.StreamHandler‘, }, }, ‘loggers‘: { ‘django.db.backends‘: { ‘handlers‘: [‘console‘], ‘propagate‘: True, ‘level‘:‘DEBUG‘, }, } }
例:
ROOT_URLCONF = ‘BlogSM.urls‘ (创建的项目名称为:BlogSM)
urlpatterns = [
url(r‘^admin/‘, admin.site.urls),
url(r‘^add/‘,views.add),
url(r‘^$‘,views.add), #(此条一般用作增加用户体验,比如首页展示特定内容)
]
http://www.itcast.cn/python/1/?i=1&p=new,只匹配“/python/1/”部分
urlpatterns = [ url(r‘^articles/2003/$‘, views.special_case_2003), url(r‘^articles/([0-9]{4})/$‘, views.year_archive), url(r‘^articles/([0-9]{4})/([0-9]{2})/$‘, views.month_archive), url(r‘^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$‘, views.article_detail), ]
一些请求的例子: /articles/2005/3/ 不匹配任何URL 模式,因为列表中的第三个模式要求月份应该是两个数字。 /articles/2003/ 将匹配列表中的第一个模式不是第二个,因为模式按顺序匹配,第一个会首先测试是否匹配。 /articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数 views.month_archive(request, ‘2005‘, ‘03‘)。
正则表达式非命名组(通过圆括号),通过位置参数传递给视图
正则表达式命名组,通过关键字参数传递给视图
语法:(?P<name>pattern)
,其中name
是组的名称,pattern
是要匹配的模式。
例如:
urlpatterns = [
url(r‘^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$‘, views.month_archive),
url(r‘^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$‘, views.article_detail),
]
]
/articles/2005/03/ 请求将调用views.month_archive(request, year=‘2005‘, month=‘03‘)函数 /articles/2003/03/03/ 请求将调用函数views.article_detail(request, year=‘2003‘, month=‘03‘, day=‘03‘)。
URLconf 在请求的URL 上查找,将它当做一个普通的Python 字符串。不包括GET和POST参数以及域名。 例如,http://www.example.com/myapp/ 请求中,URLconf 将查找myapp/。 在http://www.example.com/myapp/?page=3 请求中,URLconf 仍将查找myapp/。 URLconf 不检查请求的方法。换句话讲,所有的请求方法 —— 同一个URL的POST、GET、HEAD等等 —— 都将路由到相同的函数。
每个捕获的参数都作为一个普通的Python 字符串传递给视图,无论正则表达式使用的是什么匹配方式。例如,下面这行URLconf 中: url(r‘^articles/(?P<year>[0-9]{4})/$‘, views.year_archive), views.year_archive() 的year 参数将是一个字符串
有一个方便的小技巧是指定视图参数的默认值。 下面是一个URLconf 和视图的示例:
# URLconf from django.conf.urls import url from . import views urlpatterns = [ url(r‘^blog/$‘, views.page), url(r‘^blog/page(?P<num>[0-9]+)/$‘, views.page), ] # View (in blog/views.py) def page(request, num="1"): ...
在上面的例子中,两个URL模式指向同一个视图views.page
—— 但是第一个模式不会从URL 中捕获任何值。如果第一个模式匹配,page()
函数将使用num
参数的默认值"1"。如果第二个模式匹配,page()
将使用正则表达式捕获的num
值。
from django.conf.urls import include, url urlpatterns = [ url(r‘^admin/‘, admin.site.urls), url(r‘^blog/‘, include(‘blog.urls‘)), ]
匹配过程:先与主URLconf匹配,成功后再用剩余的部分与应用中的URLconf匹配
请求http://www.itcast.cn/booktest/1/ 在sesstings.py中的配置: url(r‘^booktest/‘, include(‘booktest.urls‘, namespace=‘booktest‘)), 在booktest应用urls.py中的配置 url(r‘^([0-9]+)/$‘, views.detail, name=‘detail‘), 匹配部分是:/booktest/1/ 匹配过程:在settings.py中与“booktest/”成功,再用“1/”与booktest应用的urls匹配
‘‘‘ urlpatterns = [ url(r‘^index‘,views.index,name=‘INDEX‘), ] ################### def index(req): if req.method==‘POST‘: username=req.POST.get(‘username‘) password=req.POST.get(‘password‘) if username==‘alex‘ and password==‘123‘: return HttpResponse("登陆成功") return render(req,‘index.html‘) ##################### <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {# <form action="/index/" method="post">#} <form action="{% url ‘INDEX‘ %}" method="post"> 用户名:<input type="text" name="username"> 密码:<input type="password" name="password"> <input type="submit" value="submit"> </form> </body> </html> ####################### ‘‘‘
Django原生自带几个默认视图用于处理HTTP错误
<!DOCTYPE html> <html> <head> <title></title> </head> <body> 找不到了 <hr/> {{request_path}} </body> </html>
在settings.py中修改调试
DEBUG = False ALLOWED_HOSTS = [‘*‘, ]
请求一个不存在的地址
http://127.0.0.1:8000/test/
defaults.bad_request(request, template_name=‘400.html‘) 错误来自客户端的操作 当用户进行的操作在安全方面可疑的时候,例如篡改会话cookie
‘‘‘ http请求-响应过程中有两个核心对象: http请求对象:HttpRequest http响应响应:HttpResponse 所在位置:django.http ‘‘‘
一个视图函数,或者简短来说叫做视图,是一个简单的Python函数,它接受web请求,并且返回web响应。
响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。
无论视图本身包含什么逻辑,都要返回响应。
返回当前日期和时间: def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
每个视图函数都应接收HttpRequest对象作为第一个参数,一般叫做request。
--------------render(request, template_name[, context]) 结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。 参数: request: 用于生成响应的请求对象。 template_name:要使用的模板的完整名称,可选的参数 context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。 content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值。 status:响应的状态码。默认为200。
-----------------------------------url.py url(r"login", views.login), url(r"yuan_back", views.yuan_back), -----------------------------------views.py def login(req): if req.method=="POST": if 1: # return redirect("/yuan_back/") name="yuanhao" return render(req,"my backend.html",locals()) return render(req,"login.html",locals()) def yuan_back(req): name="苑昊" return render(req,"my backend.html",locals()) -----------------------------------login.html <form action="/login/" method="post"> <p>姓名<input type="text" name="username"></p> <p>性别<input type="text" name="sex"></p> <p>邮箱<input type="text" name="email"></p> <p><input type="submit" value="submit"></p> </form> -----------------------------------my backend.html <h1>用户{{ name }}你好</h1> #总结: render和redirect的区别: # 1 如果 render的页面需要模板语言渲染,需要的将数据库的数据加载到html,那么所有的这一部分 # 除了写在yuan_back的视图函数中,必须还要写在login中,代码重复,没有解耦. # 2 the most important: url没有跳转到/yuan_back/,而是还在/login/,所以当刷新后 # 又得重新登录.
dict.get(‘键‘,default) 或简写为 dict[‘键‘]
dict.getlist(‘键‘,default) GET属性
def getTest1(request): return render(request,‘booktest/getTest1.html‘) def getTest2(request): return render(request,‘booktest/getTest2.html‘) def getTest3(request): return render(request,‘booktest/getTest3.html‘)
url(r‘^getTest1/$‘, views.getTest1), url(r‘^getTest2/$‘, views.getTest2), url(r‘^getTest3/$‘, views.getTest3),
<html> <head> <title>Title</title> </head> <body> 链接1:一个键传递一个值 <a href="/getTest2/?a=1&b=2">gettest2</a><br> 链接2:一个键传递多个值 <a href="/getTest3/?a=1&a=2&b=3">gettest3</a> </body> </html>
def getTest2(request): a=request.GET[‘a‘] b=request.GET[‘b‘] context={‘a‘:a,‘b‘:b} return render(request,‘booktest/getTest2.html‘,context)
<html> <head> <title>Title</title> </head> <body> a:{{ a }}<br> b:{{ b }} </body> </html>
def getTest3(request): a=request.GET.getlist(‘a‘) b=request.GET[‘b‘] context={‘a‘:a,‘b‘:b} return render(request,‘booktest/getTest3.html‘,context)
<html> <head> <title>Title</title> </head> <body> a:{% for item in a %} {{ item }} {% endfor %} <br> b:{{ b }} </body> </html>
def postTest1(request): return render(request,‘booktest/postTest1.html‘)
url(r‘^postTest1$‘,views.postTest1)
创建模板postTest1.html
<html> <head> <title>Title</title> </head> <body> <form method="post" action="/postTest2/"> 姓名:<input type="text" name="uname"/><br> 密码:<input type="password" name="upwd"/><br> 性别:<input type="radio" name="ugender" value="1"/>男 <input type="radio" name="ugender" value="0"/>女<br> 爱好:<input type="checkbox" name="uhobby" value="胸口碎大石"/>胸口碎大石 <input type="checkbox" name="uhobby" value="跳楼"/>跳楼 <input type="checkbox" name="uhobby" value="喝酒"/>喝酒 <input type="checkbox" name="uhobby" value="爬山"/>爬山<br> <input type="submit" value="提交"/> </form> </body> </html>
def postTest2(request): uname=request.POST[‘uname‘] upwd=request.POST[‘upwd‘] ugender=request.POST[‘ugender‘] uhobby=request.POST.getlist(‘uhobby‘) context={‘uname‘:uname,‘upwd‘:upwd,‘ugender‘:ugender,‘uhobby‘:uhobby} return render(request,‘booktest/postTest2.html‘,context)
url(r‘^postTest2$‘,views.postTest2)
<html> <head> <title>Title</title> </head> <body> {{ uname }}<br> {{ upwd }}<br> {{ ugender }}<br> {{ uhobby }} </body> </html>
注意:使用表单提交,注释掉settings.py中的中间件crsf
from django.http import HttpResponse def index(request): return HttpResponse(‘你好‘)
from django.http import JsonResponse def index2(requeset): return JsonResponse({‘list‘: ‘abc‘})
cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能通过cookie的内容来判断这个是“谁”了。
cookie虽然在一定程度上解决了“保持状态”的需求,但是由于cookie本身最大支持4096字节,以及cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是session。
我们可以给每个客户端的cookie分配一个唯一的id,这样用户在访问时,通过cookie,服务器就知道来的人是“谁”。然后我们再根据不同的cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。
3、总结而言:cookie弥补了http无状态的不足,让服务器知道来的人是“谁”;但是cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过cookie识别不同的用户,对应的在session里保存私密的信息以及超过4096字节的文本。
场景:
一个登陆页面,在验证了用户名和密码的正确性后跳转到后台的页面。
但是测试发现,如果绕过登陆页面,直接输入后台的url地址也可以直接访问的。这个显然是不合理的。
我们缺失的就是cookie和session配合的验证。
每当我们使用一款浏览器访问一个登陆页面的时候,一旦我们通过了认证。服务器端就会发送一组随机唯一的字符串(假设是123abc)到浏览器端,这个被存储在浏览端的东西就叫cookie。而服务器端也会自己存储一下用户当前的状态,比如login=true,username=hahaha之类的用户信息。但是这种存储是以字典形式存储的,字典的唯一key就是刚才发给用户的唯一的cookie值。那么如果在服务器端查看session信息的话,理论上就会看到如下样子的字典
{‘123abc‘:{‘login‘:true,‘username:hahaha‘}}
因为每个cookie都是唯一的,所以我们在电脑上换个浏览器再登陆同一个网站也需要再次验证。那么为什么说我们只是理论上看到这样子的字典呢?因为处于安全性的考虑,其实对于上面那个大字典不光key值123abc是被加密的,value值{‘login‘:true,‘username:hahaha‘}在服务器端也是一样被加密的。所以我们服务器上就算打开session信息看到的也是类似与以下样子的东西
{‘123abc‘:dasdasdasd1231231da1231231}
---------------------views.py def login(request): if request.method == "POST": user=request.POST.get("user") pwd=request.POST.get("pwd") if user == "kaylee" and pwd == "123": print(request.COOKIES) #第一次:{} print(request.session) #第一次:<django.contrib.sessions.backends.db.SessionStore object at 0x0000000003D4D0F0> obj=redirect("/index/") #这一步不会走index视图,注意 return redirect才会重定向 obj.set_cookie("Yuan123",11111111,max_age= 10) #三个参数:key,value,期限 return obj return render(request,"login.html") def index(request): print("+++++++++++++",request.COOKIES) #第一次{} 第二次:{‘Yuan123‘: ‘11111111‘} 第三次:{‘Yuan123‘: ‘11111111‘} print("-------------",request.session) #<django.contrib.sessions.backends.db.SessionStore object at 0x0000000003A9A828> is_login=request.COOKIES.get("Yuan123",None) if is_login: return render(request,"index.html") else: return redirect("/login/")
--------------------------------views.py from django.shortcuts import render from django.shortcuts import redirect def login(request): if request.method=="POST": username=request.POST[‘username‘] pwd=request.POST[‘passwd‘] if username==‘abc‘ and pwd==‘123‘: #设置session内部的字典内容 request.session[‘is_login‘]=‘true‘ request.session[‘username‘]=‘abc‘ #登录成功就将url重定向到后台的url return redirect(‘/backend/‘) #登录不成功或第一访问就停留在登录页面 return render(request,‘login.html‘) def backend(request): """ 这里必须用读取字典的get()方法把is_login的value缺省设置为False, 当用户访问backend这个url先尝试获取这个浏览器对应的session中的 is_login的值。如果对方登录成功的话,在login里就已经把is_login 的值修改为了True,反之这个值就是False的 """ is_login=request.session.get(‘is_login‘,False) #如果为真,就说明用户是正常登陆的 if is_login: #获取字典的内容并传入页面文件 cookie_content=request.COOKIES session_content=request.session username=request.session[‘username‘] return render(request,‘backend.html‘, { ‘cookie_content‘:cookie_content, ‘session_content‘:session_content, ‘username‘:username }) else: """ 如果访问的时候没有携带正确的session, 就直接被重定向url回login页面 """ return redirect(‘/login/‘) def logout(request): """ 直接通过request.session[‘is_login‘]回去返回的时候, 如果is_login对应的value值不存在会导致程序异常。所以 需要做异常处理 """ try: #删除is_login对应的value值 del request.session[‘is_login‘] except KeyError: pass #点击注销之后,直接重定向回登录页面 return redirect(‘/login/‘)
--------------------------------------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‘^backend/‘, views.backend), url(r‘^logout/‘, views.logout), ]
--------------------backend.html-----------------内容截取 <div class="container"> <h2>cookie 内容是 {{ cookie_content }}</h2> <h2>session 内容是 {{ session_content }}</h2> <h2>登录用户名 :{{ username }}</h2> <a href="http://830909.blog.51cto.com/logout/">注销</a> </div>
页面显示结果:
从上图中我们看到有一下几点:
1、login页面正确登录的话,后台页面可以获取到浏览器携带的cookie的。
2、第一行的sessionid其实就是cookie值
3、session的内容是加密的,从客户端获取不到session的内容
4、服务端可以通过预设的key值取出session的内容并打印到前端
从火狐浏览器里查看cookie
django的session默认是存储在数据库里的,我们再到数据库查看一下真正session内容
cookie、session总结:
# 1、获取Cookie: # request.COOKIES[‘key‘] # request.get_signed_cookie(key, default=RAISE_ERROR, salt=‘‘, max_age=None) # 参数: # default: 默认值 # salt: 加密盐 # max_age: 后台控制过期时间 # 2、设置Cookie: # rep = HttpResponse(...) 或 rep = render(request, ...) # # rep.set_cookie(key,value,...) # rep.set_signed_cookie(key,value,salt=‘加密盐‘,...) # 参数: # key, 键 # value=‘‘, 值 # max_age=None, 超时时间 # expires=None, 超时时间(IE requires expires, so set it if hasn‘t been already.) # path=‘/‘, Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问 # domain=None, Cookie生效的域名 # secure=False, https传输 # httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖) # 由于cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie。 # <script src=‘/static/js/jquery.cookie.js‘></script> # $.cookie("list_pager_num", 30,{ path: ‘/‘ });
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:
1、数据库Session
jango默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = ‘django.contrib.sessions.backends.db‘ # 引擎(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认) b. 使用 def index(request): # 获取、设置、删除Session中数据 request.session[‘k1‘] request.session.get(‘k1‘,None) request.session[‘k1‘] = 123 request.session.setdefault(‘k1‘,123) # 存在则不设置 del request.session[‘k1‘] # 所有 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 用户session的随机字符串 request.session.session_key # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否 request.session.exists("session_key") # 删除当前用户的所有Session数据 request.session.delete("session_key") ...
2、缓存Session
a. 配置 settings.py SESSION_ENGINE = ‘django.contrib.sessions.backends.cache‘ # 引擎 SESSION_CACHE_ALIAS = ‘default‘ # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存 b. 使用 同上
3、文件Session
. 配置 settings.py SESSION_ENGINE = ‘django.contrib.sessions.backends.file‘ # 引擎 SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存 b. 使用 同上
4、缓存+数据库Session
数据库用于做持久化,缓存用于提高效率 a. 配置 settings.py SESSION_ENGINE = ‘django.contrib.sessions.backends.cached_db‘ # 引擎 b. 使用 同上
5、加密cookie Session
a. 配置 settings.py SESSION_ENGINE = ‘django.contrib.sessions.backends.signed_cookies‘ # 引擎 b. 使用 同上
扩展:Session用户验证
def login(func): def wrap(request, *args, **kwargs): # 如果未登陆,跳转到指定页面 if request.path == ‘/test/‘: return redirect(‘http://www.baidu.com‘) return func(request, *args, **kwargs) return wrap
python的模板:HTML代码+逻辑控制代码
def current_time(req): now=datetime.datetime.now() return render(req, ‘current_datetime.html‘, {‘current_date‘:now})
我们通过 context 传递的简单参数值主要是字符串,然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)。
首先,句点可用于访问列表索引
-----------views.py fruit=[‘apples‘, ‘bananas‘, ‘carrots‘] ----------------templates <h2>{{ fruit.0 }}</h2>
访问字典:
-----------views.py
dic={"name":"kaylee","age":18}
----------------templates
{% for i,v in dic.items %}
{{ i }} {{ v }}
{% endfor %}
{{ dic.name }}
{{ dic.age }}
#同样,也可以通过句点来访问对象的属性
比方说, Python 的 datetime.date 对象有
#year 、 month 和 day 几个属性,你同样可以在模板中使用句点来访问这些属性:
-----------views.py
d = datetime.date(1993, 5, 2) ----------------templates {{ d.year }} {{ d.month }} {{ d.day }}
使用了一个自定义的类,通过实例变量加一点(dots)来访问它的属性,这个方法适
# 用于任意的对象。
-----------views.py
>>> class Person(object): ... def __init__(self, first_name, last_name): ... self.first_name, self.last_name = first_name, last_name ----------------templates {{ person.first_name }} {{ person.last_name }}
# 点语法也可以用来引用对象的方法。 例如,每个 Python 字符串都有 upper() 和 isdigit()
# 方法,你在模板中可以使用同样的句点语法来调用它们:
-----------views.py
Context={‘var‘: ‘123‘}
----------------templates
{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}
# 1 add : 给变量加上相应的值 # # 2 addslashes : 给变量中的引号前加上斜线 # # 3 capfirst : 首字母大写 # # 4 cut : 从字符串中移除指定的字符 # # 5 date : 格式化日期字符串 # # 6 default : 如果值是False,就替换成设置的默认值,否则就是用本来的值 # # 7 default_if_none: 如果值是None,就替换成设置的默认值,否则就使用本来的值
#实例: #value1="aBcDe" {{ value1|upper }}<br> #value2=5 {{ value2|add:3 }}<br> #value3=‘he llo wo r ld‘ {{ value3|cut:‘ ‘ }}<br> #import datetime #value4=datetime.datetime.now() {{ value4|date:‘Y-m-d‘ }}<br> #value5=[] {{ value5|default:‘空的‘ }}<br> #value6=‘<a href="#">跳转</a>‘ {{ value6 }} {% autoescape off %} {{ value6 }} {% endautoescape %} {{ value6|safe }}<br> {{ value6|striptags }} #value7=‘1234‘ {{ value7|filesizeformat }}<br> {{ value7|first }}<br> {{ value7|length }}<br> {{ value7|slice:":-1" }}<br> #value8=‘http://www.baidu.com/?a=1&b=3‘ {{ value8|urlencode }}<br> value9=‘hello I am yuan‘
语法格式: {% tags %}
{% if %}标签计算一个变量值,如果是“true”,即它存在、不为空并且不是false的boolean值,系统则会显示{% if %}和{% endif %}间的所有内容
{% if num >= 100 and 8 %} {% if num > 200 %} <p>num大于200</p> {% else %} <p>num大于100小于200</p> {% endif %} {% elif num < 100%} <p>num小于100</p> {% else %} <p>num等于100</p> {% endif %}
{% if %} 标签接受and,or或者not来测试多个变量值或者否定一个给定的变量 {% if %} 标签不允许同一标签里同时出现and和or,否则逻辑容易产生歧义,例如下面的标签是不合法的: {% if obj1 and obj2 or obj3 %}
{% for %}标签允许你按顺序遍历一个序列中的各个元素,每次循环模板系统都会渲染{% for %}和{% endfor %}之间的所有内容
-----------views.py
class Person(object): def __init__(self,name): self.name=name p1=Person("egon") p2=Person("阿毛") p3=Person("ago") querySet=[p1,p2,p3] ----------------templates {% for person in querySet %} <p>{{ person.name }}</p> {% endfor %}
#在标签里添加reversed来反序循环列表: {% for obj in list reversed %} ... {% endfor %}
#{% for %}标签可以嵌套: {% for country in countries %} <h1>{{ country.name }}</h1> <ul> {% for city in country.city_list %} <li>{{ city }}</li> {% endfor %} </ul> {% endfor %}
#系统不支持中断循环,系统也不支持continue语句,{% for %}标签内置了一个forloop模板变量, #这个变量含有一些属性可以提供给你一些关于循环的信息 1,forloop.counter表示循环的次数,它从1开始计数,第一次循环设为1: {% for item in todo_list %} <p>{{ forloop.counter }}: {{ item }}</p> {% endfor %} 2,forloop.counter0 类似于forloop.counter,但它是从0开始计数,第一次循环设为0 3,forloop.revcounter 4,forloop.revcounter0 5,forloop.first当第一次循环时值为True,在特别情况下很有用: {% for object in objects %} {% if forloop.first %}<li class="first">{% else %}<li>{% endif %} {{ object }} </li> {% endfor %} # 富有魔力的forloop变量只能在循环中得到,当模板解析器到达{% endfor %}时forloop就消失了 # 如果你的模板context已经包含一个叫forloop的变量,Django会用{% for %}标签替代它 # Django会在for标签的块中覆盖你定义的forloop变量的值 # 在其他非循环的地方,你的forloop变量仍然可用
#{% empty %} {{li }} {% for i in li %} <li>{{ forloop.counter0 }}----{{ i }}</li> {% empty %} <li>this is empty!</li> {% endfor %} # [11, 22, 33, 44, 55] # 0----11 # 1----22 # 2----33 # 3----44 # 4----55
用于生成csrf_token的标签,用于防治跨站攻击验证。 其实,这里是会生成一个input标签,和其他表单标签一起提交给后台的。
引用路由配置的地址 <form action="{% url "bieming"%}" > <input type="text"> <input type="submit"value="提交"> {%csrf_token%} </form>
用更简单的变量名替代复杂的变量名 {% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
禁止render {% verbatim %} {{ hello }} {% endverbatim %}
a、在app中创建templatetags模块(必须的)
b、创建任意 .py 文件,如:my_tags.py
from django import template from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改变 @register.filter def filter_multi(v1,v2): return v1 * v2 @register.simple_tag def simple_tag_multi(v1,v2): return v1 * v2 @register.simple_tag def my_input(id,arg): result = "<input type=‘text‘ id=‘%s‘ class=‘%s‘ />" %(id,arg,) return mark_safe(result)
c、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py :{% load my_tags %}
d、使用simple_tag和filter(如何调用)
-------------------------------.html {% load xxx %} #首行 # num=12 {{ num|filter_multi:2 }} #24 {{ num|filter_multi:"[22,333,4444]" }} {% simple_tag_multi 2 5 %} 参数不限,但不能放在if for语句中 {% simple_tag_multi num 5 %}
e、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.
注意:
filter可以用在if等语句后,simple_tag不可以
{% if num|filter_multi:30 > 100 %} {{ num|filter_multi:30 }} {% endif %}
解决问题:减少共用页面区域(比如站点导航)所引起的重复和冗余代码
本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。
你可以对那些不同 的代码段进行定义,而不是 共同 代码段。
第一步是定义 基础模板,该框架之后将由子模板所继承。
示例:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>{% block title %}{% endblock %}</title> </head> <body> <h1>My helpful timestamp site</h1> {% block content %}{% endblock %} {% block footer %} <hr> <p>Thanks for visiting my site.</p> {% endblock %} </body> </html>
子模板的作用就是重载、添加或保留那些块的内容。
我们使用模板标签: {% block %} 。 所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。 每个{% block %}标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。
子模版一:
{% extends "base.html" %} {% block title %}The current time{% endblock %} {% block content %} <p>It is now {{ current_date }}.</p> {% endblock %}
子模版二:
{% extends "base.html" %} {% block title %}Future time{% endblock %} {% block content %} <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> {% endblock %}
注意由于子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值。
父模板 {% block %} 标签中的内容总是被当作一条退路。继承并不会影响到模板的上下文。
换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。
你可以根据需要使用任意多的继承次数。
以下是使用模板继承的一些诀窍:
<1>如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。 <2>一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此 你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越 多越好。 <3>如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。 如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模 板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。 <4>不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个 相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
有一个数据表,就有一个模型类与之对应;
打开models.py文件,定义模型类;
模型类继承自models.Model类;
说明:不需要定义主键列,在生成时会自动添加,并且值为自动增长
当输出对象时,会调用对象的str方法
例如我们设计两张表:图书表和英雄表,两者关系为一对多
class BookInfo(models.Model): btitle = models.CharField(max_length=20) bpub_date = models.DateTimeField() def _ _str_ _(self): return "%d" % self.pk class HeroInfo(models.Model): hname = models.CharField(max_length=20) hgender = models.BooleanField() hcontent = models.CharField(max_length=100) hBook = models.ForeignKey(‘BookInfo‘) def _ _str_ _(self): return "%d" % self.pk
数据库与ORM(对象关系映射) 目的: 通过python代码实现对数据库的增删改查 在ORM中, 表名-----------类名 字段-----------类属性 表中的一条记录-------------类实例对象
注意:由于Django版本不同,所以实现上述的方式也不同,例如本人用的Django版本为1.11.4版本,系统会自动添加,无需自己再添加
python manage.py makemigrations
python manage.py migrate
用一访问多:对象.模型类小写_set 用一访问一:对象.模型类小写 访问id:对象.属性_id
以创建四张表为例,模拟创建模型类的过程:
四张表:书表、作者表、作者详细信息表、出版社表
其中书表与出版社表是一对多(one-to-many)关系,也被称作外键。
书表与作者表是多对多(many-to-many)关系。
此外,作者表、作者详细信息表两者是一对一关系,我们知道:
一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;
一对多:就是主外键关系;(foreign key)
多对多:(ManyToManyField) 自动创建第三张表;(当然我们也可以自己创建第三张表:两个foreign key)
注意在models文件中定义模型类时的先后顺序:
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class AuthorDetail(models.Model):
sex = models.BooleanField(max_length=1, choices=((0, ‘男‘),(1, ‘女‘),))
email = models.EmailField()
address = models.CharField(max_length=50)
birthday = models.DateField()
author = models.OneToOneField(Author)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2,default=10)
def __str__(self):
return self.title
注意:
字段类型 AutoField:一个根据实际ID自动增长的IntegerField,通常不指定 如果不指定,一个主键字段将自动添加到模型中 BooleanField:true/false 字段,此字段的默认表单控制是CheckboxInput NullBooleanField:支持null、true、false三种值 CharField(max_length=字符长度):字符串,默认的表单样式是 TextInput TextField:大文本字段,一般超过4000使用,默认的表单控件是Textarea IntegerField:整数 DecimalField(max_digits=None, decimal_places=None):使用python的Decimal实例表示的十进制浮点数 DecimalField.max_digits:位数总数 DecimalField.decimal_places:小数点后的数字位数 FloatField:用Python的float实例来表示的浮点数 DateField[auto_now=False, auto_now_add=False]):使用Python的datetime.date实例表示的日期 参数DateField.auto_now:每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false 参数DateField.auto_now_add:当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false 该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键 auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果 TimeField:使用Python的datetime.time实例表示的时间,参数同DateField DateTimeField:使用Python的datetime.datetime实例表示的日期和时间,参数同DateField FileField:一个上传文件的字段 ImageField:继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image
字段选项 通过字段选项,可以实现对字段的约束 在字段对象时通过关键字参数指定 null:如果为True,Django 将空值以NULL 存储到数据库中,默认值是 False blank:如果为True,则该字段允许为空白,默认值是 False 对比:null是数据库范畴的概念,blank是表单验证证范畴的 db_column:字段的名称,如果未指定,则使用属性的名称 db_index:若值为 True, 则在表中会为此字段创建索引 default:默认值 primary_key:若为 True, 则该字段会成为模型的主键字段 unique:如果为 True, 这个字段在表中必须有唯一值
tips:
批量导入数据: 例如类名为:Book Book.objects.bulk_create(book_list)
Book.objects.filter(id=1).delete()
ORM之增(create,save):
from app01.models import *
#create方式一: Author.objects.create(name=‘Alvin‘)
#create方式二: Author.objects.create(**{"name":"alex"})
#save方式一: author=Author(name="alvin")
author.save()
#save方式二: author=Author()
author.name="alvin"
author.save()
重点:如何处理外键关系的字段如一对多的publisher和
多对多的authors?
#一对多(ForeignKey): #方式一: 由于绑定一对多的字段,比如publish,存到数据库中的字段名叫publish_id,所以我们可以直接给这个 # 字段设定对应值: Book.objects.create(title=‘php‘, publisher_id=2, #这里的2是指为该book对象绑定了Publisher表中id=2的行对象 publication_date=‘2017-7-7‘, price=99) #方式二: # <1> 先获取要绑定的Publisher对象: pub_obj=Publisher(name=‘河大出版社‘,address=‘保定‘,city=‘保定‘, state_province=‘河北‘,country=‘China‘,website=‘http://www.hbu.com‘) OR pub_obj=Publisher.objects.get(id=1) # <2>将 publisher_id=2 改为 publisher=pub_obj #多对多(ManyToManyField()):
#多对多关系第三张表通过ManyToManyField()自动创建方式,绑定关系仅此一种 author1=Author.objects.get(id=1) author2=Author.objects.filter(name=‘alvin‘)[0] book=Book.objects.get(id=1) book.authors.add(author1,author2) #等同于: book.authors.add(*[author1,author2]) book.authors.remove(*[author1,author2]) #------------------- book=models.Book.objects.filter(id__gt=1) authors=models.Author.objects.filter(id=1)[0] authors.book_set.add(*book) authors.book_set.remove(*book) #------------------- book.authors.add(1) book.authors.remove(1) authors.book_set.add(1) authors.book_set.remove(1) #多对多关系第三张表手动创建方式: class Book2Author(models.Model): author=models.ForeignKey("Author") Book= models.ForeignKey("Book") # 那么就还有一种方式: author_obj=models.Author.objects.filter(id=2)[0] book_obj =models.Book.objects.filter(id=3)[0] s=models.Book2Author.objects.create(author_id=1,Book_id=2) s.save() s=models.Book2Author(author=author_obj,Book_id=1) s.save()
>>> Book.objects.filter(id=1).delete()
有时我们删除一条信息,实际却会删除了多条,比如某本书由两名作者共同创作完成,那么删除该本书,会连同对多关系表中和该本书相关的作者那行记录删除。
这种删除方式就是django默认的级联删除。
例如,以下几张表:
书表:
-------------------------------------------------------------------------------------------------
作者表:
-------------------------------------------------------------------------------------------------
出版社表:
-------------------------------------------------------------------------------------------------
书_作者表:
bookW=Book.objects.get(id=5) bookW.bookName ="我是一本书" bookW.save()
############################
Publisher.objects.filter(id=3).update(name="亚太出版社")
# 注意:不能用get(id=3),因为update是QuerySet对象的方法,filter返回的就是一个QuerySet对象,而get返回的是一个model对象
(filter里面的条件可能有多个条件符合)
注意:模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。
如上:
#--------------------save方法工作机制----------------------------------
###(0.003) SELECT `app01_book`.`id`, `app01_book`.`bookName`, `app01_book`.`price`,
`app01_book`.`publisher_id` FROM `app01_book` WHERE `app01_book`.`id` = 5; args=(5,)
###(0.059) UPDATE `app01_book` SET `bookName` = ‘我是一本书‘, `price` = ‘35.78‘,
`publisher_id` = 1 WHERE `app01_book`.`id` = 5; args=(‘我是一本书‘, ‘35.78‘, 1, 5)
即save方法会将所有属性重新设定一遍,效率低
#-------------------update方法工作机制---------------------------------------
(0.042) UPDATE `app01_publisher` SET `name` = ‘亚太出版社‘ WHERE `app01_publisher`.`id` = 3; args=(‘亚太出版社‘, 3)
即update方法直接设定对应属性
此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。
update()方法会返回一个整型数值,表示受影响的记录条数。
# 查询相关API: # <1>filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 # <2>all(): 查询所有结果 # <3>get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
如果未找到会引发"模型类.DoesNotExist"异常;
如果多条被返回,会引发"模型类.MultipleObjectsReturned"异常。
#-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()-------- # <4>values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列 # <5>exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 # <6>order_by(*field): 对查询结果排序 # <7>reverse(): 对查询结果反向排序 # <8>distinct(): 从返回结果中剔除重复纪录 # <9>values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 # <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。 # <11>first(): 返回第一条记录 # <12>last(): 返回最后一条记录 # <13>exists(): 如果QuerySet包含数据,就返回True,否则返回False
---------------了不起的双下划线(__)之单表条件查询---------------- # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # # models.Tb1.objects.filter(name__contains="ven") # 获取name字段包含“ven”的值 # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 # # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # # startswith,istartswith,
# endswith, iendswith,
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。
QuerySet特点:
<1> 可迭代的
<2> 可切片
#objs=models.Book.objects.all()#[obj1,obj2,ob3...] #QuerySet: 可迭代 # for obj in objs:#每一obj就是一个行对象 # print("obj:",obj) # QuerySet: 可切片 # print(objs[1]) # print(objs[1:4]) # print(objs[::-1])
QuerySet的高效使用:
<1>Django的queryset是惰性的 Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得 到数据库中名字为‘Dave’的所有的人:person_set = Person.objects.filter(first_name="Dave") 上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数, 这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。 <2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql. 为了验证这些,需要在settings里加入 LOGGING(验证方式) obj=models.Book.objects.filter(id=3) # for i in obj: # print(i) # if obj: # print("ok") <3>queryset是具有cache的 当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行 (evaluation).这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset, 你不需要重复运行通用的查询。 obj=models.Book.objects.filter(id=3) # for i in obj: # print(i) ## models.Book.objects.filter(id=3).update(title="GO") ## obj_new=models.Book.objects.filter(id=3) # for i in obj: # print(i) #LOGGING只会打印一次 <4> 简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些 数据!为了避免这个,可以用exists()方法来检查是否有数据: obj = Book.objects.filter(id=4) # exists()的检查可以避免数据放入queryset的cache。 if obj.exists(): print("hello world!") <5>当queryset非常巨大时,cache会成为问题 处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统 进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法 来获取数据,处理完数据就将其丢弃。 objs = Book.objects.all().iterator() # iterator()可以一次只从数据库获取少量数据,这样可以节省内存 for obj in objs: print(obj.name) #BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了 for obj in objs: print(obj.name) #当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使 #用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询 总结: queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。 使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能 会造成额外的数据库查询。
# --------------对象查询----------------------------------
# 正向查找
book1=Book.objects.first()
print(book1.bookName) #python
print(book1.price) #100.25
print(book1.publisher) #人民出版社 南京(因为一对多的关系所以(book1.publisher是一个对象,而不是一个queryset集合)
print(book1.publisher.name) #人民出版社
# 反向查找
pubL=Publisher.objects.last()
print(pubL.name) #亚太出版社
print(pubL.addr) #上海
#如何拿到与某一个出版社绑定的Book对象呢?
pub1=Publisher.objects.first()
print(pub1.book_set.all()) # <QuerySet [<Book: go>, <Book: 我是一本书>, <Book: 从你的全世界路过>]>
注意:条件查询与对象查询对应,是指在filter,values等方法中的通过__来明确查询条件。
#----------------了不起的双下划线(__)之多表条件关联查询--------------- # 正向查找(条件) # ret3=models.Book.objects.filter(title=‘Python‘).values(‘id‘) # print(ret3)#[{‘id‘: 1}] #正向查找(条件)之一对多 ret4=models.Book.objects.filter(title=‘Python‘).values(‘publisher__city‘) print(ret4) #[{‘publisher__city‘: ‘北京‘}] #正向查找(条件)之多对多 ret5=models.Book.objects.filter(title=‘Python‘).values(‘author__name‘) print(ret5) ret6=models.Book.objects.filter(author__name="alex").values(‘title‘) print(ret6) #注意 #正向查找的publisher__city或者author__name中的publisher,author是book表中绑定的字段 #一对多和多对多在这里用法没区别 # 反向查找(条件) #反向查找之一对多: ret8=models.Publisher.objects.filter(book__title=‘Python‘).values(‘name‘) print(ret8)#[{‘name‘: ‘人大出版社‘}] 注意,book__title中的book就是Publisher的关联表名 ret9=models.Publisher.objects.filter(book__title=‘Python‘).values(‘book__authors‘) print(ret9)#[{‘book__authors‘: 1}, {‘book__authors‘: 2}] #反向查找之多对多: ret10=models.Author.objects.filter(book__title=‘Python‘).values(‘name‘) print(ret10)#[{‘name‘: ‘alex‘}, {‘name‘: ‘alvin‘}] #注意 #正向查找的book__title中的book是表名Book #一对多和多对多在这里用法没区别
字段查询
实现where子名,作为方法filter()、exclude()、get()的参数 语法:属性名称__比较运算符=值 表示两个下划线,左侧是属性名称,右侧是比较类型 对于外键,使用“属性名_id”表示外键的原始值
比较运算符 exact:表示判等,大小写敏感;如果没有写“ 比较运算符”,表示判等
contains:是否包含,大小写敏感
startswith、endswith:以value开头或结尾,大小写敏感 isnull、isnotnull:是否为null
在前面加个i表示不区分大小写,如iexact、icontains、istarswith、iendswith
in:是否包含在范围内
例如:filter(pk__in=[1, 2, 3, 4, 5])
gt、gte、lt、lte:大于、大于等于、小于、小于等于 year、month、day、week_day、hour、minute、second:对日期间类型的属性进行运算: filter(bpub_date__year=1980) filter(bpub_date__gt=date(1980, 12, 31)) 查询的快捷方式:pk,pk表示primary key,默认的主键是id filter(pk__lt=6)
示例:
求所有书籍的平均价格:
from django.db.models import Avg,Max,Min,
Book.objects.all().aggregate(Avgprice=Avg("price"))
键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定
一个名称,可以向聚合子句提供它:
>>> Book.objects.all().aggregate(average_price=Avg(‘price‘))
如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
>>> Book.objects.aggregate(Avg(‘price‘), Max(‘price‘), Min(‘price‘))
{‘price__avg‘: 34.35, ‘price__max‘: Decimal(‘81.20‘), ‘price__min‘: Decimal(‘12.99‘)}
求所有书籍的最高价格
Book.objects.all().aggregate(Maxprice=Max("price"))
求老男孩出版过得书中的最高价
Book.objeacts.filter(authorName="老男孩").aggregate(Maxprice=Max("price"))
求每一个作者出版过得书中的最低价
Book.objects.values("authorName").annotate(Minprice=Min("price"))
查询alex出的书总价格
Book.objeacts.filter(authorName="alex").aggregate(Sumprice=Sum("price"))
count的一般用法:
count = list.count()
示例:将所有书籍的价格增长20元
from django.db.models import F
Book.objects.all().update(price=F("price")+20)
示例:
查询书籍名字或者以老开头,或者价格大于200的书籍
from django.db.models import Q
写法一:Book.objects.filter(bookName__startswith="老",price__gt=200) 写法二:Book.objects.filter(Q(bookName__startswith="老") | Q(price__gt=200))
查询书籍名字或者以老开头,或者价格大于200,或者编号为5的书籍
Book.objects.filter(Q(price__gt=100) | Q(id=5),bookName__startswith="老")
查询编号不小于6的书籍
Book.objects.filter(~Q(id__lt=6))
filter(键1=值1,键2=值2) 等价于 filter(键1=值1).filter(键2=值2)
admin是django强大功能之一,它能共从数据库中读取数据,呈现在页面中,进行管理。
默认情况下,它的功能已经非常强大,如果你不需要复杂的功能,它已经够用,但是有时候,一些特殊的功能还需要定制,比如搜索功能,下面就逐步深入介绍如何定制适合自己的admin应用。
python manage.py createsuperuser,按提示输入用户名、邮箱、密码
LANGUAGE_CODE = ‘zh-Hans‘ TIME_ZONE = ‘Asia/Shanghai‘
方式一:使用register的方法
打开booktest/admin.py文件,注册模型
from django.contrib import admin from models import BookInfo admin.site.register(BookInfo)
方式二:使用register的装饰器
@admin.register(Book)
class QuestionAdmin(admin.ModelAdmin): ... admin.site.register(Question, QuestionAdmin)
list_display = [‘pk‘, ‘btitle‘, ‘bpub_date‘]
list_filter = [‘btitle‘]
search_fields = [‘btitle‘]
list_per_page = 10
ordering: 指定排序字段
from django.contrib import admin from app01.models import * # Register your models here. # @admin.register(Book)#----->单给某个表加一个定制 class MyAdmin(admin.ModelAdmin): list_display = ("title","price","publisher") search_fields = ("title","publisher") list_filter = ("publisher",) ordering = ("price",) fieldsets =[ (None, {‘fields‘: [‘title‘]}), (‘price information‘, {‘fields‘: [‘price‘,"publisher"], ‘classes‘: [‘collapse‘]}), ] admin.site.register(Book,MyAdmin) admin.site.register(Publish) admin.site.register(Author)
fields = [‘bpub_date‘, ‘btitle‘]
fieldsets = [ (‘basic‘,{‘fields‘: [‘btitle‘]}), (‘more‘, {‘fields‘: [‘bpub_date‘]}), ]
def gender(self): if self.hgender: return ‘男‘ else: return ‘女‘ gender.short_description = ‘性别‘
class HeroInfoAdmin(admin.ModelAdmin): list_display = [‘id‘, ‘hname‘, ‘gender‘, ‘hcontent‘]
Django基础--Django基本命令、路由配置系统(URLconf)、编写视图、Template、数据库与ORM
标签:mysql数据库 还需 cfile 允许 cep 开发人员 增加 语法 obj
原文地址:http://www.cnblogs.com/metianzing/p/7419999.html