Django基础--Django基本命令、路由配置系统(URLconf)、编写视图、Template、数据库与ORM

web框架

框架,即framework,特指为解决一个开放性问题而设计的具备必定约束性的支撑结构。php

使用框架能够帮你快速开发特定的系统。html

简单地说,就是你用别人搭建好的舞台来作表演。前端

尝试搭建一个简单的web框架:python

由于咱们不但愿接触到TCP链接、HTTP原始请求和响应格式,因此,须要一个统一的接口,让咱们专心用Python编写Web业务。mysql

这个接口就是WSGI:Web Server Gateway Interface。jquery

#---------------------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>

 注意:git

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。

 

MVC和MTV模式

MVC

  • 大部分开发语言中都有MVC框架
  • MVC框架的核心思想是:解耦
  • 下降各功能模块之间的耦合性,方便变动,更容易重构代码,最大程度上实现代码的重用
  • m表示model,主要用于对数据库层的封装
  • v表示view,用于向用户展现结果
  • c表示controller,是核心,用于处理请求、获取数据、返回结果

MTV

  • Django是一款python的web开发框架
  • 与MVC有所不一样,属于MTV框架
  • m表示model,负责与数据库(ORM)交互
  • v表示view,是核心,负责接收请求、获取数据、返回结果
  • t表示template,负责呈现内容到浏览器

此外,Django还有一个url分发器,它的做用是将一个个URL的页面请求分发给不一样的view处理,view再调用相应的Model和Template程序员

 

下面进入正式部分。web

主要知识点框架罗列:ajax

  • 环境搭建
  • 定义模型
  • 使用后台管理
  • 路由配置系统(URLconf)

  • 编写视图
  • 定义模板

一 Django基本命令

 安装Django

pip install django

 

(双等号能够指定安装版本,好比我的安装1.11.4版本,就可使用:pip install django==1.11.4)

说明:使用pip install django命令进行安装时,会自动删除旧版本,再安装新版本

查看Django版本

进入python shell,运行以下代码:

import django
django.get_version()

 

建立项目

django-admin startproject project_name

 

项目结构:

例如建立一个名为test1的项目,结构以下:

说明:

  • manage.py:一个命令行工具,可使你用多种方式对Django项目进行交互
  • 内层的目录:项目的真正的Python包
  • _init _.py:一个空文件,它告诉Python这个目录应该被看作一个Python包
  • settings.py:项目的配置
  • urls.py:项目的URL声明
  • wsgi.py:项目与WSGI兼容的Web服务器入口

在项目下建立应用,好比blog:

项目与应用关系:
        一个项目有多个应用
        一个应用能够被多个项目拥有

命令:

python manage.py startapp blog

 

 

启动django项目

python manage.py runserver IP PORT
  • 能够不写ip,默认端口为8000

  这样咱们的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 会把数据所有清空掉,只留下空表。

 

 Django 项目环境终端

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

问题解决!

 

static配置

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',
        },
    }
}

 

 二 路由配置系统(URLconf)

 

  • 在settings.py文件中经过ROOT_URLCONF指定根级url的配    
  • urlpatterns是一个url()实例的列表

  例:

 

ROOT_URLCONF = 'BlogSM.urls'

(建立的项目名称为:BlogSM)

 

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^add/',views.add),
  url(r'^$',views.add),  #(此条通常用做增长用户体验,好比首页展现特定内容)

]

 

  • 一个url()对象包括:
    • 正则表达式
    • 视图函数
    • 名称name
  • 编写URLconf的注意:
    • 若要从url中捕获一个值,须要在它周围设置一对圆括号
    • 不须要添加一个前导的反斜杠,如应该写做'test/',而不该该写做'/test/'
    • 每一个正则表达式前面的r表示字符串不转义
  • 请求的url被看作是一个普通的python字符串,进行匹配时不包括get或post请求的参数及域名
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')。

 

 

无名分组(named group)

正则表达式非命名组(经过圆括号),经过位置参数传递给视图

 

有名分组(named group)

正则表达式命名组,经过关键字参数传递给视图

语法:(?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 在什么上查找

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 值。

包含其它的URLconfs

 
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匹配
 
  • 使用include能够去除urlconf的冗余
  • 参数:视图会收到来自父URLconf、当前URLconf捕获的全部参数
  • 在include中经过namespace定义命名空间,用于反解析

 

name参数

'''

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错误

404 (page not found) 视图

  • defaults.page_not_found(request, template_name='404.html')
  • 默认的404视图将传递一个变量给模板:request_path,它是致使错误的URL
  • 若是Django在检测URLconf中的每一个正则表达式后没有找到匹配的内容也将调用404视图
  • 若是在settings中DEBUG设置为True,那么将永远不会调用404视图,而是显示URLconf 并带有一些调试信息
  • 在templates中建立404.html
<!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/

 500 (server error) 视图

  • defaults.server_error(request, template_name='500.html')
  • 在视图代码中出现运行时错误
  • 默认的500视图不会传递变量给500.html模板
  • 若是在settings中DEBUG设置为True,那么将永远不会调用505视图,而是显示URLconf 并带有一些调试信息

400 (bad request) 视图

    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函数

--------------render(request, template_name[, context])

结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。

参数:
     request: 用于生成响应的请求对象。

     template_name:要使用的模板的完整名称,可选的参数

     context:添加到模板上下文的一个字典。默认是一个空字典。若是字典中的某个值是可调用的,视图将在渲染模板以前调用它。

     content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值。

     status:响应的状态码。默认为200。

 

redirect函数

-----------------------------------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/,因此当刷新后
#     又得从新登陆.

视图之其余:

 HttpReqeust对象

  • 服务器接收到http协议的请求后,会根据报文建立HttpRequest对象
  • 视图函数的第一个参数是HttpRequest对象
  • 在django.http模块中定义了HttpRequest对象的API

属性

  • 下面除非特别说明,属性都是只读的
  • path:一个字符串,表示请求的页面的完整路径,不包含域名
  • method:一个字符串,表示请求使用的HTTP方法,经常使用值包括:'GET'、'POST'
  • encoding:一个字符串,表示提交的数据的编码方式
    • 若是为None则表示使用浏览器的默认设置,通常为utf-8
    • 这个属性是可写的,能够经过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值
  • GET:一个相似于字典的对象,包含get请求方式的全部参数
  • POST:一个相似于字典的对象,包含post请求方式的全部参数
  • FILES:一个相似于字典的对象,包含全部的上传文件
  • COOKIES:一个标准的Python字典,包含全部的cookie,键和值都为字符串
  • session:一个既可读又可写的相似于字典的对象,表示当前的会话,只有当Django 启用会话的支持时才可用,详细内容见“状态保持”

方法

  • is_ajax():若是请求是经过XMLHttpRequest发起的,则返回True

 

QueryDict对象

  • request对象的属性GET、POST都是QueryDict类型的对象
  • 与python字典不一样,QueryDict类型的对象用来处理同一个键带有多个值的状况
  • 方法get():根据键获取值
    • 只能获取键的一个值
    • 若是一个键同时拥有多个值,获取最后一个值
    • dict.get('键',default)
      或简写为
      dict['键']
      

       

  • 方法getlist():根据键获取值
    • 将键的值以列表返回,能够获取一个键的多个值
    • dict.getlist('键',default)
      GET属性

 GET属性

  • QueryDict类型的对象
  • 包含get请求方式的全部参数
  • 与url请求地址中的参数对应,位于?后面
  • 参数的格式是键值对,如key1=value1
  • 多个参数之间,使用&链接,如key1=value1&key2=value2
  • 键是开发人员定下来的,值是可变的
  • 示例以下
  • 建立视图getTest1用于定义连接,getTest2用于接收一键一值,getTest3用于接收一键多值
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
url(r'^getTest1/$', views.getTest1),
url(r'^getTest2/$', views.getTest2),
url(r'^getTest3/$', views.getTest3),
  • 建立getTest1.html,定义连接
<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>
  •  完善视图getTest2的代码
def getTest2(request):
    a=request.GET['a']
    b=request.GET['b']
    context={'a':a,'b':b}
    return render(request,'booktest/getTest2.html',context)
  • 建立getTest2.html,显示接收结果
<html>
<head>
    <title>Title</title>
</head>
<body>
a:{{ a }}<br>
b:{{ b }}
</body>
</html>

 

  • 完善视图getTest3的代码
def getTest3(request):
    a=request.GET.getlist('a')
    b=request.GET['b']
    context={'a':a,'b':b}
    return render(request,'booktest/getTest3.html',context)
  • 建立getTest3.html,显示接收结果
<html>
<head>
    <title>Title</title>
</head>
<body>
a:{% for item in a %}
{{ item }}
{% endfor %}
<br>
b:{{ b }}
</body>
</html>

 

POST属性

  • QueryDict类型的对象
  • 包含post请求方式的全部参数
  • 与form表单中的控件对应
  • 问:表单中哪些控件会被提交?
  • 答:控件要有name属性,则name属性的值为键,value属性的值为键,构成键值对提交
    • 对于checkbox控件,name属性同样为一组,当控件被选中后会被提交,存在一键多值的状况
  • 键是开发人员定下来的,值是可变的
  • 示例以下
  • 定义视图postTest1
def postTest1(request):
    return render(request,'booktest/postTest1.html')
  •  配置url
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>
  • 建立视图postTest2接收请求的数据
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
url(r'^postTest2$',views.postTest2)

 

  • 建立模板postTest2.html
<html>
<head>
    <title>Title</title>
</head>
<body>
{{ uname }}<br>
{{ upwd }}<br>
{{ ugender }}<br>
{{ uhobby }}
</body>
</html>

 

注意:使用表单提交,注释掉settings.py中的中间件crsf

 

HttpResponse对象

 

  • 在django.http模块中定义了HttpResponse对象的API
  • HttpRequest对象由Django自动建立,HttpResponse对象由程序员建立
  • 不调用模板,直接返回数据
from django.http import HttpResponse

def index(request):
    return HttpResponse('你好')

 

属性

  • content:表示返回的内容,字符串类型
  • charset:表示response采用的编码字符集,字符串类型
  • status_code:响应的HTTP响应状态码
  • content-type:指定输出的MIME类型

方法

  • init :使用页内容实例化HttpResponse对象
  • write(content):以文件的方式写
  • flush():以文件的方式输出缓存区
  • set_cookie(key, value='', max_age=None, expires=None):设置Cookie
    • key、value都是字符串类型
    • max_age是一个整数,表示在指定秒数后过时
    • expires是一个datetime或timedelta对象,会话将在这个指定的日期/时间过时,注意datetime和timedelta值只有在使用PickleSerializer时才可序列化
    • max_age与expires二选一
    • 若是不指定过时时间,则两个星期后过时
  • delete_cookie(key):删除指定的key的Cookie,若是key不存在则什么也不发生

 

子类JsonResponse

  • 返回json数据,通常用于异步请求
  • _init _(data)
  • 帮助用户建立JSON编码的响应
  • 参数data是字典对象
  • JsonResponse的默认Content-Type为application/json
from django.http import JsonResponse

def index2(requeset):
    return JsonResponse({'list': 'abc'})

 

状态保持

  • http协议是无状态的:每次请求都是一次新的请求,不会记得以前通讯的状态
  • 客户端与服务器端的一次通讯,就是一次会话
  • 实现状态保持的方式:在客户端或服务器端存储与会话有关的数据
  • 存储方式包括cookie、session,会话通常指session对象
  • 使用cookie,全部数据存储在客户端,注意不要存储敏感信息
  • 推荐使用sesison方式,全部数据存储在服务器端,在客户端cookie中存储session_id
  • 状态保持的目的是在一段时间内跟踪请求者的状态,能够实现跨页面访问当前请求者的数据
  • 注意:不一样的请求者之间不会共享这个数据,与请求者一一对应

 

cookie的工做原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能经过cookie的内容来判断这个是“谁”了。

cookie虽然在必定程度上解决了“保持状态”的需求,可是因为cookie自己最大支持4096字节,以及cookie自己保存在客户端,可能被拦截或窃取,所以就须要有一种新的东西,它能支持更多的字节,而且他保存在服务器,有较高的安全性。这就是session。

咱们能够给每一个客户端的cookie分配一个惟一的id,这样用户在访问时,经过cookie,服务器就知道来的人是“谁”。而后咱们再根据不一样的cookie的id,在服务器上保存一段时间的私密资料,如“帐号密码”等等。

三、总结而言: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}

COOKIE

---------------------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/")

 

SESSION

  • 先在templates目录下建立两个html,login.html负责登陆页面。backend页面表明后台页面
  • 第二步 编辑app01应用下的views.py文件,编写代码逻辑部分
--------------------------------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/')

 

  • 第三步,编辑mydjango目录下的urls.py文件。设置函数与页面的绑定关系

 

--------------------------------------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/页面的时候直接就被重定向到了/login/
  • 只有在输入了正确的用户名和密码以后才进入到了/backend/页面
--------------------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>

 页面显示结果:

 

 

从上图中咱们看到有一下几点:

一、login页面正确登陆的话,后台页面能够获取到浏览器携带的cookie的。

二、第一行的sessionid其实就是cookie值

三、session的内容是加密的,从客户端获取不到session的内容

四、服务端能够经过预设的key值取出session的内容并打印到前端

从火狐浏览器里查看cookie

 

 

django的session默认是存储在数据库里的,咱们再到数据库查看一下真正session内容

 

 

 

 cookie、session总结:

 

# 一、获取Cookie:
# request.COOKIES['key']
# request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
#     参数:
#         default: 默认值
#            salt: 加密盐
#         max_age: 后台控制过时时间



# 二、设置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供开发者使用:

  • 数据库(默认)
  • 缓存
  • 文件
  • 缓存+数据库
  • 加密cookie

 

 一、数据库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")
 
        ...

 

 二、缓存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. 使用
 
    同上

 

 三、文件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. 使用
 
    同上

 

 四、缓存+数据库Session

 

 

数据库用于作持久化,缓存用于提升效率
 
a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
 
b. 使用
 
    同上

 

五、加密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

 

 四 Template

 python的模板:HTML代码+逻辑控制代码

模板支持的语法

变量(使用双大括号来引用变量)

语法格式: {{var_name}}

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 }}

 

变量的过滤器(filter)的使用

  # 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'

 

标签(tag)的使用(使用大括号和百分比的组合来表示使用tag)

语法格式:    {% tags %}

{% 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 %} 

 

{% for %}的使用

{% 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标签

用于生成csrf_token的标签,用于防治跨站攻击验证。 其实,这里是会生成一个input标签,和其余表单标签一块儿提交给后台的。

 

{% url %}

引用路由配置的地址

<form action="{% url "bieming"%}" >
          <input type="text">
          <input type="submit"value="提交">
          {%csrf_token%}
</form>

 

{% with %}

用更简单的变量名替代复杂的变量名



{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}

 

{% verbatim %}

禁止render

{% verbatim %}
         {{ hello }}
{% endverbatim %}

 

加载标签库:自定义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 %}

 

 extend模板继承

解决问题:减小共用页面区域(好比站点导航)所引发的重复和冗余代码

本质上来讲,模板继承就是先构造一个基础框架模板,然后在其子模板中对它所包含站点公用部分和定义块进行重载。

你能够对那些不一样 的代码段进行定义,而不是 共同 代码段。

第一步是定义 基础模板,该框架以后将由子模板所继承。

示例:

<!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 %} 标签,父模板将无从得知要使用哪一个块的内容。

 

五 数据库与ORM

 

ORM表模型

设计模型之定义模型类

有一个数据表,就有一个模型类与之对应;

打开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中,
             表名-----------类名
             字段-----------类属性
             表中的一条记录-------------类实例对象

 

  • 激活模型:编辑settings.py文件,将应用名称加入到installed_apps中

注意:因为Django版本不一样,因此实现上述的方式也不一样,例如本人用的Django版本为1.11.4版本,系统会自动添加,无需本身再添加

  • 生成迁移文件:根据模型类生成sql语句

 

python manage.py makemigrations
  •  迁移文件被生成到应用的migrations目录

 

  • 执行迁移:执行sql语句生成数据表

 

python manage.py migrate

 

关系的类型包括:

  • ForeignKey:一对多,将字段定义在多的端中
  • ManyToManyField:多对多,将字段定义在两端中
  • OneToOneField:一对一,将字段定义在任意一端中
用一访问多:对象.模型类小写_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

 

 

 注意:

    • 每一个数据模型都是django.db.models.Model的子类,它的父类Model包含了全部必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。
    • 每一个模型至关于单个数据库表(多对多关系例外,会多生成一张关系表),每一个属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)至关于数据库的字段类型(例如varchar)。
    • 使用方式
      1. 导入from django.db import models
      2. 经过models.Field建立字段类型的对象,赋值给属性
    • 上述咱们用到的属性名的类型:CharField、.URLField、BooleanField、EmailField、DateField、DecimalField,还有其余等等,见下表。
    • 属性命名限制
      • 不能是python的保留关键字
      • 因为django的查询方式,不容许使用连续的下划线
    • 定义属性:对于重要数据都作逻辑删除,不作物理删除,实现方法是定义isDelete属性,类型为BooleanField,默认值为False

 

字段类型

    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, 这个字段在表中必须有惟一值

 建立对象

  • 当建立对象时,django不会对数据库进行读写操做
  • 调用save()方法才与数据库交互,将对象保存到数据库中
  • 说明: _init _方法已经在基类models.Model中使用,在自定义模型中没法使用

 

tips:

批量导入数据:

例如类名为:Book

Book.objects.bulk_create(book_list)

 

实例的属性

  • DoesNotExist:在进行单个查询时,模型的对象不存在时会引起此异常,结合try/except使用

实例的方法

  • str (self):重写object方法,此方法在将对象转换成字符串时会被调用
  • save():将模型对象保存到数据表中
  • delete():将模型对象从数据表中删除

 

Book.objects.filter(id=1).delete()

 

 

 ORM之增(create,save)


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

 ORM之删(delete)

>>> Book.objects.filter(id=1).delete()

有时咱们删除一条信息,实际却会删除了多条,好比某本书由两名做者共同创做完成,那么删除该本书,会连同对多关系表中和该本书相关的做者那行记录删除。

这种删除方式就是django默认的级联删除。

ORM之改(update和save)

例如,如下几张表:

书表:

-------------------------------------------------------------------------------------------------

做者表:

-------------------------------------------------------------------------------------------------

出版社表:

-------------------------------------------------------------------------------------------------

书_做者表:

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()方法会返回一个整型数值,表示受影响的记录条数。

 

 ORM之查(filter,value)

# 查询相关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等于十一、2二、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,

 

QuerySet与惰性机制

所谓惰性机制: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:是否包含在范围内
例如:

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) filter(pk__in=[1, 2, 3, 4, 5])

聚合与分组:aggregate、annotate

  • 使用aggregate()函数返回聚合函数的值
  • 函数:Avg,Count,Max,Min,Sum

示例:

求全部书籍的平均价格:

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

 F对象

  • django支持对F()对象使用算数运算

示例:将全部书籍的价格增加20元


Book.objects.all().update(price=F("price")+20) from django.db.models import F

 Q对象

  • 过滤器的方法中关键字参数查询,会合并为And进行
  • 须要进行or查询,使用Q()对象
  • Q对象(django.db.models.Q)用于封装一组关键字参数,这些关键字参数与“比较运算符”中的相同
  • Q对象可使用&(and)、|(or)操做符组合起来
  • 当操做符应用在两个Q对象时,会产生一个新的Q对象
  •  使用~(not)操做符在Q对象前表示取反

示例:

查询书籍名字或者以老开头,或者价格大于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))

 

模型之其余二:

  • 查询集表示从数据库中获取的对象集合
  • 查询集能够含有零个、一个或多个过滤器
  • 过滤器基于所给的参数限制查询的结果
  • 从Sql的角度,查询集和select语句等价,过滤器像where和limit子句

查询集

  • 在管理器上调用过滤器方法会返回查询集
  • 查询集通过过滤器筛选后返回新的查询集,所以能够写成链式过滤
  • 惰性执行:建立查询集不会带来任何数据库的访问,直到调用数据时,才会访问数据库
  • 什么时候对查询集求值:迭代,序列化,与if合用
  • 返回查询集的方法,称为过滤器
    • all()
    • filter()
    • exclude()
    • order_by()
    • values():一个对象构成一个字典,而后构成一个列表返回
  • 写法:
filter(键1=值1,键2=值2)
等价于
filter(键1=值1).filter(键2=值2)
  • 返回单个值的方法
  • get():返回单个知足条件的对象
    • 若是未找到会引起"模型类.DoesNotExist"异常
    • 若是多条被返回,会引起"模型类.MultipleObjectsReturned"异常
  • count():返回当前查询的总条数
  • first():返回第一个对象
  • last():返回最后一个对象
  • exists():判断查询集中是否有数据,若是有则返回True

限制查询集

  • 查询集返回列表,可使用下标的方式进行限制,等同于sql中的limit和offset子句
  • 注意:不支持负数索引
  • 使用下标后返回一个新的查询集,不会当即执行查询
  • 若是获取一个对象,直接使用[0],等同于[0:1].get(),可是若是没有数据,[0]引起IndexError异常,[0:1].get()引起DoesNotExist异常

查询集的缓存

  • 每一个查询集都包含一个缓存来最小化对数据库的访问
  • 在新建的查询集中,缓存为空,首次对查询集求值时,会发生数据库查询,django会将查询的结果存在查询集的缓存中,并返回请求的结果,接下来对查询集求值将重用缓存的结果
  • 状况一:这构成了两个查询集,没法重用缓存,每次查询都会与数据库进行一次交互,增长了数据库的负载
  • 状况二:两次循环使用同一个查询集,第二次使用缓存中的数据
  • 什么时候查询集不会被缓存:当只对查询集的部分进行求值时会检查缓存,可是若是这部分不在缓存中,那么接下来查询返回的记录将不会被缓存,这意味着使用索引来限制查询集将不会填充缓存,若是这部分数据已经被缓存,则直接使用缓存中的数据

 

 

管理操做

admin是django强大功能之一,它能共从数据库中读取数据,呈如今页面中,进行管理。

默认状况下,它的功能已经很是强大,若是你不须要复杂的功能,它已经够用,可是有时候,一些特殊的功能还须要定制,好比搜索功能,下面就逐步深刻介绍如何定制适合本身的admin应用。

Django会根据定义的模型类彻底自动地生成管理模块

使用django的管理

  • 建立一个管理员用户
python manage.py createsuperuser,按提示输入用户名、邮箱、密码
  • 进入管理站点,默承认以对groups、users进行管理

管理界面本地化

  • 编辑settings.py文件,设置编码、时区
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai'

 

向admin注册booktest的模型

方式一:使用register的方法

  • 打开booktest/admin.py文件,注册模型

from django.contrib import admin
from models import BookInfo
admin.site.register(BookInfo)

 方式二:使用register的装饰器

@admin.register(Book)

 

  • 刷新管理页面,能够对BookInfo的数据进行增删改查操做
  • 问题:若是在str方法中返回中文,在修改和添加时会报ascii的错误
  • 解决:在str()方法中,将字符串末尾添加“.encode('utf-8')”

自定义管理页面

  • Django提供了admin.ModelAdmin类
  • 经过定义ModelAdmin的子类,来定义模型在Admin界面的显示方式
class QuestionAdmin(admin.ModelAdmin):
    ...
admin.site.register(Question, QuestionAdmin)

 

列表页属性

  • list_display:显示字段,能够点击列头进行排序
list_display = ['pk', 'btitle', 'bpub_date']
  •  list_filter:过滤字段,过滤框会出如今右侧
list_filter = ['btitle']
  •  search_fields:搜索字段,搜索框会出如今上侧
search_fields = ['btitle']
  •  list_per_page:分页,分页框会出如今下侧
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:属性的前后顺序
fields = ['bpub_date', 'btitle']
  •  fieldsets:属性分组
fieldsets = [
    ('basic',{'fields': ['btitle']}),
    ('more', {'fields': ['bpub_date']}),
]

 

布尔值的显示

  • 发布性别的显示不是一个直观的结果,可使用方法进行封装
def gender(self):
    if self.hgender:
        return '男'
    else:
        return '女'
gender.short_description = '性别'

 

  • 在admin注册中使用gender代替hgender
class HeroInfoAdmin(admin.ModelAdmin):
    list_display = ['id', 'hname', 'gender', 'hcontent']
  • 元信息db_table:定义数据表名称,推荐使用小写字母,数据表的默认名称
  •  ordering:对象的默认排序字段,获取对象的列表时使用,接收属性构成的列表
  • 字符串前加-表示倒序,不加-表示正序
  • 排序会增长数据库的开销
相关文章
相关标签/搜索