Django搭建简易博客教程(01)-Django简介

写做目的

喜欢一个学习观点以教促学, 一直以来, 学习的时候常常会发现, 某个方法某个问题本身已经明白了, 可是在教给别人的时候确说不清楚, 因此慢慢的学会了以教促学这种方法, 在教给别人知识的同时也可以提高本身对语言, 对框架的理解.php

但愿达到的目标:html

  • 但愿能写出一个系列文章, 我也不知道到底能写多少
  • 可以让认真阅读这个系列的文章的人, 能在读完以后作出一个简单的博客
  • 但愿可以加深本身对Django的理解

Django简介

Django是Python中目前风靡的Web Framework, 那么什么叫作Framework呢, 框架可以帮助你把程序的总体架构搭建好, 而咱们所须要作的工做就是填写逻辑, 而框架可以在合适的时候调用你写的逻辑, 而不须要咱们本身去调用逻辑, 让Web开发变的更敏捷.python

Django是一个高级Python Web框架, 鼓励快速,简洁, 以程序设计的思想进行开发. 经过使用这个框架, 能够减小不少开发麻烦, 使你更专一于编写本身的app, 而不须要重复造轮子. Django免费而且开源.mysql

Django特色:

  • 彻底免费并开源源代码
  • 快速高效开发
  • 使用MTV架构(熟悉Web开发的应该会说是MVC架构)
  • 强大的可扩展性.

Django的工做方式:

用户在浏览器中输入URL后的回车, 浏览器会对URL进行检查, 首先判断协议,若是是http就按照 Web 来处理, 然互调用DNS查询, 将域名转换为IP地址, 而后通过网络传输到达对应Web服务器, 服务器对url进行解析后, 调用View中的逻辑(MTV中的V), 其中又涉及到Model(MTV中的M), 与数据库的进行交互, 将数据发到Template(MTV中的T)进行渲染, 而后发送到浏览器中, 浏览器以合适的方式呈现给用户nginx

但愿到这里你能够大致的了解Django 的工做方式,若是还不太明白也没有关系,到后面咱们会详细的了解的,截止到我这这篇教程,市场上用python 2.X 和 python 3.X 的在人数方面大致持平了,因此,当你看到这个地方的时候还可能有一个小困惑,那就是:我到底应该学习什么版本的python呢?!图样图森破。。。记住答案是 python3 。听哥的没错!!git

可是学习 Django ,这对于任务时间刚开始学习的人,都有一个必须搞清楚的问题。根据官网信息:程序员

根据官网信息:(2018-03-05)web

Django 版本 python 信息
1.8 2.7, 3.2 (until the end of 2016), 3.3, 3.4, 3.5
1.9 1.10 2.7, 3.4, 3.5
1.11 2.7, 3.4, 3.5, 3.6
2.0 3.4, 3.5, 3.6
2.1 3.5, 3.6, 3.7
重点是2017年末发布的2.0版将再也不支持Python2!
因此,若是你的工做环境不强制指定版本,那么不管是从Python学习仍是Django学习而言,Python3都是你最好的选择!
  • Django makes it easier to build better Web apps more quickly and with less code.正则表达式

  • Django 让你以更快的速度、更少的代码、更轻松的方式搭建更好的 Web 应用。sql

本教程将带你使用 Django 快速开发属于本身的博客网站。

Django的流程和命令行工具(正式内容下节开始)

Django的实现流程:

  1. 安装Django
  • 命令:pip install django
  • 添加环境变量;
  1. 建立project(项目名字:mysite)
  • 命令:django-admin startproject mysite
  1. 建立APP(app名字:app01)
  • python manage.py startapp app01
  1. setting配置:
  • 在 INSTALLED_APPS 中注册app01;
  1. 编写逻辑与路由 url.py ;view.py

  2. 使用模板
  • render(req,"index.html")
  1. 启动项目
  • 命令:python manage.py runserver

以上为最最基本的操做,因此,没有涉及model.py数据库的操做。

Django的命令行工具

django-admin.py是Django的一个用于管理任务的命令行工具,manage.py是对django-admin.py的简单包装,每一Django Project里都会有一个mannage.py。

<1> 建立一个django工程 :

django-admin.py startproject mysite

当前目录下会生成mysite的工程,目录结构以下:

  • manage.py ---- Django项目里面的工具,经过它能够调用django shell和数据库等。
  • settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其余一些工做的变量。
  • urls.py ---- 负责把URL模式映射到应用程序。

在mysite目录下建立blog应用:

python manage.py startapp blog

<3>启动django项目:

python manage.py runserver 8080

这样咱们的django就启动起来了!当咱们访问:http://127.0.0.1:8080/ 时就能够看到:

<4>同步数据库

python manage.py makemigrations  # 生成同步数据库的脚本
python manage.py migrate  # 同步数据库

注意:在开发过程当中,数据库同步误操做以后,不免会遇到后面不能同步成功的状况,解决这个问题的一个简单粗暴方法是把migrations目录下的脚本(除__init__.py以外)所有删掉,再把数据库删掉以后建立一个新的数据库,数据库同步操做再从新作一遍。

 这个时候当咱们访问http://127.0.0.1:8080/admin/ 时,会出现:

<5>建立超级用户

执行命令:

python manage.py createsuperuser
python manage.py changepassword username  # 修改用户名密码

设置好用户名密码就可使用后台了。

<6>清空数据库:

python manage.py flush

<7>启动交互界面 :

python manage.py shell

这个命令和直接运行 python 进入 shell 的区别是:你能够在这个 shell 里面调用当前项目的 models.py 中的 API,对于操做数据,还有一些小测试很是方便。

<9> 终端上输入python manage.py 能够看到详细的列表,在忘记子名称的时候特别有用。

:fa-gears: Django的配置文件(settings)

:fa-map-marker: 静态文件交由web服务器处理,Django自己不处理静态文件。简单的处理逻辑以下:(以nginx为例)

  • 若是是静态文件,则由nginx直接处理;
  • 若是不是,则交由Django处理,Django根据urls.py里面的规则进行匹配;

以上是部署到web服务器后的处理方式。

为了便于开发,Django提供了在开发环境中对静态文件的处理机制,方法是这样:

  • 在INSTALLED_APPS里面加入django.contrib.staticfiles,
  • 在urls.py里面加入:
if settings.DEBUG:  
           urlpatterns += patterns('', url(r'^media/(?P<path>.*)$', 
           'django.views.static.serve', {'document_root': settings.MEDIA_ROOT }),   
            url(r'^static/(?P<path>.*)$',
          'django.views.static.serve',{'document_root':settings.STATIC_ROOT}), )
  • 这样就能够直接在开发阶段直接使用静态文件了。

Django URL (路由系统)

URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。

其形式为:

urlpatterns = [
    url(正则表达式, views视图函数,参数,别名),     # 注意是中括号
]

参数说明:

  • 一个正则表达式字符串
  • 一个可调用对象,一般为一个视图函数或一个指定视图函数路径的字符串
  • 可选的要传递给视图函数的默认参数(字典形式)
  • 一个可选的name参数

Here’s a sample URLconf:

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

from app01 import views

urlpatterns = [

    url(r'^articles/2003/$', views.special_case_2003),

    #url(r'^articles/[0-9]{4}/$', views.year_archive),

    url(r'^articles/([0-9]{4})/$', views.year_archive),  #no_named group

    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),

]

升级版----命名分组:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]

再升级版----Including other URLconfs

from django.conf.urls import include, url

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

再再升级版----name param(设置别名)

urlpatterns = [
    url(r'^index', include('blog.urls'), name='bieming'),
]


#####################
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
     <form action="{% url 'bieming' %}" method="post">
         用户名:<input type="text" name="username">
         密码:<input type="password" name="password">
         <input type="submit" value="submit">
     </form>
</body>
</html>
#######################

Django Views(视图函数)

http请求中产生两个核心对象:

  • http请求:HttpRequest对象
  • http响应:HttpResponse对象

所在位置:django.http

以前咱们用到的参数request就是HttpRequest
检测方法:isinstance(request,HttpRequest)

HttpRequest对象的属性和方法:

  • path:请求页面的全路径,不包括域名

  • method:请求中使用的HTTP方法的字符串表示。全大写表示。例如
if req.method == "GET":
    do_something()
elif req.method=="POST":
    do_something_else()
  • GET:包含全部HTTP GET参数的类字典对象;

  • POST:包含全部HTTP POST参数的类字典对象

    服务器收到空的POST请求的状况也是可能发生的,也就是说,表单form经过HTTP POST方法提交请求,可是表单中可能没有数据,所以不能使用if req.POST来判断是否使用了HTTP POST 方法;应该使用 if req.method=="POST"

  • COOKIES: 包含全部cookies的标准Python字典对象;keys和values都是字符串。

  • FILES: 包含全部上传文件的类字典对象;FILES中的每个Key都是<input type="file" name="" />标签中name属性的值,FILES中的每个value同时也是一个标准的python字典对象,包含下面三个Keys:
  • filename: 上传文件名,用字符串表示
  • content_type: 上传文件的Content Type
  • content: 上传文件的原始内容

  • user: 是一个django.contrib.auth.models.User对象,表明当前登录的用户。若是访问用户当前没有登录,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你能够经过user的is_authenticated()方法来辨别用户是否登录:if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware时该属性才可用

  • session : 惟一可读写的属性,表明当前会话的字典对象;本身有激活Django中的session支持时该属性才可用。

  • 注意一个经常使用方法:request.POST.getlist('')

HttpResponse对象:

对于HttpRequest对象来讲,是由django自动建立的,可是,HttpResponse对象就必须咱们本身建立。每一个view请求处理方法必须返回一个HttpResponse对象。

HttpResponse类在django.http.HttpResponse

在HttpResponse对象上扩展的经常使用方法:

  • 页面渲染: render()(推荐) render_to_response(),
  • 页面跳转: redirect("路径")
  • locals(): 能够直接将函数中全部的变量传给模板

Template基础

模板系统的介绍

你可能已经注意到咱们在例子视图中返回文本的方式有点特别。 也就是说,HTML被直接硬编码在 Python代码之中:

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now  # 这种写法low 爆了
    return HttpResponse(html)

尽管这种技术便于解释视图是如何工做的,但直接将HTML硬编码到你的视图里却并非一个好主意。 让咱们来看一下为何:

  • 对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改每每比底层 Python 代码的修改要频繁得多,所以若是能够在不进行 Python 代码修改的状况下变动设计,那将会方便得多。

  • Python 代码编写和 HTML 设计是两项不一样的工做,大多数专业的网站开发环境都将他们分配给不一样的人员(甚至不一样部门)来完成。 设计者和HTML/CSS的编码人员不该该被要求去编辑Python的代码来完成他们的工做。

  • 程序员编写 Python代码和设计人员制做模板两项工做同时进行的效率是最高的,远胜于让一我的等待另外一我的完成对某个既包含 Python又包含 HTML 的文件的编辑工做。

基于这些缘由,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 咱们可使用 Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。

: 模板语法

1. 模版的组成:

组成:HTML代码+逻辑控制代码

2. 逻辑控制代码的组成

2.1 变量(使用双大括号来引用变量):
{{var_name}}    # 语法格式
2.1.1 Template和Context对象
>>> python manange.py shell  (进入该django项目的环境)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.'


# 同一模板,多个上下文,一旦有了模板对象,你就能够经过它渲染多个context,不管什么时候咱们均可以
# 像这样使用同一模板源渲染多个context,只进行 一次模板建立而后屡次调用render()方法渲染会
# 更为高效:
# Low
for name in ('John', 'Julie', 'Pat'):
    t = Template('Hello, {{ name }}')
    print t.render(Context({'name': name}))

# Good
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
    print t.render(Context({'name': name}))

Django 模板解析很是快捷。 大部分的解析工做都是在后台经过对简短正则表达式一次性调用来完成。 这和基于 XML 的模板引擎造成鲜明对比,那些引擎承担了 XML 解析器的开销,且每每比 Django 模板渲染引擎要慢上几个数量级。

from django.shortcuts import render,HttpResponse
from django.template.loader import get_template #记得导入
# Create your views here.


import datetime
from django.template import Template,Context

# def current_time(req):
    #原始的视图函数
    # now=datetime.datetime.now()
    # html="<html><body>如今时刻:<h1>%s.</h1></body></html>" %now
    # return HttpResponse(html)



# def current_time(req):

#     django模板修改的视图函数
#     now=datetime.datetime.now()
#     t=Template('<html><body>如今时刻是:<h1 style="color:red">{{current_date}}</h1></body></html>')
#     t=get_template('current_datetime.html')
#     c=Context({'current_date':now})
#     html=t.render(c)
#     return HttpResponse(html)

#另外一种写法(推荐)

def current_time(req):

    now=datetime.datetime.now()

    return render(req, 'current_datetime.html', {'current_date':now})
2.1.2 深度变量的查找(万能的句点号)

在到目前为止的例子中,咱们经过 context 传递的简单参数值主要是字符串,然而,模板系统可以很是简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。

在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)。

#最好是用几个例子来讲明一下。
# 首先,句点可用于访问列表索引,例如:

>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
'Item 2 is carrots.'

#假设你要向模板传递一个 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)
'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
>>> d.month
>>> d.day
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'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)
'Hello, John Smith.'

# 点语法也能够用来引用对象的方法。 例如,每一个 Python 字符串都有 upper() 和 isdigit()
# 方法,你在模板中可使用一样的句点语法来调用它们:
>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True'

# 注意这里调用方法时并* 没有* 使用圆括号 并且也没法给该方法传递参数;你只能调用不需参数的方法。
2.1.3 变量的过滤器(filter)的使用
{{obj|filter:param}}   # 语法格式
# 1  add          :   给变量加上相应的值
#
# 2  addslashes   :    给变量中的引号前加上斜线
#
# 3  capfirst     :    首字母大写
#
# 4  cut          :   从字符串中移除指定的字符
#
# 5  date         :   格式化日期字符串
#
# 6  default      :   若是值是False,就替换成设置的默认值,不然就是用原本的值
#
# 7  default_if_none:  若是值是None,就替换成设置的默认值,不然就使用原本的值


#实例:

#value1="aBcDe"
{{ value1|upper }}

#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 lowenve'
2.2 标签(tag)的使用(使用大括号和百分比的组合来表示使用tag)
{% tags %}
2.2.1 {% if %} 的使用

{% 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 %}
2.2.2 {% for %}的使用

{% for %}标签容许你按顺序遍历一个序列中的各个元素,每次循环模板系统都会渲染{% for %}{% endfor %}之间的全部内容.

<ul>
{% for obj in list %}
    <li>{{ obj.name }}</li>
{% endfor %}
</ul>


#在标签里添加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
2.2.3 {%csrf_token%}:csrf_token标签

用于生成csrf_token的标签,用于防治跨站攻击验证。注意若是你在view的index里用的是render_to_response方法,不会生效.

其实,这里是会生成一个input标签,和其余表单标签一块儿提交给后台的。

2.2.4 {% url %}: 引用路由配置的地址
<form action="{% url "bieming"%}" >
          <input type="text">
          <input type="submit"value="提交">
          {%csrf_token%}
</form>
2.2.5 {% with %}:用更简单的变量名替代复杂的变量名
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
2.2.6 {% verbatim %}: 禁止render
{% verbatim %}
    {{ hello }}
{% endverbatim %}
2.2.7 {% load %}: 加载标签库
2.3 自定义filter和simple_tag
  • 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 %}
2.4 extend模板继承
2.4.1 include 模板标签

在讲解了模板加载机制以后,咱们再介绍一个利用该机制的内建模板标签: {% include %} 。该标签容许在(模板中)包含其它的模板的内容。 标签的参数是所要包含的模板名称,能够是一个变量,也能够是用单/双引号硬编码的字符串。 每当在多个模板中出现相同的代码时,就应该考虑是否要使用{% include %} 来减小重复。

2.4.2 extend(继承)模板标签

到目前为止,咱们的模板范例都只是些零星的 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 %} 标签中的内容老是被看成一条退路。继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板均可以访问到你传到模板中的每个模板变量。你能够根据须要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:
  • <1> 建立 base.html 模板,在其中定义站点的主要外观感觉。 这些都是不常修改甚至从不修改的部分。
  • <2> 为网站的每一个区域建立 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。这些模板对base.html 进行拓展,并包含区域特定的风格与设计。
  • <3> 为每种类型的页面建立独立的模板,例如论坛页面或者图片库。 这些模板拓展相应的区域模板。

这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工做。

如下是使用模板继承的一些诀窍:

  • 若是在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 不然,模板继承将不起做用。

  • 通常来讲,基础模板中的 {% block %} 标签越多越好。 记住,子模板没必要定义父模板中全部的代码块,所以你能够用合理的缺省值对一些代码块进行填充,而后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。

  • 若是发觉本身在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。若是你须要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。 若是只想在上级代码块基础上添加内容,而不是所有重载,该变量就显得很是有用了。

  • 不容许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是由于block 标签的工做方式是双向的。也就是说,block 标签不只挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。若是模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪一个块的内容。

:fa-institution: Models

:tw-1f338:数据库的配置

1. django默认支持sqlite,mysql, oracle,postgresql数据库。

  • sqlite
  • django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3

  • mysql
  • 引擎名称:django.db.backends.mysql

2. mysql驱动程序

  • MySQLdb(mysql python)
  • mysqlclient
  • MySQL
  • PyMySQL(纯python的mysql驱动程序)

3. 在django的项目中会默认使用sqlite数据库,在settings里有以下设置:

若是咱们想要更改数据库,须要修改以下:

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()

问题解决!

ORM(对象关系映射)

用于实现面向对象编程语言里不一样类型系统的数据之间的转换,换言之,就是用面向对象的方式去操做数据库的建立表以及增删改查等操做。

优势:

  • ORM使得咱们的通用数据库交互变得简单易行,并且彻底不用考虑该死的SQL语句。快速开发,由此而来。

  • 能够避免一些新手程序猿写sql语句带来的性能问题。

好比 咱们查询User表中的全部字段:

新手可能会用select * from auth_user,这样会由于多了一个匹配动做而影响效率的。

缺点:

  • 性能有所牺牲,不过如今的各类ORM框架都在尝试各类方法,好比缓存,延迟加载登来减轻这个问题。效果很显著。

  • 对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM通常也支持写raw sql。

  • 经过QuerySet的query属性查询对应操做的sql语句

author_obj=models.Author.objects.filter(id=2)
print(author_obj.query)

下面要开始学习Django ORM语法了,为了更好的理解,咱们来作一个基本的 书籍/做者/出版商 数据库结构。 咱们这样作是由于 这是一个众所周知的例子,不少SQL有关的书籍也经常使用这个举例。

:tw-1f351: 表(模型)的建立:

实例:咱们来假定下面这些概念,字段和关系

做者模型:一个做者有姓名。

做者详细模型:把做者的详情放到详情表,包含性别,email地址和出生日期,做者详情模型和做者模型之间是一对一的关系(one-to-one)(相似于每一个人和他的身份证之间的关系),在大多数状况下咱们没有必要将他们拆分红两张表,这里只是引出一对一的概念。

出版商模型:出版商有名称,地址,所在城市,省,国家和网站。

书籍模型:书籍有书名和出版日期,一本书可能会有多个做者,一个做者也能够写多本书,因此做者和书籍的关系就是多对多的关联关系(many-to-many),一本书只应该由一个出版商出版,因此出版商和书籍是一对多关联关系(one-to-many),也被称做外键。

from django.db import models<br>
class Publisher(models.Model):
    name = models.CharField(max_length=30, verbose_name="名称")
    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 Meta:
        verbose_name = '出版商'
        verbose_name_plural = verbose_name
 
    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
  • 注意1:记得在settings里的INSTALLED_APPS中加入'app01',而后再同步数据库。
  • 注意2: models.ForeignKey("Publish") & models.ForeignKey(Publish)

:tw-1f34e: 分析代码:

  • 每一个数据模型都是django.db.models.Model的子类,它的父类Model包含了全部必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。

  • 每一个模型至关于单个数据库表(多对多关系例外,会多生成一张关系表),每一个属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)至关于数据库的字段类型(例如varchar)。你们能够留意下其它的类型都和数据库里的什么字段对应。

  • 模型之间的三种关系:一对一,一对多,多对多。
  • 一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;
  • 一对多:就是主外键关系;(foreign key)
  • 多对多:(ManyToManyField) 自动建立第三张表(固然咱们也能够本身建立第三张表:两个foreign key)

模型经常使用的字段类型参数

<1> CharField
    #字符串字段, 用于较短的字符串.
    #CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所容许的最大字符数.

<2> IntegerField
    #用于保存一个整数.

<3> FloatField
    # 一个浮点数. 必须 提供两个参数:
    #
    # 参数    描述
    # max_digits    总位数(不包括小数点和符号)
    # decimal_places    小数位数
        # 举例来讲, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段:
        #
        # models.FloatField(..., max_digits=5, decimal_places=2)
        # 要保存最大值一百万(小数点后保存10位)的话,你要这样定义:
        #
        # models.FloatField(..., max_digits=19, decimal_places=10)
        # admin 用一个文本框(<input type="text">)表示该字段保存的数据.

<4> AutoField
    # 一个 IntegerField, 添加记录时它会自动增加. 你一般不须要直接使用这个字段; 
    # 自定义一个主键:my_id=models.AutoField(primary_key=True)
    # 若是你不指定主键的话,系统会自动添加一个主键字段到你的 model.

<5> BooleanField
    # A true/false field. admin 用 checkbox 来表示此类字段.

<6> TextField
    # 一个容量很大的文本字段.
    # admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框).

<7> EmailField
    # 一个带有检查Email合法性的 CharField,不接受 maxlength 参数.

<8> DateField
    # 一个日期字段. 共有下列额外的可选参数:
    # Argument    描述
    # auto_now    当对象被保存时,自动将该字段的值设置为当前时间.一般用于表示 "last-modified" 时间戳.
    # auto_now_add    当对象首次被建立时,自动将该字段的值设置为当前时间.一般用于表示对象建立时间.
    #(仅仅在admin中有意义...)

<9> DateTimeField
    #  一个日期时间字段. 相似 DateField 支持一样的附加选项.

<10> ImageField
    # 相似 FileField, 不过要校验上传对象是不是一个合法图片.#它有两个可选参数:height_field和width_field,
    # 若是提供这两个参数,则图片将按提供的高度和宽度规格保存.     
<11> FileField
    # 一个文件上传字段.
     #要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime #formatting, 
    #该格式将被上载文件的 date/time 
    #替换(so that uploaded files don't fill up the given directory).
    # admin 用一个<input type="file">部件表示该字段保存的数据(一个文件上传部件) .

    #注意:在一个 model 中使用 FileField 或 ImageField 须要如下步骤:
    #(1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件.
    # (出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 做为该目录的公共 URL. 要确保该目录对
    #  WEB服务器用户账号是可写的.
    #(2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django
    # 使用 MEDIA_ROOT 的哪一个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). 
    # 出于习惯你必定很想使用 Django 提供的 get_<#fieldname>_url 函数.举例来讲,若是你的 ImageField 
    # 叫做 mug_shot, 你就能够在模板中以 {{ object.#get_mug_shot_url }} 这样的方式获得图像的绝对路径.

<12> URLField
    # 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且
    # 没有返回404响应).
    # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框)

<13> NullBooleanField
    # 相似 BooleanField, 不过容许 NULL 做为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项
    # admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据.

<14> SlugField
    # "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.#它们一般用于URLs
    # 若你使用 Django 开发版本,你能够指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50.  #在
    # 之前的 Django 版本,没有任何办法改变50 这个长度.
    # 这暗示了 db_index=True.
    # 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-#populate 
    # the slug, via JavaScript,in the object's admin form: models.SlugField
    # (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields.

<13> XMLField
    #一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema #的文件系统路径.

<14> FilePathField
    # 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的.
    # 参数    描述
    # path    必需参数. 一个目录的绝对文件系统路径. FilePathField 据此获得可选项目. 
    # Example: "/home/images".
    # match    可选参数. 一个正则表达式, 做为一个字符串, FilePathField 将使用它过滤文件名.  
    # 注意这个正则表达式只会应用到 base filename 而不是
    # 路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif.
    # recursive可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的所有子目录.
    # 这三个参数能够同时使用.
    # match 仅应用于 base filename, 而不是路径全名. 那么,这个例子:
    # FilePathField(path="/home/images", match="foo.*", recursive=True)
    # ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif

<15> IPAddressField
    # 一个字符串形式的 IP 地址, (i.e. "24.124.1.30").
<16># CommaSeparatedIntegerField
    # 用于存放逗号分隔的整数值. 相似 CharField, 必需要有maxlength参数.

:tw-1f349: Field重要参数

<1> null : 数据库中字段是否能够为空

<2> blank: django的 Admin 中添加数据时是否可容许空值

<3> default:设定缺省值

<4> editable:若是为假,admin模式下将不能改写。缺省为真

<5> primary_key:设置主键,若是没有设置django建立表时会自动加上:
    id = meta.AutoField('ID', primary_key=True)
    primary_key=True implies blank=False, null=False and unique=True. Only one
    primary key is allowed on an object.

<6> unique:数据惟一

<7> verbose_name  Admin中字段的显示名称

<8> validator_list:有效性检查。非有效产生 django.core.validators.ValidationError 错误


<9> db_column,db_index 若是为真将为此字段建立索引

<10>choices:一个用来选择值的2维元组。第一个值是实际存储的值,第二个用来方便进行选择。
    如SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),)
    gender = models.CharField(max_length=2,choices = SEX_CHOICES)
:tw-1f485: 表的操做(增删改查):

:tw-2795: 增(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()):

    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)

#注意: 若是第三张表是经过models.ManyToManyField()自动建立的,那么绑定关系只有上面一种方式
#     若是第三张表是本身建立的:
     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()

:tw-2796: 删(delete)

>>> Book.objects.filter(id=1).delete()
(3, {'app01.Book_authors': 2, 'app01.Book': 1})

咱们表面上删除了一条信息,实际却删除了三条,由于咱们删除的这本书在Book_authors表中有两条相关信息,这种删除方式就是django默认的级联删除。

若是是多对多的关系: remove()和clear()方法:

#正向
book = models.Book.objects.filter(id=1)

#删除第三张表中和女孩1关联的全部关联信息
book.author.clear()        #清空与book中id=1 关联的全部数据
book.author.remove(2)  #能够为id
book.author.remove(*[1,2,3,4])     #能够为列表,前面加*

#反向
author = models.Author.objects.filter(id=1)
author.book_set.clear() #清空与boy中id=1 关联的全部数据

改(update和save)

实例:

注意

  • 第二种方式修改不能用get的缘由是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象(filter里面的条件可能有多个条件符合,好比name='alvin',可能有两个name='alvin'的行数据)。

  • 在“插入和更新数据”小节中,咱们有提到模型的save()方法,这个方法会更新一行里的全部列。 而某些状况下,咱们只须要更新行里的某几列。

#---------------- update方法直接设定对应属性----------------
    models.Book.objects.filter(id=3).update(title="PHP")
    ##sql:
    ##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3)


#--------------- save方法会将全部属性从新设定一遍,效率低-----------
    obj=models.Book.objects.filter(id=3)[0]
    obj.title="Python"
    obj.save()
# SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price", 
# "app01_book"."color", "app01_book"."page_num", 
# "app01_book"."publisher_id" FROM "app01_book" WHERE "app01_book"."id" = 3 LIMIT 1; 
# 
# UPDATE "app01_book" SET "title" = 'Python', "price" = 3333, "color" = 'red', "page_num" = 556,
# "publisher_id" = 1 WHERE "app01_book"."id" = 3;

在这个例子里咱们能够看到Django的save()方法更新了不只仅是title列的值,还有更新了全部的列。 若title之外的列有可能会被其余的进程所改动的状况下,只更改title列显然是更加明智的。更改某一指定的列,咱们能够调用结果集(QuerySet)对象的update()方法,与之等同的SQL语句变得更高效,而且不会引发竞态条件。

此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你能够同时更新多条记录update()方法会返回一个整型数值,表示受影响的记录条数。

注意,这里由于update返回的是一个整形,因此无法用query属性;对于每次建立一个对象,想显示对应的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',
        },
    }
}

LOGGING

注意:若是是多对多的改:

obj=Book.objects.filter(id=1)[0]
author=Author.objects.filter(id__gt=2)

obj.author.clear()
obj.author.add(*author)

查(filter,value等)

# 查询相关API:

#  <1>filter(**kwargs):      它包含了与所给筛选条件相匹配的对象

#  <2>all():                 查询全部结果

#  <3>get(**kwargs):         返回与所给筛选条件相匹配的对象,返回结果有且只有一个,若是符合筛选条件的对象超过一个或者没有都会抛出错误。

#-----------下面的方法都是对查询的结果再进行处理:好比 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。

补充:

#扩展查询,有时候DJANGO的查询API不能方便的设置查询条件,提供了另外的扩展查询方法extra:
#extra(select=None, where=None, params=None, tables=None,order_by=None, select_params=None

(1)  Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
(2)  Blog.objects.extra(
        select=SortedDict([('a', '%s'), ('b', '%s')]),
        select_params=('one', 'two'))

(3)  q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
     q = q.extra(order_by = ['-is_recent'])

(4)  Entry.objects.extra(where=['headline=%s'], params=['Lennon'])  

extra

惰性机制:
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会立刻执行sql,而是当调用QuerySet的时候才执行。

QuerySet特色:

  • 可迭代的
  • 可切片
#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,可能
会形成额外的数据库查询。

对象查询,单表条件查询,多表条件关联查询

#--------------------对象形式的查找--------------------------
    # 正向查找
    ret1=models.Book.objects.first()
    print(ret1.title)
    print(ret1.price)
    print(ret1.publisher)
    print(ret1.publisher.name)  #由于一对多的关系因此ret1.publisher是一个对象,而不是一个queryset集合

    # 反向查找
    ret2=models.Publish.objects.last()
    print(ret2.name)
    print(ret2.city)
    #如何拿到与它绑定的Book对象呢?
    print(ret2.book_set.all()) #ret2.book_set是一个queryset集合

#---------------了不得的双下划线(__)之单表条件查询----------------

#    models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
#
#    models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于十一、2二、33的数据
#    models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
#
#    models.Tb1.objects.filter(name__contains="ven")
#    models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
#
#    models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and
#
#    startswith,istartswith, endswith, iendswith,

#----------------了不得的双下划线(__)之多表条件关联查询---------------

# 正向查找(条件)

#     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
    #一对多和多对多在这里用法没区别

注意:条件查询即与对象查询对应,是指在filter,values等方法中的经过__来明确查询条件。

聚合查询和分组查询

**<1> aggregate(*args,kwargs):

经过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。

from django.db.models import Avg,Min,Sum,Max

从整个查询集生成统计值。好比,你想要计算全部在售书的平均价钱。Django的查询语法提供了一种方式描述全部
图书的集合。

>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

aggregate()子句的参数描述了咱们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值

aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的
标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。若是你想要为聚合值指定
一个名称,能够向聚合子句提供它:
>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}


若是你也想知道全部图书价格的最大值和最小值,能够这样查询:
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

**<2> annotate(*args,kwargs):
能够经过计算查询结果中每个对象所关联的对象集合,从而得出总计值(也能够是平均值或总和),即为查询集的每一项生成聚合。

查询alex出的书总价格

查询各个做者出的书的总价格,这里就涉及到分组了,分组条件是authors__name

查询各个出版社最便宜的书价是多少

F查询和Q查询

仅仅靠单一的关键字参数查询已经很难知足查询要求。此时Django为咱们提供了F和Q查询:

# F 使用查询条件的值,专门取对象中某列值的操做

    # from django.db.models import F
    # models.Tb1.objects.update(num=F('num')+1)


# Q 构建搜索条件
    from django.db.models import Q

    #1 Q对象(django.db.models.Q)能够对关键字参数进行封装,从而更好地应用多个查询
    q1=models.Book.objects.filter(Q(title__startswith='P')).all()
    print(q1)#[<Book: Python>, <Book: Perl>]

    # 二、能够组合使用&,|操做符,当一个操做符是用于两个Q的对象,它产生一个新的Q对象。
    Q(title__startswith='P') | Q(title__startswith='J')

    # 三、Q对象能够用~操做符放在前面表示否认,也可容许否认与不否认形式的组合
    Q(title__startswith='P') | ~Q(pub_date__year=2005)

    # 四、应用范围:

    # Each lookup function that takes keyword-arguments (e.g. filter(),
    #  exclude(), get()) can also be passed one or more Q objects as
    # positional (not-named) arguments. If you provide multiple Q object
    # arguments to a lookup function, the arguments will be “AND”ed
    # together. For example:

    Book.objects.get(
        Q(title__startswith='P'),
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
    )

    #sql:
    # SELECT * from polls WHERE question LIKE 'P%'
    #     AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

    # import datetime
    # e=datetime.date(2005,5,6)  #2005-05-06

    # 五、Q对象能够与关键字参数查询一块儿使用,不过必定要把Q对象放在关键字参数查询的前面。
    # 正确:
    Book.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
        title__startswith='P')
    # 错误:
    Book.objects.get(
        question__startswith='P',
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

raw sql

django中models的操做,也是调用了ORM框架来实现的,pymysql 或者mysqldb,因此咱们也可使用原生的SQL语句来操做数据库!

admin的配置

admin是django强大功能之一,它能共从数据库中读取数据,呈如今页面中,进行管理。默认状况下,它的功能已经很是强大,若是你不须要复杂的功能,它已经够用,可是有时候,一些特殊的功能还须要定制,好比搜索功能,下面这一系列文章就逐步深刻介绍如何定制适合本身的admin应用。

若是你以为英文界面很差用,能够在setting.py 文件中修改如下选项

LANGUAGE_CODE = 'en-us'  #LANGUAGE_CODE = 'zh-hans'

认识ModelAdmin

管理界面的定制类,如需扩展特定的model界面需从该类继承。

注册medel类到admin的两种方式:

  • 使用register的方法
admin.site.register(Book,MyAdmin)
  • 使用register的装饰器
@admin.register(Book)

三 掌握一些经常使用的设置技巧

  • list_display: 指定要显示的字段
  • search_fields: 指定搜索的字段
  • list_filter: 指定列表过滤器
  • 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)

部份内容来自于:https://www.cnblogs.com/yuanchenqi/articles/6083427.html

相关文章
相关标签/搜索