本节内容
Django流程介绍
Django url
Django view
Django models
Django template
Django form
Django admin
Django流程
Django流程介绍
略 直接拷贝Alex博客内容
创建Django工程
1 django-admin startproject day16_site 创建工程目录 2 cd day16_site/ 3 python3.5 manage.py startapp app01 进入目录创建app01程序
目录简介:
$tree day16_site . |____app01 程序目录 | |______init__.py | |____admin.py admin后台文件 次重要 | |____apps.py 没什么用 | |____migrations 此目录存储数据更改的配置文件 | | |______init__.py | |____models.py 写我们数据库ORM对象映射的 重要 | |____tests.py 这个程序写单元测试的 | |____views.py 负责逻辑处理的 视图 重要 |____day16_site 项目配置文件目录 | |______init__.py | |______pycache__ | |____settings.py 整个项目工程的全局配置文件 重要2 | |____urls.py 整个项目工程的全局控制器 重要1 | |____wsgi.py 这个不用管,Django框架基于wsgi实现的.这个不用管就行了. |____manage.py |____templates 存html的,通过命令创建工程目录默认是不创建的,需要手动创建.
Django URL
先看urls.py整个项目工程的全局控制器 ,(又可以看作全局的路由控制器)
1.静态路由配置实例:
a.添加路由
1 from django.conf.urls import url 2 from django.contrib import admin 3 from app01 import views 4 urlpatterns = [ 5 url(r'^admin/', admin.site.urls), #默认的,后台管理的路由 6 url(r'^articles/2003/$', views.special_case_2003), #我们添加的一条静态路由 7 ]
b.在视图中添加一个处理方法.
1 from django.shortcuts import render,HttpResponse # HttpResponse用来封装字符串的方法,封装后才可以返回字符串.否则不返回给浏览器 2 # Create your views here. 3 def special_case_2003(request): # 函数必须有request参数,这个参数就是封装了用户请求所有相关的数据 4 print("matched 2003") 5 return HttpResponse("matched .....") # 视图必须有返回值
PS:Django框架中,只要有做修改,Django框架如果是在运行状态,它会默认重启.
启动djanggo项目
python3.5 manage.py runserver 127.0.0.1:8000
2.1使用正则表达式,实现动态路由:
1 from django.conf.urls import url 2 from django.contrib import admin 3 from app01 import views 4 urlpatterns = [ 5 url(r'^admin/', admin.site.urls), 6 url(r'^articles/2003/$', views.special_case_2003), 7 url(r'^articles/([0-9]{4})/$', views.year_archive), # 动态路由1,一个括号 8 url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), #动态路由2,两个括号 9 url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), #动态路由3,3个括号 10 url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+).(w+)/$', views.article_detail2), #动态路由4,4个括号 11 12 ]
PS: 1.我们在urls中定义动态路由时有几个括号,就要在视图里的处理方法里设置几个参数.但是不能忘记默认要有一个request参数.
2.路由条目的匹配从上而下,一旦匹配上就不下向下进行.所以设置路由条目时顺序要把握好,
我们在视图里要定义如下函数:
1 from django.shortcuts import render,HttpResponse 2 # Create your views here. 3 def special_case_2003(request): 4 print("matched 2003") 5 6 return HttpResponse("matched 2003.....") 7 8 def year_archive(request,year): 9 print("--->",year) 10 return HttpResponse(year) 11 12 def month_archive(request,year,month): 13 print("--->",year,month) 14 return HttpResponse(year+"/"+month) 15 16 def article_detail(request,year,month,article_id): 17 print("--->",year,month) 18 return HttpResponse("%s/%s__%s"%(year,month,article_id)) 19 def article_detail2(request,year,month,article_id,file_type): 20 print("--->",year,month,article_id,file_type) 21 return HttpResponse("%s/%s__%s.%s"%(year,month,article_id,file_type))
2.2 动态路由之 在路由条目中指定变量名:
在路由条目中指定变量名称的好处是:我们在视图中定义函数时就不用在意参数的顺序位置了.
需要注意的是: 我们在视图中定义的函数名称必须和路由中指定的参数名称一致.
这里就举一个例子,除了上面两点,没有其他的特别之处了.
urls.py中定义:
1 urlpatterns = [ 2 url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), # 这句的意思就是 当匹配到这个正则的时候,将匹配到的值传入 year这个变量 3 ]
# (?P<year>[0-9]{4}) 中的?P<year> 就是指定变量名,这个写法可不是Django中特有的,我们在学习正则表达式re时曾经说过.它是正则表达式的用法.
# 正则使用的举例:
>>> m = re.search("(?P<year>[0-9]{4})","test 2001 year haha") >>> m.group('year') '2001'
上面这个例子没看出来定义变量名有啥用.下面这个例子就可以说明了
>>> m = re.search("(?P<year>[0-9]{4})/(?P<month>[0-9]{2})","test 2001/08 year haha") >>> m.group() '2001/08' >>> m.group('year') '2001' >>> m.group('month') '08'
app01/views中如下定义:
1 def year_archive(request,year): # 这里的参数名必须是year,并且除了request之外只能有一个参数叫year 2 print("--->",year) 3 return HttpResponse(year)
PS:动态匹配到的URL永远都是字符串.
3.理论上会了静态路由和动态路由,就可以搞定所有的路由了.但是为了降低Django程序的耦合.最后为每一个APP创建一个urls.py控制器.
场景:比如京东有多个子系统,什么团购支付促销. 加入它是Django框架开发的,你一个子项目在写路由的时候写错了.那影响的将是整个京东网站,所以要分开.
具体语法如下:
1 from django.conf.urls import include, url 2 from django.contrib import admin 3 from app01 import urls as payment_urls #这里导入下app01下的urls 4 5 urlpatterns = [ 6 # ... snip ... 7 url(r'^admin/', admin.site.urls), 8 url(r'^payment/', include('payment_urls')), 9 # ... snip ... 10 ]
上面定义的结果:
所有请求以payment开头都通过app01下的urls文件进行路由.
PS:
1.我们现在还没定义接口规范,现在告诉我们一个常规,URL里从不写动词,只写名词.写动词会出错吗?不会,只是不专业.
2.默认app01目录下是没有urls.py文件的.需要从全局文件拷贝.
3.在app01/urls.py文件里配置路由时,就不用写前面的URL里,因为已经进来呢.
写法如下:
1 from django.conf.urls import url 2 from app01 import views 3 urlpatterns = [ 4 url(r'^$', views.index), # 因为这里已经是二级匹配了,所以前面的url路径就不需要了.$匹配的根目录比如http://127.0.0.1/payment/ 5 url(r'^cash/$', views.pay_by_cash), 6 ]
4.路由条目知识的小扩展.不太常用,但是要知道
假如有如下条目
1 from django.conf.urls import url 2 from . import views 3 4 urlpatterns = [ 5 url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/history/$', views.history), 6 url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/edit/$', views.edit), 7 url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/discuss/$', views.discuss), 8 url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/permissions/$', views.permissions), 9 ]
我们发现,条目前面'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/'这部分内容都是一样的.
如果你不想重复写,可以按照下面的写法书写:
1 from django.conf.urls import include, url 2 from . import views 3 4 urlpatterns = [ 5 url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/', include([ 6 url(r'^history/$', views.history), 7 url(r'^edit/$', views.edit), 8 url(r'^discuss/$', views.discuss), 9 url(r'^permissions/$', views.permissions), 10 ])), 11 ]
这是一种写法,你就知道它就行了,当看到别人这么写你知道什么意思就行了.
5.路由条目最后一个小知识点.
url路由条目往视图函数里传入额外的参数
怎么写呢?写法如下:
1 from django.conf.urls import url 2 from . import views 3 4 urlpatterns = [ 5 url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), #前面的都一样,多了一个字典{'foo': 'bar'} 6 ]
我们在app01/urls.py写一个:
1 from django.conf.urls import url 2 from app01 import views 3 urlpatterns = [ 4 url(r'^$', views.index), # 因为这里已经是二级匹配了,所以前面的url路径就不需要了.$匹配的根目录比如http://127.0.0.1/payment/ 5 url(r'^cash/$', views.pay_by_cash), 6 url(r'^articles/2003/$', views.special_case_2003,{'file_type':"json"}), 7 ]
视图为:
1 def special_case_2003(request): 2 print("matched 2003") 3 return HttpResponse("matched 2003.....")
这个时候我们访问:http://127.0.0.1:8000/payment/articles/2003/
会报错:
Exception Value:
special_case_2003() got an unexpected keyword argument 'file_type'
提示URL传入了参数,但是视图里却没有这个参数.所以我们应该在这个视图里加上这个参数,并且参数名必须是file_type
视图更改为:
1 def special_case_2003(request,file_type): 2 print("matched 2003") 3 return HttpResponse("matched 2003..... %s"%file_type)
那么作用呢?什么场景下用?
注意了,这里是路由传入的,不是用户在浏览器输入url的请求.是路由自己添加的.那么应用场景是什么?
我们加入京东要求其他子项目都要又一个规范,比如只要用户一登陆,其他子项目就登陆.这个用户信息就传入其他子项目.
于是全局的urls.py文件可以这么写:
1 urlpatterns = [ 2 # ... snip ... 3 url(r'^admin/', admin.site.urls), 4 url(r'^payment/', include('payment_urls'),{'user':'alex'}), #这就代表所有以payment开头的请求都会传入{'user':'alex'}这个字典 5 # ... snip ... 6 ]
当然payment_urls 里引入的视图都要都要加上一个user参数名.
还有这里的'alex'只是一个隐喻,实际中它是动态的user_object,实际生产环境,我们不会动态的传入用户名,而是用户对象.用户对象包含很多东西,名字,有效期。
url配置扩展
代码
1 urlpatterns = [ 2 url(r'^admin/', admin.site.urls), 3 url(r'^accounts/payment/$', views.payment,name='sss'), 4 ]
url中特殊的name参数,是url的別名,用于与前端的交互,前端不需要知道具体的URL,只需要指定别名即可多说用于前端页面的form action和前端跳转或者前端链接。应用场景:当URL变化时,不想改模版html。
比如:
1 <form action = {% url ‘sss’ %}method = ‘post’/> 2 ...... 3 </form>
urls的别名参数用于前端交互。views不能与urls的别名交互。
url目前的知识就知道上面6条,对于现在的我们就够了.
Django GET和Post请求方法
大家知道Get和Post只是 两种浏览器请求页面的方式.从socket层面来讲都是一样的.只是HTTP协议自己定义的两种方式.
比如我们之前自己写socket的时候,曾经定义过在发送字符串之前,先发送描述字符串大小的验证信息.这是我们自己定义的.
同样HTTP自己定义了,认为某一种方式为GET方式,而另外一种方式为POST
那具体有什么区别呢?主要体现在请求参数上.
如果是用GET提交数据到后台,最终体现在URL上.比如你往后台提交用户名和密码,用get的方式就明文出现在URL上:如下:
http://127.0.0.1/payment?user=alex&password=123&email=123@123.com
后台就会接收到明文的请求:GET /payment?user=alex&password=123&email=123@123.com
如果是用POST请求,就不是明文.
无论是GET还是POST,我们的视图都可以获得.还记得之前在视图中request参数吗?它封装了用户请求的所有内容.
request.method 获得用户用哪一种请求方式.
request.GET 获得GET方式提交请求的参数内容格式为{'user':'alex','password':'123','email':'123@123.com'}
request.POST 获得GET方式提交请求的参数内容格式和GET获取的相同
另外:GET方式提交数据的内容大小根据浏览器的不同而不同,chrome限制8K,IE限制2K
而POST提交没有限制,可以是几十M的文档.
get和post除了上面两点区别.我们在实际开发中,应用规范应该遵循get请求只用来获取数据.不能用于创建数据.(不专业的,这是规矩)
post就是创建或者修改.
根据同一个视图针对同一个URL根据请求方法不同,可以返回不同的处理结果.
这种新的玩法专业名称:URL的复用
视图中用if判断:
1 if request.method == 'GET': 2 pass 3 else: 4 pass
Django中不管你是GET,POST,视图都不知道.所以你要在视图中判断.
Django 视图VIEWS
最简单的返回一个字符串形式的view,这种东西是在ajax的时候(整个页面不刷新,只是部分刷新,返回的是一个具体数据,而不是新的页面)
1 from django.http import HttpResponse 2 3 def my_view(request): 4 if request.method == 'GET': 5 # <view logic> 6 return HttpResponse('result')
如果想直接返回一个html文档
1 from django.shortcuts import render,HttpResponse 2 3 # Create your views here. 4 5 6 def test_view(request): 7 return render(request,'index.html')
Django Template渲染
a. 直接返回字符串使用HttpRespons (用于返回ajax)
b. 返回html模版使用render 用于返回html
c. 动态返回html中,在视图中使用django.template模块中的Template, Context方法
d. 动态返回html,在模版中使用jinjia2 语法,并且获取列表或者字典使用 item.name的形式.而不是item.get[name]
e. 动态返回html,在模版中for循环写法,以及if写法
{% for item in user_info %} {% if {{ item.name }} == 'alex3' %} <li class='c1'> {{ item.name }} </li> {% else %} <li> {{ item.name }} </li> {% endfor%}
f. 动态返回html,判断for循环的行号.来进行不同的样式
{% for item in user_info %} {% if forloop.counter > 2 %} //这里用forloop.counter 来记录循环的行数 <li class='c1'> {{ item.name }} </li> {% else %} <li> {{ item.name }} </li> {% endfor%}
ps:需要注意的一点 forloop.counter0 的意思是从0来计算,也就是说第一个循环的计数从0开始 .
默认forloop.counter 为从数字1开始计数.
g. 动态返回html,使用{{value|divisibleby:"2"}}来实现隔行换色的需求
{% for item in user_info %} {% if forloop.counter|divisibleby:"2" %} //如果循环到的次数是2的整数倍,那么返回true,如果不能整除2则返回false <li class='c1'> {{ item.name }} </li> {% else %} <li> {{ item.name }} </li> {% endfor%}
h. 高级用法: 我们知道一些网站的导航往往不会变.基于开发的不写重复代码的思想.我们应该把一个大家都要用到的代码块作为模版.Django中使用{% extends %}引入模版
假设又一个templatesapp01 1.html.里面有头部导航标题.底部有落款,每一个该项目下的网页都需要用,代码如下:
<body> <ul> <li style="display: inline-block">Home</li> <li style="display: inline-block">page1</li> <li style="display: inline-block">page2</li> <li style="display: inline-block">page3</li> </ul> <div> <div>这是模版文件里的内容</div> </div> <footer> <p>Powered by alex</p> </footer> </body>
我们想把这个头部和底部应用到某一个html文件中 ,就需要在那个文件中使用{% extends %}
比如,现在写一个my1.html,内容如下:
{% extends 'app01/index.html' %} page 1
结果是 ,my1.html 继承了t1.html但是,它自己里的内容也被干掉了.也就是不显示page1,而是完完全全的显示t1.html里面的内容.
实际开发我们肯定不希望这样.那么怎样解决呢.我们在模版文件中,可以先声明哪些内容可以被重写的.
使用{% block xxx %}
... ...
{% endblook %}
t1.html模版文件的代码更改如下:
<body> <ul> <li style="display: inline-block">Home</li> <li style="display: inline-block">page1</li> <li style="display: inline-block">page2</li> <li style="display: inline-block">page3</li> </ul> {% block content-contain %} // 这个content-contain 名字是随意起的 <div> <div>这是模版文件里的内容</div> </div> {% endblook %} <footer> <p>Powered by alex</p> </footer> </body>
PS:
{% block content-contain %} ... {% endblook %} 就是声明这部分内容是可以重写.
我们在引入这个模版的html文件中,通过下面的方式进行重写:
{% extends 'app01/index.html' %} {% block content-contain %} page 1 {% endblook %}
这样写,我们就把模版里声明的那部分重写了.要注意的是 模版中定义的block名称和引入的html文档里的block名称要一致.
i. 高级用法之include,前面我们学习了使用extends 方法将另外一个html文件完全引入到当前html文件.我们称被引入的文件为html模版文件.
当一个文件需要被网站内的其他大部分文件引用的时候我们才会把这个文件作为模版的方式引入其他文件.
有些文件会被引用,但是又不需要被大多数文件引用.那么我们就不需要用extends 方式引用.而是使用另外一种方式引入内容.
这部分被引入的内容又一个特点,它不是一个完整的html代码文件,而是一部分功能代码块.比如一个form表单功能,我们在一个文件中直接写入以下内容:
<div style=" <form action="/register/"> Username:<input name="username" type="text"/> Password:<input name="password" type="text"> </form> </div>
然后我们在前面page1页面里加入
{% extends 'app01/index.html' %} {% block content-contain %} page 1 {% include 'app01/m1.html' %} //这里就引入了上面的代码块了 {% endblook %}
PS: {% extends 'xxx/xx.html' %} 要写在文件开始处,并且一个html只能引入一个文件.
{% include 'xxx/yy.html' %} 位置不限.数量不限
j. 模版html之多重继承,和多重重写.
一个html文件a.html作为模版被另外一个html文件b.html引入.
那么b.html还可以被另外一个html文件c.html文件引入.
在c.html文件可以根据{% block xxx %} 定义定名称进行重写.
上面a,b,c,d,e,f,g,h,i,j几条是总结内容.看懂了就基本上把Django中模版所用到的知识覆盖了.下面是老师博客内容的详细描述.
1.我们在templates目录下创建一个目录app01,然后在templates/app01/目录下创建一个index.html
2.在settings.py文件中添加模版目录
参照上节内容总结.
你可能已经注意到我们在例子视图中返回文本的方式有点特别。 也就是说,HTML被直接硬编码在 Python 代码之中。
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
尽管这种技术便于解释视图是如何工作的,但直接将HTML硬编码到你的视图里却并不是一个好主意。 让我们来看一下为什么:
对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。
Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。
程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 我们可以使用 Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。
Django 模版基本语法
python3.5 manage.py shell (这样执行后,我们的终端就包含了Django全局配置的环境变量了.)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
u'My name is Stephane.'
同一模板,多个上下文
一旦有了 模板 对象,你就可以通过它渲染多个context, 例如:
>>> from django.template import Template, Context
>>> t = Template('Hello, {{ name }}')
>>> print t.render(Context({'name': 'John'}))
Hello, John
>>> print t.render(Context({'name': 'Julie'}))
Hello, Julie
>>> print t.render(Context({'name': 'Pat'}))
Hello, Pat
无论何时我们都可以像这样使用同一模板源渲染多个context,只进行 一次模板创建然后多次调用render()方法渲染会更为高效:
# 不好的写法,是因为你每一次生成模版都是要有开销的 for name in ('John', 'Julie', 'Pat'): t = Template('Hello, {{ name }}') print t.render(Context({'name': name})) # 好的写法,一个模版实例可以多次进行上下文的渲染 t = Template('Hello, {{ name }}') for name in ('John', 'Julie', 'Pat'): print t.render(Context({'name': name}))
Django 模板解析非常快捷。 大部分的解析工作都是在后台通过对简短正则表达式一次性调用来完成。 这和基于 XML 的模板引擎形成鲜明对比,那些引擎承担了 XML 解析器的开销,且往往比 Django 模板渲染引擎要慢上几个数量级。
深度变量的查找
在到目前为止的例子中,我们通过 context 传递的简单参数值主要是字符串,然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。
在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)。
最好是用几个例子来说明一下。 比如,假设你要向模板传递一个 Python 字典。 要通过字典键访问该字典的值,可使用一个句点:
>>> from django.template import Template, Context >>> person = {'name': 'Sally', 'age': '43'} >>> t = Template('{{ person.name }} is {{ person.age }} years old.') >>> c = Context({'person': person}) >>> t.render(c) u'Sally is 43 years old.'
同样,也可以通过句点来访问对象的属性。 比方说, Python 的 datetime.date 对象有 year 、 month 和 day 几个属性,你同样可以在模板中使用句点来访问这些属性:
>>> from django.template import Template, Context >>> import datetime >>> d = datetime.date(1993, 5, 2) >>> d.year 1993 >>> d.month 5 >>> d.day 2 >>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.') >>> c = Context({'date': d}) >>> t.render(c) u'The month is 5 and the year is 1993.'
这个例子使用了一个自定义的类,演示了通过实例变量加一点(dots)来访问它的属性,这个方法适用于任意的对象。
>>> from django.template import Template, Context >>> class Person(object): ... def __init__(self, first_name, last_name): ... self.first_name, self.last_name = first_name, last_name >>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.') >>> c = Context({'person': Person('John', 'Smith')}) >>> t.render(c) u'Hello, John Smith.'
点语法也可以用来引用对象的* 方法*。 例如,每个 Python 字符串都有 upper() 和 isdigit() 方法,你在模板中可以使用同样的句点语法来调用它们:
>>> from django.template import Template, Context >>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}') >>> t.render(Context({'var': 'hello'})) u'hello -- HELLO -- False' >>> t.render(Context({'var': '123'})) u'123 -- 123 -- True'
注意这里调用方法时并* 没有* 使用圆括号 而且也无法给该方法传递参数;你只能调用不需参数的方法。 (我们将在本章稍后部分解释该设计观。)
最后,句点也可用于访问列表索引,例如:
>>> from django.template import Template, Context >>> t = Template('Item 2 is {{ items.2 }}.') >>> c = Context({'items': ['apples', 'bananas', 'carrots']}) >>> t.render(c) u'Item 2 is carrots.'
include 模板标签
在讲解了模板加载机制之后,我们再介绍一个利用该机制的内建模板标签: {% include %} 。该标签允许在(模板中)包含其它的模板的内容。 标签的参数是所要包含的模板名称,可以是一个变量,也可以是用单/双引号硬编码的字符串。 每当在多个模板中出现相同的代码时,就应该考虑是否要使用 {% include %} 来减少重复。
下面这两个例子都包含了 nav.html 模板。这两个例子是等价的,它们证明单/双引号都是允许的。
{% include 'nav.html' %}
{% include "nav.html" %}
下面的例子包含了 includes/nav.html 模板的内容:
{% include 'includes/nav.html' %}
模板继承
到目前为止,我们的模板范例都只是些零星的 HTML 片段,但在实际应用中,你将用 Django 模板系统来创建整个 HTML 页面。 这就带来一个常见的 Web 开发问题: 在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?
解决该问题的传统做法是使用 服务器端的 includes ,你可以在 HTML 页面中使用该指令将一个网页嵌入到另一个中。 事实上, Django 通过刚才讲述的 {% include %} 支持了这种方法。 但是用 Django 解决此类问题的首选方法是使用更加优雅的策略—— 模板继承 。
本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。
让我们通过修改 current_datetime.html 文件,为 current_datetime 创建一个更加完整的模板来体会一下这种做法:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>The current time</title>
</head>
<body>
<h1>My helpful timestamp site</h1>
<p>It is now {{ current_date }}.</p>
<hr>
<p>Thanks for visiting my site.</p>
</body>
</html>
这看起来很棒,但如果我们要为第三章的 hours_ahead 视图创建另一个模板会发生什么事情呢?
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>Future time</title>
</head>
<body>
<h1>My helpful timestamp site</h1>
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
<hr>
<p>Thanks for visiting my site.</p>
</body>
</html>
很明显,我们刚才重复了大量的 HTML 代码。 想象一下,如果有一个更典型的网站,它有导航条、样式表,可能还有一些 JavaScript 代码,事情必将以向每个模板填充各种冗余的 HTML 而告终。
解决这个问题的服务器端 include 方案是找出两个模板中的共同部分,将其保存为不同的模板片段,然后在每个模板中进行 include。 也许你会把模板头部的一些代码保存为 header.html 文件:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
你可能会把底部保存到文件 footer.html :
<hr>
<p>Thanks for visiting my site.</p>
</body>
</html>
对基于 include 的策略,头部和底部的包含很简单。 麻烦的是中间部分。 在此范例中,每个页面都有一个<h1>My helpful timestamp site</h1> 标题,但是这个标题不能放在 header.html 中,因为每个页面的 <title> 是不同的。 如果我们将 <h1> 包含在头部,我们就不得不包含 <title> ,但这样又不允许在每个页面对它进行定制。 何去何从呢?
Django 的模板继承系统解决了这些问题。 你可以将其视为服务器端 include 的逆向思维版本。 你可以对那些不同 的代码段进行定义,而不是 共同 代码段。
第一步是定义 基础模板 , 该框架之后将由 子模板 所继承。 以下是我们目前所讲述范例的基础模板:
<!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>
这个叫做 base.html 的模板定义了一个简单的 HTML 框架文档,我们将在本站点的所有页面中使用。 子模板的作用就是重载、添加或保留那些块的内容。 (如果你一直按顺序学习到这里,保存这个文件到你的template目录下,命名为 base.html .)
我们使用一个以前已经见过的模板标签: {% block %} 。 所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。 每个{% block %}标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。
现在我们已经有了一个基本模板,我们可以修改 current_datetime.html 模板来 使用它:
{% extends "base.html" %}
{% block title %}The current time{% endblock %}
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}
再为 hours_ahead 视图创建一个模板,看起来是这样的:
{% extends "base.html" %}
{% block title %}Future time{% endblock %}
{% block content %}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}
看起来很漂亮是不是? 每个模板只包含对自己而言 独一无二 的代码。 无需多余的部分。 如果想进行站点级的设计修改,仅需修改 base.html ,所有其它模板会立即反映出所作修改。
以下是其工作方式。 在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎立即装载其父模板,即本例中的 base.html 。
此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block 。因此,引擎将会使用我们在 { block title %} 中定义的标题,对 {% block content %} 也是如此。 所以,网页标题一块将由{% block title %}替换,同样地,网页的内容一块将由 {% block content %}替换。
注意由于子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容总是被当作一条退路。
继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。
你可以根据需要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:
创建 base.html 模板,在其中定义站点的主要外观感受。 这些都是不常修改甚至从不修改的部分。
为网站的每个区域创建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。这些模板对base.html 进行拓展,并包含区域特定的风格与设计。
为每种类型的页面创建独立的模板,例如论坛页面或者图片库。 这些模板拓展相应的区域模板。
这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工作。
以下是使用模板继承的一些诀窍:
如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。
如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
Django Models
django 本身提供了非常强大易使用的ORM组件,并且支持多种数据库,如sqllite,mysql,progressSql(做金融的比较多,默认支持事物),Oracle等,当然最常用的搭配还是mysql,要启用orm,先要配置好连接数据 的信息
Django配置使用数据库
在你创建的project目录下编辑settings.py
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 's12day16', #确保此数据库已存在 'HOST':'127.0.0.1', 'PORT':'3307', 'USER':'root', 'PASSWORD':'123456' } }
ps:mysqldb目前还不支持3.0python
唉,最近赶了个新潮,用起了Python3.4跟Django1.6,数据库依然是互联网企业常见的MySql。
悲催的是在Python2.7时代连接MySql的MySQLdb还不支持Python3.4,还好,苦苦追问G哥终于找到一款代替品,而且效果不错,这代替品就是:pymysql。
下载此物然后进行安装,跟其它python第三包没任何区别,一样的安装。
关于Django1.6中DATABASES的设置也是一样不用做任何修改,跟以前MySQLdb的时候一样,settings.py里的配置不变,但是要在项目目录下的__init__.py文件加入下面两句
这里是mysite/mysite/__init__.py
1 import pymysql 2 pymysql.install_as_MySQLdb()
做完上述动作后,即可在django中访问mysql了。
下面要开始学习Django ORM语法了,为了更好的理解,我们来做一个基本的 书籍/作者/出版商 数据库结构。 我们这样做是因为 这是一个众所周知的例子,很多SQL有关的书籍也常用这个举例。
我们来假定下面的这些概念、字段和关系:
一个作者有姓,有名及email地址。
出版商有名称,地址,所在城市、省,国家,网站。
书籍有书名和出版日期。 它有一个或多个作者(和作者是多对多的关联关系[many-to-many]), 只有一个出版商(和出版商是一对多的关联关系[one-to-many],也被称作外键[foreign key])
from django.db import models class Publisher(models.Model): name = models.CharField(max_length=30,unique=True) 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() class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
更多models field 字段:https://docs.djangoproject.com/en/1.9/ref/models/fields/
每个模型相当于单个数据库表,每个属性也是这个表中的一个字段。 属性名就是字段名,它的类型(例如CharField )相当于数据库的字段类型 (例如 varchar )。例如, Publisher 模块等同于下面这张表(用PostgreSQL的 CREATE TABLE 语法描述):
CREATE TABLE "books_publisher" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(30) NOT NULL, "address" varchar(50) NOT NULL, "city" varchar(60) NOT NULL, "state_province" varchar(30) NOT NULL, "country" varchar(50) NOT NULL, "website" varchar(200) NOT NULL );
模型安装
完成这些代码之后,现在让我们来在数据库中创建这些表。 要完成该项工作,第一步是在 Django 项目中 激活这些模型。 将上面的模型所在的app (此例子app是books) 添加到配置文件的已安装应用列表中即可完成此步骤。
再次编辑 settings.py 文件, 找到 INSTALLED_APPS 设置。 INSTALLED_APPS 告诉 Django 项目哪些 app 处于激活状态。 缺省情况下如下所示:
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'app01', #这里是添加的,这里添加的内容必须是APP的目录名,也就是python3.5 manage.py startapp APP名称 )
使数据库按照模型的配置生成表结构
$ python manage.py makemigrations #生成同步纪录 Migrations for 'app01': 0001_initial.py: - Create model Author - Create model Book - Create model Publisher - Add field publisher to book $ python manage.py migrate #开始同步 Operations to perform: Apply all migrations: admin, contenttypes, app01, auth, sessions Running migrations: Rendering model states... DONE Applying app01.0001_initial... OK
执行我们看看创建关于app01的表
show tables; +----------------------------+ | Tables_in_s12day16 | +----------------------------+ | app01_author | | app01_book | | app01_book_authors |这个表我们在定义models.py时并没有创建这个类.它就是定义many—to_many创建的关系表,和sqlachemy一样 | app01_publisher |
+----------------------------+
创建了表之后,我们就可以通过ORM的命令对数据库进行增删改查的操作了.但是在讲解命令之前.我们先使用Django自带的admin后台管理程序,通过页面来对我们的项目表进行操作.
怎样通过页面才能访问呢?
1.首先在app01 APP目录下的admin.py文件中,注册我们创建的ORM类
from django.contrib import admin # Register your models here. from app01 import models #引入我们之前写的models admin.site.register(models.Author) #注册Author的ORM类 admin.site.register(models.Book) # 注册Book ORM类 admin.site.register(models.Publisher) # 注册Publisher ORM类
此时就可以访问http://127.0.0.1:8000/admin/
2.为admin后台创建管理员账户
python3.5 manage.py createsuperuser
账户创建完成后,我们就可以访问http://127.0.0.1:8000/admin/,输入用户名和密码登陆到后台管理系统了.
3.使用后台管理系统,管理注册到admin文件的models
ps:查看Django版本
python3.5 manage.py shell >>> import django >>> django.VERSION (1, 9, 5, 'final', 0)
我们先通过后台管理系统创建两个作者.
创建完成后,我们在管理后台的界面中只能看到:
插入图
这是为什么呢?我们在学习sqlachemy的时候,也碰到过,就是查找出来后,print的时候是显示对象,对于结果为多个对象时就会搞不清楚.谁是谁.
还记得我们当时怎样解决的吗?对是在orm类中定义def __repr__() 函数.这里我们在Django中同样也是通过这个方法.
只是在Django中定义的不是__repr__()函数,而是定义__unicode__(self):函数.具体举例如下:
from django.db import models # Create your models here. class Publisher(models.Model): name = models.CharField(max_length=30,unique=True) # CharField后必须有(max_length=?) 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 __unicode__(self): return "<%s>"%(self.name) class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() def __unicode__(self): return "<%s %s>"%(self.first_name,self.last_name) class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() def __unicode__(self): return "<%s>"%(self.title)
按照常理来说,应该在后台里就可以看到是姓名的格式了.但是我们实际没成功.为什么呢.因为在python3.x中,就不能定义__unicode__,而是定义程__str__
如:
def __str__(self): return "<%s>"%(self.name)
这时候我们在后台中查看就显示我们自己定义的格式了:
AUTHOR
<nanhai old>
<alex li>
接下来我们在后台中创建2个出版社.和2本书
我们在创建出版社的时候,很普通
当我们创建2本书的时候,你会发现,后台支持你选择作者并且可以多选.这是因为我们在创建书的orm类时,设置了一个many_to_many的字段authors = models.ManyToManyField(Author)
同样在选择出版社的时候,支持单选,因为我们创建书的orm类时 创建了一个外键publisher = models.ForeignKey(Publisher)
上面这些介绍admin后台管理功能的内容,1.告诉我们要想使用后台管理数据库要先注册.2.要手动创建后台管理员账户 3.后台操作数据库提供很多方便和很多强大的功能.
基本数据访问
一旦你创建了模型,Django自动为这些模型提供了高级的Python API。
运行 python manage.py shell 并输入下面的内容试试看:
增加
>>> from books.models import Publisher >>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue', ... city='Berkeley', state_province='CA', country='U.S.A.', ... website='http://www.apress.com/') >>> p1.save() >>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.', ... city='Cambridge', state_province='MA', country='U.S.A.', ... website='http://www.oreilly.com/') >>> p2.save() >>> publisher_list = Publisher.objects.all() >>> publisher_list [<Publisher: Publisher object>, <Publisher: Publisher object>] 这里有一个值得注意的地方,在这个例子可能并未清晰地展示。 当你使用Django modle API创建对象时Django并未将对象保存至数据库内,除非你调用`` save()`` 方法: p1 = Publisher(...) # At this point, p1 is not saved to the database yet! p1.save() # Now it is.
如果需要一步完成对象的创建与存储至数据库,就使用`` objects.create()`` 方法。 下面的例子与之前的例子等价:
>>> p1 = Publisher.objects.create(name='Apress', ... address='2855 Telegraph Avenue', ... city='Berkeley', state_province='CA', country='U.S.A.', ... website='http://www.apress.com/') >>> p2 = Publisher.objects.create(name="O'Reilly", ... address='10 Fawcett St.', city='Cambridge', ... state_province='MA', country='U.S.A.', ... website='http://www.oreilly.com/') >>> publisher_list = Publisher.objects.all() >>> publisher_list
__unicode__()方法是个什么鬼?
当我们打印整个publisher列表时,我们没有得到想要的有用信息,无法把````对象区分开来:
[<Publisher: Publisher object>, <Publisher: Publisher object>]
我们可以简单解决这个问题,只需要为Publisher 对象添加一个方法 __unicode__() 。 __unicode__() 方法告诉Python如何将对象以unicode的方式显示出来。 为以上三个模型添加__unicode__()方法后,就可以看到效果了。
def __unicode__(self):
return self.name
对__unicode__()的唯一要求就是它要返回一个unicode对象 如果`` __unicode__()`` 方法未返回一个Unicode对象,而返回比如说一个整型数字,那么Python将抛出一个`` TypeError`` 错误,并提示:”coercing to Unicode: need string or buffer, int found” 。
插入和更新数据
你已经知道怎么做了: 先使用一些关键参数创建对象实例,如下:
>>> p = Publisher(name='Apress', ... address='2855 Telegraph Ave.', ... city='Berkeley', ... state_province='CA', ... country='U.S.A.', ... website='http://www.apress.com/')
这个对象实例并 没有 对数据库做修改。 在调用`` save()`` 方法之前,记录并没有保存至数据库,像这样:
>>> p.save()
在SQL里,这大致可以转换成这样:
INSERT INTO books_publisher (name, address, city, state_province, country, website) VALUES ('Apress', '2855 Telegraph Ave.', 'Berkeley', 'CA', 'U.S.A.', 'http://www.apress.com/');
因为 Publisher 模型有一个自动增加的主键 id ,所以第一次调用 save() 还多做了一件事: 计算这个主键的值并把它赋值给这个对象实例:
>>> p.id 52 # this will differ based on your own data
接下来再调用 save() 将不会创建新的记录,而只是修改记录内容(也就是 执行 UPDATE SQL语句,而不是 INSERT语句):
>>> p.name = 'Apress Publishing' >>> p.save()
前面执行的 save() 相当于下面的SQL语句:
UPDATE books_publisher SET name = 'Apress Publishing', address = '2855 Telegraph Ave.', city = 'Berkeley', state_province = 'CA', country = 'U.S.A.', website = 'http://www.apress.com' WHERE id = 52;
注意,并不是只更新修改过的那个字段,所有的字段都会被更新。效率不高,但是它就是这个样子.
查找对象
当然,创建新的数据库,并更新之中的数据是必要的,但是,对于 Web 应用程序来说,更多的时候是在检索查询数据库。 我们已经知道如何从一个给定的模型中取出所有记录:
>>> Publisher.objects.all() [<Publisher: Apress>, <Publisher: O'Reilly>]
这相当于这个SQL语句:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher;
注意到Django在选择所有数据时并没有使用 SELECT* ,而是显式列出了所有字段。 设计的时候就是这样:SELECT* 会更慢,而且最重要的是列出所有字段遵循了Python 界的一个信条: 明言胜于暗示。
数据过滤
我们很少会一次性从数据库中取出所有的数据;通常都只针对一部分数据进行操作。 在Django API中,我们可以使用`` filter()`` 方法对数据进行过滤:
>>> Publisher.objects.filter(name='Apress') [<Publisher: Apress>]
filter() 根据关键字参数来转换成 WHERE SQL语句。 前面这个例子 相当于这样:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = 'Apress';
你可以传递多个参数到 filter() 来缩小选取范围:
>>> Publisher.objects.filter(country="U.S.A.", state_province="CA") [<Publisher: Apress>]
多个参数会被转换成 AND SQL从句, 因此上面的代码可以转化成这样:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = 'U.S.A.' AND state_province = 'CA';
前面都是精确过滤,那能不能模糊过滤呢?可以,字段名__contians,实例代码如下:
>>> Publisher.objects.filter(name__contains="press") # name__contains 注name后面是两个下划线 [<Publisher: Apress>]
在 name 和 contains 之间有双下划线。和Python一样,Django也使用双下划线来表明会进行一些魔术般的操作。这里,contains部分会被Django翻译成LIKE语句:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name LIKE '%press%';
其他的一些查找类型有:icontains(大小写不敏感的LIKE),startswith和endswith, 还有range
例:models.Author.objects.filter(id__range=[1,3]) 取ID值1~3之间的
获取单个对象
上面的例子中`` filter()`` 函数返回一个记录集,这个记录集是一个列表。 相对列表来说,有些时候我们更需要获取单个的对象, `` get()`` 方法就是在此时使用的:
>>> Publisher.objects.get(name="Apress") <Publisher: Apress>
这样,就返回了单个对象,而不是列表(更准确的说,QuerySet)。 所以,如果结果是多个对象,会导致抛出异常:
>>> Publisher.objects.get(country="U.S.A.") Traceback (most recent call last): ... MultipleObjectsReturned: get() returned more than one Publisher -- it returned 2! Lookup parameters were {'country': 'U.S.A.'}
如果查询没有返回结果也会抛出异常:
>>> Publisher.objects.get(name="Penguin") Traceback (most recent call last): ... DoesNotExist: Publisher matching query does not exist.
这个 DoesNotExist 异常 是 Publisher 这个 model 类的一个属性,即 Publisher.DoesNotExist。在你的应用中,你可以捕获并处理这个异常,像这样:
try: p = Publisher.objects.get(name='Apress') except Publisher.DoesNotExist: print "Apress isn't in the database yet." else: print "Apress is in the database."
数据排序
在运行前面的例子中,你可能已经注意到返回的结果是无序的。 我们还没有告诉数据库 怎样对结果进行排序,所以我们返回的结果是无序的。
在你的 Django 应用中,你或许希望根据某字段的值对检索结果排序,比如说,按字母顺序。 那么,使用order_by() 这个方法就可以搞定了。
>>> Publisher.objects.order_by("name") [<Publisher: Apress>, <Publisher: O'Reilly>]
跟以前的 all() 例子差不多,SQL语句里多了指定排序的部分:
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name;
我们可以对任意字段进行排序:
>>> Publisher.objects.order_by("address") [<Publisher: O'Reilly>, <Publisher: Apress>] >>> Publisher.objects.order_by("state_province") [<Publisher: Apress>, <Publisher: O'Reilly>]
如果需要以多个字段为标准进行排序(第二个字段会在第一个字段的值相同的情况下被使用到),使用多个参数就可以了,如下
>>> Publisher.objects.order_by("state_province", "address") [<Publisher: Apress>, <Publisher: O'Reilly>]
我们还可以指定逆向排序,在前面加一个减号 - 前缀:
>>> Publisher.objects.order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress>]
尽管很灵活,但是每次都要用 order_by() 显得有点啰嗦。 大多数时间你通常只会对某些 字段进行排序。 在这种情况下,Django让你可以指定模型的缺省排序方式:
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 __unicode__(self): return self.name class Meta: ordering = ['name']
现在,让我们来接触一个新的概念。 class Meta,内嵌于 Publisher 这个类的定义中(如果 class Publisher 是顶格的,那么 class Meta 在它之下要缩进4个空格--按 Python 的传统 )。你可以在任意一个 模型 类中使用Meta 类,来设置一些与特定模型相关的选项。Meta 还可设置很多其它选项,现在,我们关注ordering 这个选项就够了。 如果你设置了这个选项,那么除非你检索时特意额外地使用了 order_by(),否则,当你使用 Django 的数据库 API 去检索时,Publisher对象的相关返回值默认地都会按 name 字段排序。
连锁查询
我们已经知道如何对数据进行过滤和排序。 当然,通常我们需要同时进行过滤和排序查询的操作。 因此,你可以简单地写成这种“链式”的形式:
>>> Publisher.objects.filter(country="U.S.A.").order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress>]
你应该没猜错,转换成SQL查询就是 WHERE 和 ORDER BY 的组合:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = 'U.S.A' ORDER BY name DESC;
限制返回的数据
另一个常用的需求就是取出固定数目的记录。 想象一下你有成千上万的出版商在你的数据库里, 但是你只想显示第一个。 你可以使用标准的Python列表裁剪语句:
>>> Publisher.objects.order_by('name')[0] <Publisher: Apress>
这相当于:
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name LIMIT 1;
类似的,你可以用Python的range-slicing语法来取出数据的特定子集:
>>> Publisher.objects.order_by('name')[0:2]
这个例子返回两个对象,等同于以下的SQL语句:
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name OFFSET 0 LIMIT 2;
注意,不支持Python的负索引(negative slicing):
>>> Publisher.objects.order_by('name')[-1] Traceback (most recent call last): ... AssertionError: Negative indexing is not supported.
虽然不支持负索引,但是我们可以使用其他的方法。 比如,稍微修改 order_by() 语句来实现:
>>> Publisher.objects.order_by('-name')[0]
更新多个对象
在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。
例如说我们现在想要将Apress Publisher的名称由原来的”Apress”更改为”Apress Publishing”。若使用save()方法,如
>>> p = Publisher.objects.get(name='Apress') >>> p.name = 'Apress Publishing' >>> p.save()
这等同于如下SQL语句:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = 'Apress'; UPDATE books_publisher SET name = 'Apress Publishing', address = '2855 Telegraph Ave.', city = 'Berkeley', state_province = 'CA', country = 'U.S.A.', website = 'http://www.apress.com' WHERE id = 52;
(注意在这里我们假设Apress的ID为52)
在这个例子里我们可以看到Django的save()方法更新了不仅仅是name列的值,还有更新了所有的列。 若name以外的列有可能会被其他的进程所改动的情况下,只更改name列显然是更加明智的。 更改某一指定的列,我们可以调用结果集(QuerySet)对象的update()方法: 示例如下:
>>> Publisher.objects.filter(id=52).update(name='Apress Publishing')
与之等同的SQL语句变得更高效,并且不会引起竞态条件。
UPDATE books_publisher SET name = 'Apress Publishing' WHERE id = 52;
update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。 以下示例演示如何将所有Publisher的country字段值由’U.S.A’更改为’USA’:
>>> Publisher.objects.all().update(country='USA')
update()方法会返回一个整型数值,表示受影响的记录条数。 在上面的例子中,这个值是2。
删除对象
删除数据库中的对象只需调用该对象的delete()方法即可:
>>> p = Publisher.objects.get(name="O'Reilly") >>> p.delete() >>> Publisher.objects.all() [<Publisher: Apress Publishing>]
同样我们可以在结果集上调用delete()方法同时删除多条记录。这一点与我们上一小节提到的update()方法相似:
>>> Publisher.objects.filter(country='USA').delete() >>> Publisher.objects.all().delete() >>> Publisher.objects.all() []
删除数据时要谨慎! 为了预防误删除掉某一个表内的所有数据,Django要求在删除表内所有数据时显示使用all()。 比如,下面的操作将会出错:
>>> Publisher.objects.delete() Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: 'Manager' object has no attribute 'delete'
而一旦使用all()方法,所有数据将会被删除:
>>> Publisher.objects.all().delete()
如果只需要删除部分的数据,就不需要调用all()方法。再看一下之前的例子:
>>> Publisher.objects.filter(country='USA').delete()
PS:前面说了那么ORM的语法,最后在来一个一个比较有用的,关联查询.
我们知道,一本书可以有多个作者.
那么我们来差一本书有多少个作者,并列出来
>>> from app01 import models >>> b1 = models.Book.objects.last() #先找到一本书 >>> b1 # 看看这本书 <Book: <学厨师>> >>> b1.authors # 这时返回的是一个对象.涉及到映射其他表.不会直接返回作者对象,必须还要查以下 <django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager object at 0x103d18a58> >>> b1.authors.select_related() # 用select_related()方法进行查询,这个要记住. [<Author: <nanhai old>>]
OK,先会这些简单的就行了.
Django ORM学习总结:
1.我们创建APP项目的时候会在APP目录下默认创建models.py文件,在models.py文件中创建ORM类
2.除了编辑models.py文件,还要在mysite/mysite/settings.py文件里配置激活APP项目
3.settings.py配置文件简单说明.import os
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # BASE_DIR是环境变量的根目录 2 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 3 SECRET_KEY = 'hy8f807k+s-g3h51q(#=2b=vy2g68i#_5w%m32@2lp=--2*oj!' 4 # 是否打开开发模式,如果打开会在页面中展示错误.上生产以后就要改成False 5 DEBUG = True 6 # 当DEBUG 改成 False就意味着上生产了,Django框架当进入生产时,默认拒绝所有,这里把设置程ALLOWED_HOSTS = [*]表示允许所有 7 ALLOWED_HOSTS = [] 8 9 10 # Application definition 11 # 注册我们的app ,这样我们在app目录下的models.py文件配置才会生效,否则报错 12 INSTALLED_APPS = [ 13 'django.contrib.admin', 14 'django.contrib.auth', 15 'django.contrib.contenttypes', 16 'django.contrib.sessions', 17 'django.contrib.messages', 18 'django.contrib.staticfiles', 19 'app01', 20 ] 21 22 # 这个是中间件(全局的),目前用不到.比如说我们做一个策略当一个客户端访问本网站达1秒钟访问次数60次. 23 那么我们就把它拒绝掉.拒绝以后访问请求还未到达urls.py文件就会被拒绝掉. 24 MIDDLEWARE_CLASSES = [ 25 'django.middleware.security.SecurityMiddleware', 26 'django.contrib.sessions.middleware.SessionMiddleware', 27 'django.middleware.common.CommonMiddleware', 28 'django.middleware.csrf.CsrfViewMiddleware', 29 'django.contrib.auth.middleware.AuthenticationMiddleware', 30 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 31 'django.contrib.messages.middleware.MessageMiddleware', 32 'django.middleware.clickjacking.XFrameOptionsMiddleware', 33 ] 34 35 ROOT_URLCONF = 'day16_site.urls' 36 # 配置模版相关 37 TEMPLATES = [ 38 { 39 'BACKEND': 'django.template.backends.django.DjangoTemplates', #告诉Django用什么模版引擎.基本上不改 40 'DIRS': [os.path.join(BASE_DIR,'templates'),'/var/www/html'], #如图可以设置多个,但是一般只有一个 41 'APP_DIRS': True, 42 'OPTIONS': { 43 'context_processors': [ 44 'django.template.context_processors.debug', 45 'django.template.context_processors.request', 46 'django.contrib.auth.context_processors.auth', 47 'django.contrib.messages.context_processors.messages', 48 ], 49 }, 50 }, 51 ] 52 # Django基于wsgi实现,默认的,不用管它 53 WSGI_APPLICATION = 'day16_site.wsgi.application' 54 55 56 # Database 57 # 数据库引擎配置 58 DATABASES = { 59 'default': { 60 # 'ENGINE': 'django.db.backends.sqlite3', 61 # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 62 'ENGINE': 'django.db.backends.mysql', 63 'NAME':'s12day16', 64 'HOST':'127.0.0.1', 65 'PORT':'3307', 66 'USER':'root', 67 'PASSWORD':'123456' 68 } 69 } 70 71 72 # Password validation 73 # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators 74 # 这是1.9里新加的,1.8里没有,密码验证.忘记它把. 75 AUTH_PASSWORD_VALIDATORS = [ 76 { 77 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 78 }, 79 { 80 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 81 }, 82 { 83 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 84 }, 85 { 86 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 87 }, 88 ] 89 90 91 # Internationalization 92 # https://docs.djangoproject.com/en/1.9/topics/i18n/ 93 94 # 默认的语言 95 LANGUAGE_CODE = 'en-us' 96 # 时区 97 TIME_ZONE = 'UTC' 98 99 USE_I18N = True 100 101 USE_L10N = True 102 103 USE_TZ = True 104 105 106 # Static files (CSS, JavaScript, Images) 107 # https://docs.djangoproject.com/en/1.9/howto/static-files/ 108 109 # 配置css,js以及其他html静态文件. 110 STATIC_URL = '/static/'
4.配置app项目下的models.py
那作者出版社书 这个实例来做这个项目最合适了.
书写三个表的orm类,一个ORM类就是一张表.
要注意的是 书和 作者是多对多的关系.书和出版社是多对一的关系
关系一旦建立,在生成表的时候就会多一张关系表.书本id , 用户id
mysql> select * from app01_book_authors; +----+---------+-----------+ | id | book_id | author_id | +----+---------+-----------+ | 1 | 1 | 1 | | 2 | 2 | 2 | +----+---------+-----------+
而一对多关系,不会出现一个关系表,只会在建立外键的表上
mysql> select * from app01_book; +----+-----------+------------------+--------------+ | id | title | publication_date | publisher_id | +----+-----------+------------------+--------------+ | 1 | 学python | 2016-08-08 | 1 | | 2 | 学厨师 | 2016-08-08 | 2 | +----+-----------+------------------+--------------+
5. models.py文件配置好后,使用命令创建表.
$ python manage.py makemigrations #生成同步纪录
$ python manage.py migrate #开始同步
6. 使用Django自带的后台管理我们app项目的数据库
1. 在app项目/admin.py文件里注册我们为改项目创建的orm类
from django.contrib import admin # Register your models here. from app01 import models #引入我们之前写的models admin.site.register(models.Author) #注册Author的ORM类 admin.site.register(models.Book) # 注册Book ORM类 admin.site.register(models.Publisher) # 注册Publisher ORM类
此时就可以访问http://127.0.0.1:8000/admin/
2.为admin后台创建管理员账户
python3.5 manage.py createsuperuser
账户创建完成后,我们就可以访问http://127.0.0.1:8000/admin/,输入用户名和密码登陆到后台管理系统了.
3.使用后台管理系统,管理注册到admin文件的ORM类.
7. 实际项目中使用命令行方式管理ORM类才是最常用的.
增
删
改
查
...
本节总结下来就是上面的内容