Django框架

 

1、Django简介php

一、MVC模型css

MVC模型是web服务器开发领域著名的模型。html

M:模型,业务对象与数据库的映射(ORM)java

V:视图,用于与用户的交互(页面)python

C:控制器,接收用户的输入调用模型和视图完成用户的请求mysql

二、MTV模型jquery

MTV模型是Django的web服务器开发模型,与MVC本质上的原理是同样的,也是为了各组件间保持松耦合关系。git

M(model):模型,负责业务对象与数据库的关系映射(ORM)web

T(template):模板,负责将HTML页面展现给用户ajax

V(view):视图,负责业务逻辑,并在适当时候调用Model和Template

除了以上三层以外,还须要一个URL分发器,它的做用是将一个个URL的页面请求分发给不一样的View处理,View再调用相应的Model和Template,最后将响应数据返回给用户。

三、Django的下载

pip3 install django  # 下载最新版本
pip3 install django==1.11  # 指定版本下载

四、建立一个Django项目:

django-admin.py startproject mysite  # 建立一个叫mysite的Django项目

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

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

wsgi.py----遵循wsgi规范的模块

五、在mysite目录下建立应用

python3 manage.py startapp blog  # 建立一个叫blog的app

目录结构:

六、启动Django项目

python3 manage.py runserver 127.0.0.1:8088

七、基于Django实现一个简单的示例:显示当前日期

import time
from django.shortcuts import render


def index(request):
    ctime = time.strftime("%Y-%m-%d %X")
    return render(request, "index.html", {"ctime": ctime})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>显示当前时间</title>
</head>
<body>
    <div>
        <h3>{{ ctime }}</h3>
    </div>
</body>
</html>
index.html
from django.contrib import admin
from django.urls import path
from blog import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),  # 设置路由
]
urls.py

记得在settings.py中设置templates的路径:

在浏览器访问http://127.0.0.1:8088/index/便可:

2、Django的路由

一、使用正则

经过"圆括号"来捕获URL 中的值并以位置参数传递给视图。

from django.contrib import admin
from django.urls import path, re_path
from blog import views

urlpatterns = [
    # http://127.0.0.1:8088/articles/2003/ 将调用函数special_case_2003(request)
    re_path(r'^articles/2003/$', views.special_case_2003),

    # http://127.0.0.1:8088/articles/2004/ 将调用函数year_archive(request, year)
    re_path(r'^articles/([0-9]{4})/$', views.year_archive),

    # /articles/2005/03/ 将调用函数month_archive(request, year, month)
    re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),

    # http://127.0.0.1:8088/articles/2003/04/12/ 将调用函数article_detail(request, year, month, day)
    re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
urls.py
from django.shortcuts import render, HttpResponse


def special_case_2003(request):
    return HttpResponse("special_case_2003")


def year_archive(request, year):
    print(year)
    return HttpResponse("year_archive")


def month_archive(request, year, month):
    print(year, month)
    return HttpResponse("month_archive")


def article_detail(request, year, month, day):
    print(year, month, day)
    return HttpResponse("article_detail")
views.py

二、有名分组

给URL中的参数命名,语法:(?P<name>pattern),name是指组的名称, pattern是指要匹配的模式。

与上面的区别在于,捕获的值做为关键字参数而不是位置参数传递给视图函数。

from django.contrib import admin
from django.urls import path, re_path
from blog import views

urlpatterns = [
    # http://127.0.0.1:8088/articles/2003/ 将调用函数special_case_2003(request)
    re_path(r'^articles/2003/$', views.special_case_2003),

    # http://127.0.0.1:8088/articles/2004/ 将调用函数year_archive(request, year)
    re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

    # /articles/2005/03/ 将调用函数month_archive(request, year, month)
    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),

    # http://127.0.0.1:8088/articles/2003/04/12/ 将调用函数article_detail(request, year, month, day)
    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]+)/$', views.article_detail),
]
urls.py

三、路由分发

有时候在开发一个项目中,能够会有多个APP,咱们能够在每一个APP下单独给这个APP设置urls,使得咱们可以更清晰直观的读取URL。

如:

在blog app下新建urls.py:

from django.urls import path
from blog import views

urlpatterns = [
    path('index/', views.index),

]
blog/urls.py

在全局urls.py中经过include来进行路由分发:

from django.contrib import admin
from django.urls import path, re_path, include
from blog import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include("blog.urls")),  # http://127.0.0.1:8088/blog/index/ 将访问blog下的urls.py
]
urls.py
import time
from django.shortcuts import render, HttpResponse


def index(request):
    ctime = time.strftime("%Y-%m-%d %X")
    return render(request, "index.html", {"ctime": ctime})
views.py

四、反向解析

人们强烈但愿不要硬编码这些URL,费力、不可扩展且容易产生错误,经过给URL设置name,再借助Django 提供不一样的工具用于URL 反查

(1)在模板中:使用url 模板标签 {% %}

(2)在视图中:使用from django.urls import reverse 函数

from django.contrib import admin
from django.urls import path, re_path, include
from blog import views


urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^book/(?P<pk>\d+)/$', views.book, name="book"),
]
urls.py
from django.shortcuts import render, HttpResponse
from django.urls import reverse


def book(request, pk):
    url = reverse("book", args=(pk,))  # 反向获取URL,book是在urls.py中给URL设置的名字,args是URL中的参数
    print(url)
    return render(request, "book.html", locals())
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {#    book是给URL设置的名称,pk是URL中的参数 #}
    <a href="{% url "book" pk %}">book {{ pk }}</a>
</body>
</html>
book.html

五、命名空间

命名空间(Namespace)是表示标识符的可见范围。

因为name没有做用域,Django在反解URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,当即返回。 咱们在开发项目时,会常用name属性反解出URL,当不当心在不一样的app的urls中定义相同的name时,可能会致使URL反解错误,为了不这种事情发生,引入了命名空间。

在Django项目mysite下新建两个APP:

python3 manage.py startapp app01
python3 manage.py startapp app02

mysite/urls.py:

from django.urls import path, re_path, include


urlpatterns = [
    re_path(r'^app01/', include(("app01.urls", "app01"), namespace="app01")),
    re_path(r'^app02/', include(("app02.urls", "app02"), namespace="app02")),
]

 

app01/urls.py:

from django.conf.urls import re_path
from app01.views import index

urlpatterns = [
    re_path(r'^index/$', index, name="index"),
]

app02/urls.py:

from django.conf.urls import re_path
from app02.views import index

urlpatterns = [
    re_path(r'^index/$', index, name="index"),
]

app01/views.py:

from django.shortcuts import HttpResponse


def index(request):
    return HttpResponse("in app01-index")

app02/views.py:

from django.shortcuts import HttpResponse


def index(request):
    return HttpResponse("in app02-index")

经过浏览器访问:

include()源码:

六、Django2.0版本的path

有以下URL:

urlpatterns = [
re_path('articles/(?P<year>[0-9]{4})/', year_archive),
re_path('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view),
]

思考:

(1)默认函数 year_archive 中year参数是字符串类型的,有时候咱们须要将它转换成整数类型的变量值来使用,固然year=int(year) 不会有诸如如TypeError或者ValueError的异常。那么有没有一种方法,在url中,使得这一转化步骤能够由Django自动完成?

(2)三个路由中article_id都是一样的正则表达式,可是你须要写三遍,当以后article_id规则改变后,须要同时修改三处代码,那么有没有一种方法,只需修改一处便可?

在Django2.0中,可使用 path 解决以上的两个问题,基本规则:

(1)使用尖括号(<>)从url中捕获值。

(2)捕获值中能够包含一个转化器类型(converter type),好比使用<int:year>捕获一个整数变量。若果没有转化器,将匹配任何字符串,固然也包括了 / 字符。

from django.urls import path  
from . import views  
urlpatterns = [  
    path('articles/2003/', views.special_case_2003),  
    path('articles/<int:year>/', views.year_archive),  
    path('articles/<int:year>/<int:month>/', views.month_archive),  
    path('articles/<int:year>/<int:month>/<slug>/', views.article_detail),  
]

Django默认支持如下5个转化器:

  • str,匹配除了路径分隔符(/)以外的非空字符串,这是默认的形式
  • int,匹配正整数,包含0。
  • slug,匹配字母、数字以及横杠、下划线组成的字符串。
  • uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
  • path,匹配任何非空字符串,包含了路径分隔符

 

能够注册自定义转化器

对于一些复杂或者复用的须要,能够定义本身的转化器。转化器是一个类或接口,它的要求有三点:

  • regex 类属性,字符串类型
  • to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。
  • to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,一般用于url反向引用。
class FourDigitYearConverter:  
    regex = '[0-9]{4}'  
    def to_python(self, value):  
        return int(value)  
    def to_url(self, value):  
        return '%04d' % value

使用register_converter 将其注册到URL配置中:

from django.urls import register_converter, path  
from . import converters, views  
# 注册类,并给这个转换器命名为xx register_converter(converters.FourDigitYearConverter, 'xx') urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<xx:year>/', views.year_archive), # 使用转换器 ]

 

3、Django的视图

Django中的视图有两类:基于函数的视图(FBV)和基于类的视图(CBV),它接收Web请求(HttpRequest对象)而且返回Web响应(HttpResponse对象)。

一、HttpRequest对象

1.HttpRequest.GET
  一个相似于字典的QueryDict对象,包含了 HTTP GET 的全部参数。
2.HttpRequest.POST
  一个QueryDict 对象,若是请求中包含表单数据,则将这些数据封装成 QueryDict 对象。
  POST 请求能够带有空的POST字典。若是经过POST方法发送一个表单,可是表单中没有任何的数据,QueryDict 对象依然会被建立。
   所以,不该该使用 if request.POST  来检查使用的是不是POST 方法;应该使用 if request.method == "POST"
  另外:若是使用 POST 上传文件的话,文件信息将包含在 FILES 属性中。
     注意:键值对的值是多个的时候,好比checkbox类型的input标签,select标签,须要用:request.POST.getlist("hobby") 来获取数据

3.HttpRequest.body
  一个字符串,表明请求报文的主体。在处理非 HTTP 形式的报文时很是有用,例如:二进制图片、XML,Json等。
  可是,若是要处理表单数据的时候,推荐仍是使用 HttpRequest.POST 。

4.HttpRequest.path
  一个字符串,表示请求的路径组件(不含域名)。
  例如:"/music/bands/the_beatles/"

5.HttpRequest.method
  一个字符串,表示请求使用的HTTP 方法。必须使用大写。
  例如:"GET""POST"

6.HttpRequest.encoding
  一个字符串,表示提交的数据的编码方式(若是为 None 则表示使用 DEFAULT_CHARSET 的设置,默认为 'utf-8')。
   这个属性是可写的,你能够修改它来修改访问表单数据使用的编码。
   接下来对属性的任何访问(例如从 GET 或 POST 中读取数据)将使用新的 encoding 值。
   若是你知道表单数据的编码不是 DEFAULT_CHARSET ,则使用它。

7.HttpRequest.META
   一个标准的Python 字典,包含全部的HTTP 首部。具体的头部信息取决于客户端和服务器,下面是一些示例:
    CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
    CONTENT_TYPE —— 请求的正文的MIME 类型。
    HTTP_ACCEPT —— 响应可接收的Content-Type。
    HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
    HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
    HTTP_HOST —— 客服端发送的HTTP Host 头部。
    HTTP_REFERER —— Referring 页面。
    HTTP_USER_AGENT —— 客户端的user-agent 字符串。
    QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
    REMOTE_ADDR —— 客户端的IP 地址。
    REMOTE_HOST —— 客户端的主机名。
    REMOTE_USER —— 服务器认证后的用户。
    REQUEST_METHOD —— 请求方法,一个字符串,例如"GET""POST"。
    SERVER_NAME —— 服务器的主机名。
    SERVER_PORT —— 服务器的端口(是一个字符串)。
   从上面能够看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 以外,请求中的任何 HTTP 首部转换为 META 的键时,
    都会将全部字母大写并将链接符替换为下划线最后加上 HTTP_  前缀。
    因此,一个叫作 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。

8.HttpRequest.FILES
  一个相似于字典的对象,包含全部的上传文件信息。
   FILES 中的每一个键为<input type="file" name="" /> 中的name,值则为对应的数据。
  注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的状况下才会
   包含数据。不然,FILES 将为一个空的相似于字典的对象。

9.HttpRequest.COOKIES
  一个标准的Python 字典,包含全部的cookie。键和值都为字符串。

10.HttpRequest.session
   一个既可读又可写的相似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。

11.HttpRequest.user(用户认证组件下使用)
  一个 AUTH_USER_MODEL 类型的对象,表示当前登陆的用户。
  若是用户当前没有登陆,user 将设置为 django.contrib.auth.models.AnonymousUser 的一个实例。你能够经过 is_authenticated() 区分它们。

    例如:
    if request.user.is_authenticated():
        # Do something for logged-in users.
    else:
        # Do something for anonymous users.
       user 只有当Django 启用 AuthenticationMiddleware 中间件时才可用。
     -------------------------------------------------------------------------------------
    匿名用户
    class models.AnonymousUser
    django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具备下面几个不一样点:
    id 永远为None。
    username 永远为空字符串。
    get_username() 永远返回空字符串。
    is_staff 和 is_superuser 永远为False。
    is_active 永远为 False。
    groups 和 user_permissions 永远为空。
    is_anonymous() 返回True 而不是False。
    is_authenticated() 返回False 而不是True。
    set_password()、check_password()、save() 和delete() 引起 NotImplementedError。
    New in Django 1.8:
    新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User。
View Code

经常使用方法:

1.HttpRequest.get_full_path()
  返回 path,若是能够将加上查询字符串。
  例如:"/music/bands/the_beatles/?print=true"
2.HttpRequest.is_ajax()
  若是请求是经过XMLHttpRequest 发起的,则返回True,方法是检查 HTTP_X_REQUESTED_WITH 相应的首部是不是字符串'XMLHttpRequest'。
  大部分现代的 JavaScript 库都会发送这个头部。若是你编写本身的 XMLHttpRequest 调用(在浏览器端),你必须手工设置这个值来让 is_ajax() 能够工做。
  若是一个响应须要根据请求是不是经过AJAX 发起的,而且你正在使用某种形式的缓存例如Django 的 cache middleware,
   你应该使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 装饰你的视图以让响应可以正确地缓存。
View Code

二、HttpResponse对象

1、响应对象主要有三种形式:
HttpResponse()
render()
redirect()

2、HttpResponse()
直接在括号内加上字符串便可返回,如HttpResponse("操做成功!")

3、render()
格式:render(request, template_name[, context])
 结合一个给定的模板和一个给定的上下文字典,将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面做为响应体。
参数:
request: 用于生成响应的请求对象。
template_name:要使用的模板的完整名称
context:添加到模板上下文的一个字典。默认是一个空字典。若是字典中的某个值是可调用的,视图将在渲染模板以前调用它。

4、redirect()
传递要重定向的一个硬编码的URL
如:
def my_view(request):
    ...
    return redirect('/books/2/')
View Code

301和302重定向:

(1)301和302的区别。
  301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址能够从响应的Location首部中获取
  (用户看到的效果就是他输入的地址A瞬间变成了另外一个地址B)——这是它们的共同点。
  他们的不一样在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向以后的网址;
  302表示旧地址A的资源还在(仍然能够访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。 

(2)重定向缘由:
    1)网站调整(如改变网页目录结构);
    2)网页被移到一个新地址;
    3)网页扩展名改变(如应用须要把.php改为.Html或.shtml)。
        这种状况下,若是不作重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户获得一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的网站,也须要经过重定向让访问这些域名的用户自动跳转到主站点等。
View Code

 

4、Django的模板

集中存放HTML代码文件的地方

一、Django模板语法——变量

在模板中调用变量:{{var_name}}

import datetime
from django.shortcuts import render


def index(request):
    s = "hello"
    l = [111, 222, 333]  # 列表
    dic = {"name": "amy", "age": 18}  # 字典
    date = datetime.date(1993, 5, 2)  # 日期对象

    class Person(object):
        def __init__(self, name):
            self.name = name

    person_wang = Person("wang")  # 自定义类对象
    person_li = Person("li")
    person_he = Person("he")

    person_list = [person_wang, person_li, person_he]

    return render(request, "index.html", {"s": s, "l": l, "dic": dic, "date": date, "person_list": person_list})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板语法之变量</title>
</head>
<body>
<div>
    <h4>字符串:{{ s }}</h4>
    <h4>列表:{{ l.0 }}</h4>
    <h4>列表:{{ l.2 }}</h4>
    <h4>字典:{{ dic.name }}</h4>
    <h4>日期:{{ date.year }}</h4>
    <h4>类对象列表:{{ person_list.0.name }}</h4>
</div>
</body>
</html>
index.html

二、模板语法——过滤器

语法:{{obj|filter__name:param}}

常见过滤器:
1、default
若是一个变量是false或者为空,使用给定的默认值。不然,使用变量的值。例如:
{{ value|default:"nothing" }}

2、length
返回值的长度。它对字符串和列表都起做用。例如:
{{ value|length }}
若是 value 是 ['a', 'b', 'c', 'd'],那么输出是 43、date
若是 value=datetime.datetime.now(),想要显示“2019-1-12”的格式,则写为:{{ value|date:"Y-m-d" }}

4、slice
经过字符串的index值来截取字符串
若是 value="hello world",{{ value|slice:"2:-1" }}将会显示:llo worl

5、truncatechars
若是字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
参数:要截断的字符数
例如:value="hello world",{{ value|truncatechars:6  }}
结果:hel...

6、safe
Django的模板中会对HTML标签和JS等语法标签进行自动转义,缘由显而易见,这样是为了安全。可是有的时候咱们可能不但愿这些HTML元素被转义,好比咱们作一个内容管理系统,后台添加的文章中是通过修饰的,这些修饰多是经过一个相似于FCKeditor编辑加注了HTML修饰符的文本,若是自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,若是是一个单独的变量咱们能够经过过滤器“|safe”的方式告诉Django这段代码是安全的没必要转义。好比:
value="<a href="">点击</a>"
{{ value|safe}}
View Code

三、模板语法——标签

语法:{% tag %}

用于一些在输出中建立文本,一些经过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中的状况

(1)for标签:遍历每个元素

  • 遍历字典:
{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}
  • 循环序号:{{forloop}}
forloop.counter       # 计数,index从1开始  ,如1,2,3 
forloop.counter0           # 计数,index从0开始,如 0,1,2
forloop.revcounter         # 计数,从最后一个index开始倒数到1,如3,2,1
forloop.revcounter0        # 计数,从最后一个index开始倒数到0,如2,1,0
forloop.first              # 判断是否为第一个元素,返回true或false
forloop.last              # 判断是否为最后一个元素,返回true或false

# 例:
{% for item in l %}
       <p>{{ forloop.counter }}</p>
{% endfor %}
  • for ... empty

for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,能够有所操做。

{% for person in person_list %}
        {# 若是person_list不为空就打印这里: #}
            <p>{{ person.name }}</p>
{% empty %}
        {#  若是person_list为空就打印这里: #}
            <p>sorry,no person here</p>
{% endfor %}

(2)if 标签
{% if %}会对一个变量求值,若是它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。

{% if num > 100 or num < 0 %}
    <p>无效</p>
{% elif num > 80 and num < 100 %}
    <p>优秀</p>
{% else %}
    <p>凑活吧</p>
{% endif %}

(3)with

使用一个简单地名字缓存一个复杂的变量,当你须要使用一个“昂贵的”方法(好比访问数据库)不少次的时候是很是有用的

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

(4)csrf_token

这个标签用于跨站请求伪造保护

<form>
    {% csrf_token %}
    <input type="text" name="username"/>
    <input type="submit" value="提交"/>
</form>

使用这个标签后,在发送form表单数据时就不会出现404 forbidden

(5)能够利用 {% for obj in list reversed %} 反向完成循环。

四、自定义标签和过滤器

(1)在settings中的INSTALLED_APPS配置当前app,否则django没法找到自定义的simple_tag.


(2)在app中建立templatetags模块(模块名只能是templatetags)
(3)建立任意 .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)
my_tags.py

(4)在使用自定义simple_tag和filter的html文件中导入以前建立的 my_tags.py

{% load my_tags %}

(5)使用simple_tag和filter

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>xxx</title>
</head>

<body>
<div>
    {% load my_tags %}
{#     s是视图中的变量,s=10 #}
    {{ s|filter_multi:2 }}
    {{ 3|filter_multi:3 }}
    {% simple_tag_multi 2 7 %} {# 参数不限,但不能放在if、for语句中 #}
    {% simple_tag_multi s 5 %}
    {% my_input s "username" %}
</div>
</body>
</html>
index.html

结果:

注意:filter能够用在if等语句后,simple_tag不能够

{% if num|filter_multi:30 > 100 %}
    {{ num|filter_multi:30 }}
{% endif %}

 

五、模板继承

模版继承可让您建立一个基本的“骨架”模版,它包含您站点中的所有元素,而且能够定义可以被子模版覆盖的 blocks 。

语法:{% block name %} content {% endblock %}

name表示block的名称,content表示内容

新建一个base.html的文档做为HTML基本骨架:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>
        {% block title %}My amazing site{% endblock %}
    </title>
</head>

<body>
    <div id="sidebar">
        {% block sidebar %}
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/index/">Blog</a></li>
        </ul>
        {% endblock %}
    </div>

    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>
base.html

在这个例子中, block 标签订义了三个能够被子模版内容填充的block。

导入和使用模板:

{% extends "base.html" %}
{% block title %}this is index{% endblock %}

{% block content %}
    <h4>in content block</h4>
{% endblock %}
index.html

注意:

  • 若是你在模版中使用 {% extends %} 标签,它必须是模版中的第一个标签。其余的任何状况下,模版继承都将没法工做。
  • 在base模版中设置越多的 {% block %} 标签越好。请记住,子模版没必要定义所有父模版中的blocks,因此,你能够在大多数blocks中填充合理的默认内容,而后,只定义你须要的那一个。多一点钩子总比少一点好。
  • 若是你发现你本身在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个 {% block %} 中。
  • If you need to get the content of the block from the parent template, the variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.
  • 为了更好的可读性,你也能够给你的{% endblock %} 标签一个 名字 。例如:
{% block content %}
...
{% endblock content %}

在大型模版中,这个方法帮你清楚的看到哪个  {% block %} 标签被关闭了。

  • 不能在一个模版中定义多个相同名字的 block 标签。  

 

5、Django模型层

MVC或者MTV框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不须要依赖于特定的数据库,经过简单的配置就能够轻松更换数据库,这极大的减轻了开发人员的工做量,不须要面对因数据库变动而致使的无效劳动
ORM是“对象-关系-映射”的简称。

一、mysql操做和Python类操做数据库

MySQL:

# 建立数据库
CREATE DATABASE db4 default character set utf8 collate utf8_general_ci;

# 建立数据表
CREATE TABLE employee(id INT PRIMARY KEY auto_increment ,
name VARCHAR (20), 
gender BIT default 1, 
birthday DATE , 
department VARCHAR (20), 
salary DECIMAL (8,2) unsigned);

# 插入数据
INSERT employee (name,gender,birthday,salary,department)
 VALUES   ("amy",1,"1999-11-11",8000,"开发部");

# 查询
SELECT * FROM employee WHERE gender=1;

# 修改
UPDATE employee SET birthday="1993-10-24" WHERE id=1;

# 删除
DELETE FROM employee WHERE name="amy";

Python类:

新建一个Django工程。

编写类:

from django.db import models


class Employee(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    gender = models.BooleanField()
    birthday = models.DateField()
    department = models.CharField(max_length=32)
    salary = models.DecimalField(max_digits=8, decimal_places=2)
models.py

在terminal中作数据库迁移:

python3 manage.py makemigrations

python3 manage.py migrate

编写视图:

from django.shortcuts import render, HttpResponse
from app01.models import Employee


def index(request):
    # 添加一条表纪录(实例化类):
    emp = Employee(name="amy", gender=True, birthday="1999-12-12", department="开发部", salary=8000)
    emp.save()

    # 查询一条表纪录:
    Employee.objects.filter(gender=True)

    # 更新一条表纪录:
    Employee.objects.filter(id=1).update(birthday="1999-10-24")

    # # 删除一条表纪录:
    Employee.objects.filter(name="amy").delete()
    return HttpResponse(".....")
views.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'^index/', views.index),
]
urls.py

二、单表操做

(1)建立Django项目,建立名为app01的app,在app01的models.py中建立模型:

from django.db import models


class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    state = models.BooleanField()
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish = models.CharField(max_length=32)

字段:

<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参数.
View Code

参数:

(1)null

若是为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.

(1)blank

若是为True,该字段容许不填。默认为False。
要注意,这与 null 不一样。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
若是一个字段的blank=True,表单的验证将容许该字段是空值。若是字段的blank=False,该字段就是必填的。

(2)default

字段的默认值。能够是一个值或者可调用对象。若是可调用 ,每有新对象被建立它都会被调用。

(3)primary_key

若是为True,那么这个字段就是模型的主键。若是你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段作为主键,因此除非你想覆盖默认的主键行为,
不然不必设置任何一个字段的primary_key=True。

(4)unique

若是该值设置为 True, 这个数据字段的值在整张表中必须是惟一的

(5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 若是设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>并且这个选择框的选项就是choices 中的选项。
View Code

(2)settings配置

若想将模型转为mysql数据库中的表,须要在settings中配置:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db4',  # 要链接的数据库,链接前须要建立好
        'USER': 'root',  # 链接数据库的用户名
        'PASSWORD': '123456789',  # 链接数据库的密码
        'HOST': '127.0.0.1',  # 链接主机,默认本级
        'PORT': 3306  # 端口 默认3306
    }
}

对于Python3 使用PyMySQL来驱动MySQL数据库,找到项目名文件下的init.py文件,在里面写入:

import pymysql
pymysql.install_as_MySQLdb()

在pycharm的终端执行数据库迁移命令便可在指定的数据库中建立表 :

python3 manage.py makemigrations
python3 manage.py migrate

注意:

  • 要确保配置文件中的INSTALLED_APPS中写入咱们建立的app名称:
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "app01"
]
  • 若是报错以下:
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None

MySQLclient目前只支持到python3.4,所以若是使用的更高版本的python,须要修改以下:

经过查找路径C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql 这个路径里的文件把:

if version < (1, 3, 3):
     raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)

注释掉 就OK了。

  • 若是想打印orm转换过程当中的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',
        },
    }
}

(3)添加表记录

方法1:

book_obj = Book.objects.create(title="葵花宝典", state=True, price=100, publish="苹果出版社", pub_date="2012-12-12")

方法2:

book_obj=Book(title="python教程",state=True,price=100,publish="苹果出版社",pub_date="2012-12-12")
book_obj.save()

(4)查询记录

查询API:

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

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

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

<4> exclude(**kwargs):      它包含了与所给筛选条件不匹配的对象

<5> order_by(*field):       对查询结果排序

<6> reverse():              对查询结果反向排序

<8> count():                返回数据库中匹配查询(QuerySet)的对象数量。

<9> first():                返回第一条记录

<10> last():                返回最后一条记录

<11> exists():              若是QuerySet包含数据,就返回True,不然返回False

<12> values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后获得的并非一系列
                            model的实例化对象,而是一个可迭代的字典序列
<13> values_list(*field):   它与values()很是类似,它返回的是一个元组序列,values返回的是一个字典序列

<14> distinct():            从返回结果中剔除重复纪录
View Code

基于双下划线的模糊查询:

Book.objects.filter(price__in=[100,200,300])  # 价格在列表里面的记录
Book.objects.filter(price__gt=100)  # 价格大于100
Book.objects.filter(price__lt=100)  # 价格小于100
Book.objects.filter(price__range=[100,200])  # 区间
Book.objects.filter(title__contains="python")  # 包含,大小写敏感
Book.objects.filter(title__icontains="python")  # 包含,大小写不敏感
Book.objects.filter(title__startswith="py")  # 以xx开头,大小写敏感
Book.objects.filter(pub_date__year=2012)  # 2012年出版的
View Code

(5)删除记录

删除方法就是 delete(),它运行时当即删除对象,返回删除对象:

model_obj.delete()

例:

res = Book.objects.filter(id=1).delete()  # 删除id=1 的记录
print(res)  #结果: (1, {'app01.Book': 1})

也能够一次性删除多个对象。每一个 QuerySet 都有一个 delete() 方法,它会一次性删除 QuerySet 中全部的对象。

如:删除2005年出版的全部书籍

Book.objects.filter(pub_date__year=2005).delete()

要注意的是: delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 自己。这是一种保护机制,是为了不意外地调用 Book.objects.delete() 方法致使全部的记录被误删除。若是你确认要删除全部的对象,那么你必须显式地调用:

Book.objects.all().delete()

另外,在 Django 删除对象时,会模仿 SQL 约束 ON DELETE CASCADE 的行为,即在删除一个对象时也会删除与它相关联的外键对象。若是不想级联删除,能够设置on_delete参数:

pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)

on_delete的models属性:

CASCADE:这就是默认的选项,级联删除,你无需显性指定它。
PROTECT: 保护模式,若是采用该选项,删除的时候,会抛出ProtectedError错误。
SET_NULL: 置空模式,删除的时候,外键字段被设置为空,前提就是blank=True, null=True,定义该字段的时候,容许为空。
SET_DEFAULT: 置默认值,删除的时候,外键字段设置为默认值,因此定义外键的时候注意加上一个默认值。
SET(): 自定义一个值,该值固然只能是对应的实体了

**set()官方案例**
def get_sentinel_user():
    return get_user_model().objects.get_or_create(username='deleted')[0]

class MyModel(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET(get_sentinel_user),
    )

(6)修改记录

Book.objects.filter(title__startswith="py").update(price=120)

(7)代码:

from django.shortcuts import render, HttpResponse
from app01.models import Book


def index(request):
    # 一、建立记录--------------------------------------------------
    # 方法1:
    # create方法的返回值book_obj就是插入book表中的葵花宝典这本书籍对象
    book_obj = Book.objects.create(title="葵花宝典", state=True, price=100, publish="苹果出版社", pub_date="2012-12-12")

    # 方法2:
    book_obj = Book(title="python教程", state=True, price=100, publish="苹果出版社", pub_date="2012-12-12")
    book_obj.save()

    # 二、查询记录--------------------------------------------------------
    print(Book.objects.all())
    print(Book.objects.filter(title__contains="python"))

    # 三、删除记录--------------------------------------------------------
    res = Book.objects.filter(id=1).delete()  # 删除id=1 的记录
    print(res)  # (1, {'app01.Book': 1})

    # 四、修改记录--------------------------------------------------------
    Book.objects.filter(title__startswith="py").update(price=120)

    return HttpResponse(".....")
views.py

三、多表操做

(1)建立模型

实例:
做者模型(Author):一个做者有姓名和年龄。
做者详细模型(AuthorDetail):把做者的详情放到详情表,包含生日,手机号,家庭住址等信息。做者详情模型和做者模型之间是一对一的关系(one-to-one)
出版商模型(Publish):出版商有名称,所在城市以及email。

书籍模型(Book): 书籍有书名和出版日期,一本书可能会有多个做者,一个做者也能够写多本书,因此做者和书籍的关系就是多对多的关联关系(many-to-many);

一本书只应该由一个出版商出版,因此出版商和书籍是一对多关联关系(one-to-many)。

from django.db import models


class Author(models.Model):
    """做者"""
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    # 与AuthorDetail创建一对一的关系
    authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)


class AuthorDetail(models.Model):
    """做者详细"""
    nid = models.AutoField(primary_key=True)
    birthday = models.DateField()
    telephone = models.BigIntegerField()
    addr = models.CharField(max_length=64)


class Publish(models.Model):
    """出版社"""
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()


class Book(models.Model):
    """书籍"""
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)

    # 与Publish创建一对多的关系,外键字段创建在多的一方
    publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.SET_NULL, null=True, blank=True)
    # 与Author表创建多对多的关系,ManyToManyField能够建在两个模型中的任意一个,自动建立第三张表
    authors = models.ManyToManyField(to='Author', )
models.py

(2)添加表记录

一对多或一对一:

方式1:
   publish_obj=Publish.objects.get(nid=1)  # 获取出版对象
   book_obj=Book.objects.create(title="python",publishDate="2012-12-12",price=100,publish=publish_obj)  # 将对象赋值给publish

方式2:
   book_obj=Book.objects.create(title="python",publishDate="2012-12-12",price=100,publish_id=1)  # 将出版对象对应的id赋值给publish_id

多对多:

    # 一、获取当前生成的书籍对象
    book_obj=Book.objects.create(title="追风筝的人",price=200,publishDate="2012-11-12",publish_id=1)
    # 二、要为书籍绑定的做者对象
    li=Author.objects.filter(name="li").first() # 在Author表中name为li的纪录
    wang=Author.objects.filter(name="wang").first() # 在Author表中name为wang的纪录

    # 三、绑定多对多关系,即向关系表book_authors中添加纪录
    book_obj.authors.add(li, wang)    #  将某些特定的 model 对象添加到被关联对象集合中。      等价于: book_obj.authors.add(*[])

多对多关系其它经常使用API:

book_obj.authors.remove()      # 将某个特定的对象从被关联对象集合中去除。    等价于:  book_obj.authors.remove(*[])
book_obj.authors.clear()       #清空被关联对象集合
book_obj.authors.set()         #先清空再设置

补充: class RelatedManager

"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。它存在于下面两种状况:
ForeignKey关系的“另外一边”。像这样:

from django.db import models

class Reporter(models.Model):
    # ...
    pass

class Article(models.Model):
    reporter = models.ForeignKey(Reporter)

在上面的例子中,管理器reporter.article_set拥有下面的方法。
ManyToManyField关系的两边:

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)

这个例子中,topping.pizza_set 和pizza.toppings都拥有下面的方法。
add(obj1[, obj2, ...])

把指定的模型对象添加到关联对象集中。

例如:

>>> b = Blog.objects.get(id=1)
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.add(e) # Associates Entry e with Blog b.
在上面的例子中,对于ForeignKey关系,e.save()由关联管理器调用,执行更新操做。然而,在多对多关系中使用add()并不会调用任何 save()方法,而是由QuerySet.bulk_create()建立关系。

延伸:

# 1 *[]的使用
>>> book_obj = Book.objects.get(id=1)
>>> author_list = Author.objects.filter(id__gt=2)
>>> book_obj.authors.add(*author_list)


# 2 直接绑定主键
book_obj.authors.add(*[1,3])  # 将id=1和id=3的做者对象添加到这本书的做者集合中
                              # 应用: 添加或者编辑时,提交做者信息时能够用到.

create(**kwargs)

#建立一个新的对象,保存对象,并将它添加到关联对象集之中。返回新建立的对象:

>>> b = Blog.objects.get(id=1)
>>> e = b.entry_set.create(
...     headline='Hello',
...     body_text='Hi',
...     pub_date=datetime.date(2005, 1, 1)
... )

# No need to call e.save() at this point -- it's already been saved.
这彻底等价于(不过更加简洁于):

>>> b = Blog.objects.get(id=1)
>>> e = Entry(
...     blog=b,
...     headline='Hello',
...     body_text='Hi',
...     pub_date=datetime.date(2005, 1, 1)
... )
>>> e.save(force_insert=True)
#要注意咱们并不须要指定模型中用于定义关系的关键词参数。在上面的例子中,咱们并无传入blog参数给create()。Django会明白新的 Entry对象blog 应该添加到b中。

remove(obj1[, obj2, ...])

#从关联对象集中移除执行的模型对象:

>>> b = Blog.objects.get(id=1)
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.remove(e) # Disassociates Entry e from Blog b.
#对于ForeignKey对象,这个方法仅在null=True时存在。

clear()

#从关联对象集中移除一切对象。

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.clear()
#注意这样不会删除对象 —— 只会删除他们之间的关联。

#就像 remove() 方法同样,clear()只能在 null=True的ForeignKey上被调用。

set()方法
先清空,再设置,编辑书籍时便可用到

注意
对于全部类型的关联字段,add()、create()、remove()和clear(),set()都会立刻更新数据库。换句话说,在关联的任何一端,都不须要再调用save()方法。
直接赋值:
经过赋值一个新的可迭代的对象,关联对象集能够被总体替换掉。

>>> new_list = [obj1, obj2, obj3]
>>> e.related_set = new_list

若是外键关系知足null=True,关联管理器会在添加new_list中的内容以前,首先调用clear()方法来解除关联集中一切已存在对象的关联。不然, new_list中的对象会在已存在的关联的基础上被添加。

(3)基于对象的跨表查询

  • 一对多查询(Publish 与 Book)

正向查询(按字段:publish):

#查询主键为1的书籍的出版社所在的城市
book_obj = models.Book.objects.filter(nid=1).first()
city = book_obj.publish.city
print(city)

反向查询(按表名:book_set):

# 查询与苹果出版社关联的全部书籍对象集合
publish_obj = models.Publish.objects.get(name="苹果出版社")
book_list = publish_obj.book_set.all()
for book_obj in book_list:
    print(book_obj.title)
  • 一对一查询(Author 与 AuthorDetail)

正向查询(按字段:authorDetail):

#查询张三的手机号
tel = models.Author.objects.get(name="张三").authorDetail.telephone
print(tel)

反向查询(按表名:author):

# 查询全部住址在上海的做者的姓名
author_detail_list = models.AuthorDetail.objects.filter(addr="上海")
for author_detail_obj in author_detail_list:
    print(author_detail_obj.author.name)
  • 多对多查询 (Author 与 Book)

正向查询(按字段:authors):

# 查询python全部做者的名字以及手机号
authors = models.Book.objects.get(title="python").authors.all()
for author_obj in authors:
    print(author_obj.name, author_obj.authorDetail.telephone)

反向查询(按表名:book_set):

#查询jack出过的全部书籍的名字
book_list = models.Author.objects.filter(name="jack").first().book_set.all()
for book in book_list:
    print(book.title)

注意:
你能够经过在 ForeignKey() 和ManyToManyField()的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,若是 Article model 中作一下更改:

publish = ForeignKey(Book, related_name='bookList')

这样就能够直接经过 book_list = models.Author.objects.filter(name="jack").first().bookList.all() 来获取对象。

(4)基于双下划线的跨表查询

使用两个下划线来连接模型(model)间关联字段的名称,直到最终连接到你想要的 model 为止。

关键点:正向查询按字段,反向查询按表名。

  • 一对多
# 查询苹果出版社出版过的全部书籍的名字与价格
# 正向查询
books1 = models.Book.objects.filter(publish__name="苹果出版社").values("title", "price")
print(books1)
# 反向查询
books2 = models.Publish.objects.filter(name="苹果出版社").values("book__title", "book__price")
print(books2)
  • 多对多
# 查询jack出过的全部书籍的名字
# 正向查询
books1 = models.Book.objects.filter(authors__name="jack").values("title")
print(books1)
# 反向查询
books2 = models.Author.objects.filter(name="jack").values("book__title")
print(books2)
  • 混合查询
# 查询橘子出版社出版过的全部书籍的名字以及做者的姓名
# 正向查询
info_list = models.Book.objects.filter(publish__name="橘子出版社").values("title", "authors__name")
print(info_list)
# 反向查询
info = models.Publish.objects.filter(name="橘子出版社").values("book__title", "book__authors__name")
print(info)

# 手机号以2开头的做者出版过的全部书籍名称以及出版社名称
# 正向查询
info1 = models.AuthorDetail.objects.filter(telephone__startswith=2).values("author__book__title",
                                                                           "author__book__publish__name")
print(info1)
# 反向查询
info2 = models.Book.objects.filter(authors__authorDetail__telephone__startswith=2).values("title", "publish__name")
print(info2)

 

(5)聚合查询

aggregate(*args, **kwargs)

# 计算全部图书的平均价格
from django.db.models import Avg
avg_price = models.Book.objects.all().aggregate(Avg('price'))
print(avg_price)  # {'price__avg': 113.333333}

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

from django.db.models import Avg
avg_price = models.Book.objects.all().aggregate(avg_price=Avg('price'))
print(avg_price)  # {'avg_price': 113.333333}

向aggregate()子句中添加参数:

from django.db.models import Avg, Max, Min
res = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
print(res)  # {'price__avg': 113.333333, 'price__max': Decimal('120.00'), 'price__min': Decimal('100.00')}

(6)分组查询 annotate(*args, **kwargs)

在models.py中添加以下模型:

class Employee(models.Model):
    """员工表"""
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary = models.DecimalField(max_digits=8, decimal_places=2)
    dep = models.ForeignKey(to="Department", on_delete=models.SET_NULL, null=True, blank=True)


class Department(models.Model):
    """部门表"""
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)

数据库迁移,并在表中添加数据:

单表查询:

# 查询每个部门以及对应的员工数
"""
SQL查询:
select dep, Count(*) from employee group by dep;
"""
from django.db.models import Count
info = models.Employee.objects.all().values("dep").annotate(Count("nid"))
print(info)
"""
结果:
<QuerySet [
    {'dep': 1, 'nid__count': 2}, 
    {'dep': 2, 'nid__count': 3}, 
    {'dep': 3, 'nid__count': 2}
]>
"""

多表查询:

# 查询每个部门名称以及对应的员工数
"""
SQL查询:
select dep.name, Count(*) from employee
left join department on employee.dep_id=department.nid
group by department.name;
"""
from django.db.models import Count
info = models.Department.objects.all().annotate(c=Count("employee")).values("name", "c")
print(info)
"""
结果:
<QuerySet [
    {'name': '财务部', 'c': 2}, 
    {'name': '开发部', 'c': 3}, 
    {'name': '人力资源部', 'c': 2}
]>
"""

 

# 统计每一本书的做者个数
res = models.Book.objects.all().annotate(count=Count("authors")).values("title", "count")
print(res)

# 统计每个出版社的最便宜的书
res = models.Publish.objects.all().annotate(min_price=Min("book__price"))
for obj in res:
    print(obj.name, obj.min_price)

# 统计每一本以Py开头的书籍的做者个数
res = models.Book.objects.filter(title__startswith="Py").annotate(count=Count("authors")).values("title", "count")
print(res)

# 统计不止一个做者的图书
res = models.Book.objects.all().annotate(author_num=Count("authors")).\
    filter(author_num__gt=1).values("title", "author_num")
print(res)

# 根据一本图书做者数量的多少对查询集QuerySet进行排序
res = models.Book.objects.all().annotate(author_num=Count("authors")).\
    order_by("author_num").values("title", "author_num")
print(res)

# 查询各个做者出的书的总价格
res = models.Author.objects.all().annotate(sum=Sum("book__price")).values("name", "sum")
print(res)

 

(7)F查询

  • 比较同一个 model 实例中两个不一样字段的值
# 查询评论数大于收藏数的书籍

from django.db.models import F
Book.objects.filter(commnetNum__gt=F('keepNum'))
  • F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操做
# 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commnetNum__gt=F('keepNum')*2)
#将每一本书的价格提升30元:
Book.objects.all().update(price=F("price")+30)

(8)Q查询

filter() 等方法中的关键字参数查询都是一块儿进行“AND” 的。 若是你须要执行更复杂的查询(例如OR 语句),你可使用Q 对象。

Q 对象可使用 &(and) 和 |(或) 操做符组合起来。当一个操做符在两个Q 对象上使用时,它产生一个新的Q 对象。

from django.db.models import Q
# 查询做者为jack或者wang的书籍
bookList=Book.objects.filter(Q(authors__name="jack")|Q(authors__name="wang"))

Q 对象可使用 ~ 操做符取反,这容许组合正常的查询和取反(NOT) 查询:

# 查询做者叫wang而且出版时间不在2017年的书籍
bookList=Book.objects.filter(Q(authors__name="wang") & ~Q(publishDate__year=2017)).values_list("title")

查询函数能够混合使用Q 对象和关键字参数。全部提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一块儿。可是,若是出现Q 对象,它必须位于全部关键字参数的前面。

# 2016年或2017年出版的名字包含python的书籍
bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),title__icontains="python")
from django.shortcuts import render, HttpResponse
from django.db.models import Count, Min, Sum
from app01 import models


def index2(request):
    # 一、建立记录--------------------------------------------------
    detail_obj1 = models.AuthorDetail.objects.create(birthday="1963-12-10", telephone=128938383838, addr="上海")
    detail_obj2 = models.AuthorDetail.objects.create(birthday="1976-3-23", telephone=338383838, addr="南京")
    detail_obj3 = models.AuthorDetail.objects.create(birthday="1977-12-3", telephone=2348383838, addr="广州")

    publish_obj1 = models.Publish.objects.create(name="苹果出版社", city="天津", email="cddd@163.com")
    publish_obj2 = models.Publish.objects.create(name="橘子出版社", city="深圳", email="wewe@163.com")
    publish_obj3 = models.Publish.objects.create(name="北京出版社", city="北京", email="sff@163.com")

    author_obj1 = models.Author.objects.create(name="mark", age=43, authorDetail_id=2)  # 经过id添加外键,authorDetail_id表示id
    author_obj2 = models.Author.objects.create(name="张三", age=42,
                                               authorDetail=detail_obj3)  # 经过对象添加外键,authorDetail表示一个做者详情对象
    author_obj3 = models.Author.objects.create(name="jack", age=56, authorDetail_id=1)

    book_obj1 = models.Book.objects.create(title="Python", publishDate="2012-12-12", price=100, publish=publish_obj1)
    book_obj2 = models.Book.objects.create(title="java", publishDate="2014-12-12", price=120, publish_id=2)
    book_obj3 = models.Book.objects.create(title="数据库", publishDate="2014-3-12", price=120, publish_id=3)
    # 多对多新增记录:
    book_obj1.authors.add(author_obj1, author_obj2)
    book_obj2.authors.add(author_obj2, author_obj3)
    book_obj3.authors.add(author_obj3)

    # 二、基于对象的跨表查询--------------------------------------------------------
    # 一对多
    # 正向查询:按字段  查询主键为1的书籍的出版社所在的城市
    book_obj = models.Book.objects.filter(nid=1).first()
    city = book_obj.publish.city
    print(city)

    # 反向查询 :按表名:book_set 查询与苹果出版社关联的全部书籍对象集合
    publish_obj = models.Publish.objects.get(name="苹果出版社")
    book_list = publish_obj.book_set.all()
    for book_obj in book_list:
        print(book_obj.title)

    # 一对一
    # 正向查询:按字段 查询张三的手机号
    tel = models.Author.objects.get(name="张三").authorDetail.telephone
    print(tel)

    # 反向查询(按表名:author):查询全部住址在上海的做者的姓名
    author_detail_list = models.AuthorDetail.objects.filter(addr="上海")
    for author_detail_obj in author_detail_list:
        print(author_detail_obj.author.name)

    # 多对多
    # 正向查询(按字段:authors):查询python全部做者的名字以及手机号
    authors = models.Book.objects.get(title="python").authors.all()
    for author_obj in authors:
        print(author_obj.name, author_obj.authorDetail.telephone)

    # 反向查询(按表名:book_set):查询jack出过的全部书籍的名字
    book_list = models.Author.objects.filter(name="jack").first().book_set.all()
    for book in book_list:
        print(book.title)

    # 三、基于双下划线的跨表查询--------------------------------------------------------
    # 一对多
    # 查询苹果出版社出版过的全部书籍的名字与价格
    # 正向查询
    books1 = models.Book.objects.filter(publish__name="苹果出版社").values("title", "price")
    print(books1)
    # 反向查询
    books2 = models.Publish.objects.filter(name="苹果出版社").values("book__title", "book__price")
    print(books2)

    # 多对多
    # 查询jack出过的全部书籍的名字
    # 正向查询
    books1 = models.Book.objects.filter(authors__name="jack").values("title")
    print(books1)
    # 反向查询
    books2 = models.Author.objects.filter(name="jack").values("book__title")
    print(books2)

    # 混合使用
    # 查询橘子出版社出版过的全部书籍的名字以及做者的姓名
    # 正向查询
    info_list = models.Book.objects.filter(publish__name="橘子出版社").values("title", "authors__name")
    print(info_list)
    # 反向查询
    info = models.Publish.objects.filter(name="橘子出版社").values("book__title", "book__authors__name")
    print(info)

    # 手机号以2开头的做者出版过的全部书籍名称以及出版社名称
    # 正向查询
    info1 = models.AuthorDetail.objects.filter(telephone__startswith=2).values("author__book__title",
                                                                               "author__book__publish__name")
    print(info1)
    # 反向查询
    info2 = models.Book.objects.filter(authors__authorDetail__telephone__startswith=2).values("title", "publish__name")
    print(info2)

    # 四、聚合查询--------------------------------------------------------
    # 计算全部图书的平均价格
    from django.db.models import Avg
    avg_price = models.Book.objects.all().aggregate(avg_price=Avg('price'))
    print(avg_price)  # {'avg_price': 113.333333}

    from django.db.models import Avg, Max, Min
    res = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
    print(res)  # {'price__avg': 113.333333, 'price__max': Decimal('120.00'), 'price__min': Decimal('100.00')}

    # 五、分组查询----------------------------------------------------------
    # 查询每个部门以及对应的员工数
    """
    SQL查询:
    select dep, Count(*) from employee group by dep;
    """
    from django.db.models import Count
    info = models.Employee.objects.all().values("dep").annotate(Count("nid"))
    print(info)
    """
    结果:
    <QuerySet [
        {'dep': 1, 'nid__count': 2},
        {'dep': 2, 'nid__count': 3},
        {'dep': 3, 'nid__count': 2}
    ]>
    """

    # 查询每个部门名称以及对应的员工数
    """
    SQL查询:
    select dep.name, Count(*) from employee
    left join department on employee.dep_id=department.nid
    group by department.name;
    """
    from django.db.models import Count
    info = models.Department.objects.all().annotate(c=Count("employee")).values("name", "c")
    print(info)
    """
    结果:
    <QuerySet [
        {'name': '财务部', 'c': 2},
        {'name': '开发部', 'c': 3},
        {'name': '人力资源部', 'c': 2}
    ]>
    """

    # 统计每一本书的做者个数
    res = models.Book.objects.all().annotate(count=Count("authors")).values("title", "count")
    print(res)

    # 统计每个出版社的最便宜的书
    res = models.Publish.objects.all().annotate(min_price=Min("book__price"))
    for obj in res:
        print(obj.name, obj.min_price)

    # 统计每一本以Py开头的书籍的做者个数
    res = models.Book.objects.filter(title__startswith="Py").annotate(count=Count("authors")).values("title", "count")
    print(res)

    # 统计不止一个做者的图书
    res = models.Book.objects.all().annotate(author_num=Count("authors")).\
        filter(author_num__gt=1).values("title", "author_num")
    print(res)

    # 根据一本图书做者数量的多少对查询集QuerySet进行排序
    res = models.Book.objects.all().annotate(author_num=Count("authors")).\
        order_by("author_num").values("title", "author_num")
    print(res)

    # 查询各个做者出的书的总价格
    res = models.Author.objects.all().annotate(sum=Sum("book__price")).values("name", "sum")
    print(res)

    return HttpResponse("ok")
views.py

6、Django组件——cookie与session

一、会话跟踪技术

在JavaWeb中,客户向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器,会话结束。 在一个会话的多个请求中共享数据,这就是会话跟踪技术。

会话跟踪技术使用Cookie或session完成。

咱们知道HTTP协议是无状态协议,也就是说每一个请求都是独立的!没法记录前一次请求的状态。但HTTP协议中可使用Cookie来完成会话跟踪!在Web开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。

二、cookie

(1)什么是cookie?

Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是key-value结构,相似于一个python中的字典。

当浏览器第一次访问服务器,由服务器建立Cookie,而后经过响应发送给客户端。客户端会保存Cookie,并会标注出Cookie的来源(哪一个服务器的Cookie)。当客户端再次向服务器发出请求时会把全部这个服务器Cookie包含在请求中发送给服务器,这样服务器就能够识别客户端了!

(2)cookie规范

  • Cookie大小上限为4KB;
  • 一个服务器最多在客户端浏览器上保存20个Cookie;
  • 一个浏览器最多保存300个Cookie;
    上面的数据只是HTTP的Cookie规范,但在浏览器大战的今天,一些浏览器为了战胜对手,为了展示本身的能力起见,可能对Cookie规范“扩展”了一些,例如每一个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现把你硬盘占满的可能!
  • 不一样浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,而后由IE保存起来,当你在使用FireFox访问服务器时,不可能把IE保存的Cookie发送给服务器。

(3)Cookie与HTTP头
Cookie是经过HTTP请求头和响应头在客户端和服务器端传递的:

  • Cookie:请求头,客户端发送给服务器端;
  • 格式:Cookie: a=A; b=B; c=C。即多个Cookie用分号分隔,
  • Set-Cookie:响应头,服务器端发送给客户端;
  • 一个Cookie对象一个Set-Cookie: Set-Cookie: a=A Set-Cookie: b=B Set-Cookie: c=C
  • Cookie的覆盖

若是服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie: a=A;第二请求服务器端发送的是:Set-Cookie: a=AA,那么客户端只留下一个Cookie,即:a=AA。

(4)django中的cookie语法

  • 设置cookie:
rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect()  
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)

源码:

class HttpResponseBase:
        def set_cookie(self, key,                 键
                     value='',            值
                     max_age=None,        超长时间
                              cookie须要延续的时间(以秒为单位)
                              若是参数是\ None`` ,这个cookie会延续到浏览器关闭为止。
                     expires=None,        超长时间
                                 expires默认None ,cookie失效的实际日期/时间。
                                
                     path='/',           Cookie生效的路径,
                                                 浏览器只会把cookie回传给带有该路径的页面,这样能够避免将
                                                 cookie传给站点中的其余的应用。
                                                 / 表示根路径,特殊的:根路径的cookie能够被任何url的页面访问
                    
                             domain=None,         Cookie生效的域名

                                                  你可用这个参数来构造一个跨站cookie。
                                                  如, domain=".example.com"
                                                  所构造的cookie对下面这些站点都是可读的:
                                                  www.example.com 、 www2.example.com
                                 和an.other.sub.domain.example.com 。
                                                  若是该参数设置为 None ,cookie只能由设置它的站点读取。
                     secure=False,        若是设置为 True ,浏览器将经过HTTPS来回传cookie。
                     httponly=False       只能http协议传输,没法被JavaScript获取
                                                 (不是绝对,底层抓包能够获取到也能够被覆盖)
                  ): pass
  • 获取cookie:
request.COOKIES
  • 删除cookie:
response.delete_cookie("cookie_key",path="/",domain=name)

 

(5)案例1:显示上次访问时间。 
(6)案例2:显示上次浏览过的商品。

三、session

(1)session介绍

Session是服务器端技术,利用这个技术,服务器在运行时能够为每个用户的浏览器建立一个其独享的session对象,因为 session为用户浏览器独享,因此用户在访问服务器的web资源时 ,能够把各自的数据放在各自的session中,当用户再去访问该服务器中的其它web资源时,其它web资源再从用户各自的session中 取出数据为用户服务。

(2)django中session语法

#一、设置Sessions值
          request.session['session_name'] ="admin"
#二、获取Sessions值
          session_name = request.session["session_name"]
#三、删除Sessions值
          del request.session["session_name"]
#四、删除当前的会话数据并删除会话的Cookie。这用于确保前面的会话数据不能够再次被用户的浏览器访问
          flush()
#五、获取session值
        get(key, default=None)
        fav_color = request.session.get('fav_color', 'red')  
#六、删除
        pop(key)
        fav_color = request.session.pop('fav_color')  
#七、查看全部key
        keys()
#八、查询全部键值对
        items()  
#九、setdefault()  
#十、用户session的随机字符串
        request.session.session_key

# 十一、将全部Session失效日期小于当前日期的数据删除
        request.session.clear_expired()

#十二、 检查用户session的随机字符串在数据库中是否存在
        request.session.exists("session_key")

# 1三、删除当前用户的全部Session数据
        request.session.delete("session_key")

#1四、设置超时时间
        request.session.set_expiry(value)
            * 若是value是个整数,session会在xx秒后失效。
            * 若是value是个datatime或timedelta,session就会在这个时间后失效。
            * 若是value是0,用户关闭浏览器session就会失效。
            * 若是value是None,session会依赖全局session失效策略。            

(3)session配置

Django默认支持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,默认修改以后才保存(默认)

(4)登陆案例

#login.html:登陆页面,提供登陆表单;
#index1.html:主页,显示当前用户名称,若是没有登陆,显示您还没登陆;
#index2.html:主页,显示当前用户名称,若是没有登陆,显示您还没登陆;

思考:若是第二我的再次再同一个浏览器上登陆,django-session表会怎样?

7、Django组件——forms

一、forms组件的校验字段功能

如:用户注册

模型models.py:

class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    email = models.EmailField()
    tel = models.CharField(max_length=32)

模板 register.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>
<body>

<form action="" method="post">
    {% csrf_token %}
    <div>
        <label for="user">用户名</label>
        <p><input type="text" name="name" id="name"></p>
    </div>
    <div>
        <label for="pwd">密码</label>
        <p><input type="password" name="pwd" id="pwd"></p>
    </div>
    <div>
        <label for="r_pwd">确认密码</label>
        <p><input type="password" name="r_pwd" id="r_pwd"></p>
    </div>
    <div>
        <label for="email">邮箱</label>
        <p><input type="text" name="email" id="email"></p>
    </div>
    <input type="submit">
</form>

</body>
</html>
register.html

视图views.py:

from django import forms
from django.forms import widgets

from django.shortcuts import render, HttpResponse
from app01 import models

wid_01 = widgets.TextInput(attrs={"class": "form-control"})
wid_02 = widgets.PasswordInput(attrs={"class": "form-control"})


class UserForm(forms.Form):
    name = forms.CharField(max_length=32, widget=wid_01)
    pwd = forms.CharField(max_length=32, widget=wid_02)
    r_pwd = forms.CharField(max_length=32, widget=wid_02)
    email = forms.EmailField(widget=wid_01)
    tel = forms.CharField(max_length=32, widget=wid_01)


def register(request):
    if request.method == "POST":
        form = UserForm(request.POST)
        if form.is_valid():  # 字段合法
            print(1, form.cleaned_data)  # 全部干净的字段以及对应的值
            return HttpResponse("OK")
        else:
            print(2, form.cleaned_data)  # 合法字段
            print(3, form.errors, type(form.errors))  # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(4, form.errors.get("email"))  # 当输入错误格式的email时就会捕获到这个错误信息:ErrorList ["错误信息",]
            return HttpResponse(str(form.errors))

    form = UserForm()
    return render(request, "register.html", locals())
views.py

二、forms组件的渲染标签功能

views.py:

方式1:

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h3>注册页面</h3>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-lg-offset-3">

            <form action="" method="post">
                {% csrf_token %}
                <div>
                    <label for="">用户名</label>
                    {{ form.name }}
                </div>
                <div>
                    <label for="">密码</label>
                    {{ form.pwd }}
                </div>
                <div>
                    <label for="">确认密码</label>
                    {{ form.r_pwd }}
                </div>
                <div>
                    <label for=""> 邮箱</label>
                    {{ form.email }}
                </div>
                <div>
                    <label for=""> 电话</label>
                    {{ form.tel }}
                </div>

                <input type="submit" class="btn btn-default pull-right">
            </form>
        </div>
    </div>
</div>
</body>
</html>
register.html

效果:

方式2:

<form action="" method="post">
    {% csrf_token %}

    {% for field in form %}
        <div>
            <label for="">{{ field.label }}</label>
            {{ field }}
        </div>
    {% endfor %}
    <input type="submit" class="btn btn-default pull-right">
</form>

效果:

若是想要显示中文,就须要到UserForm类中设置label属性:

方式3:

<form action="" method="post">
    {% csrf_token %}

    {{ form.as_p }}
    <input type="submit" class="btn btn-default pull-right">

</form>

三、在页面显示错误提示信息

from django import forms
from django.forms import widgets

from django.shortcuts import render, HttpResponse
from app01 import models

wid_01 = widgets.TextInput(attrs={"class": "form-control"})
wid_02 = widgets.PasswordInput(attrs={"class": "form-control"})


class UserForm(forms.Form):
    name = forms.CharField(max_length=32, widget=wid_01, label="用户名")
    pwd = forms.CharField(max_length=32, widget=wid_02)
    r_pwd = forms.CharField(max_length=32, widget=wid_02)
    email = forms.EmailField(widget=wid_01)
    tel = forms.CharField(max_length=32, widget=wid_01)


def register(request):
    if request.method == "POST":
        form = UserForm(request.POST)
        if form.is_valid():  # 字段合法
            print(1, form.cleaned_data)  # 全部干净的字段以及对应的值
            return HttpResponse("OK")
        else:
            print(2, form.cleaned_data)  # 合法字段
            print(3, form.errors, type(form.errors))  # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(4, form.errors.get("email"))  # 当输入错误格式的email时就会捕获到这个错误信息:ErrorList ["错误信息",]
            return render(request, "register.html", locals())

    form = UserForm()  # 实例化
    return render(request, "register.html", locals())
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h3>注册页面</h3>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-lg-offset-3">

            <form action="" method="post">
                {% csrf_token %}

                {% for field in form %}
                    <div>
                        <label for="">{{ field.label }}</label>
                        {{ field }} <span class="pull-right" style="color: red">{{ field.errors.0 }}</span>
                    </div>
                {% endfor %}
                <input type="submit" class="btn btn-default">

            </form>

        </div>
    </div>
</div>
</body>
</html>
register.html

四、局部钩子与全局钩子

from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError

from django.shortcuts import render, HttpResponse
from app01 import models

wid_01 = widgets.TextInput(attrs={"class": "form-control"})
wid_02 = widgets.PasswordInput(attrs={"class": "form-control"})


class UserForm(forms.Form):
    name = forms.CharField(max_length=32, widget=wid_01, label="用户名")
    pwd = forms.CharField(max_length=32, widget=wid_02, label="密码")
    r_pwd = forms.CharField(max_length=32, widget=wid_02, label="确认密码")
    email = forms.EmailField(widget=wid_01, label="邮箱")
    tel = forms.CharField(max_length=32, widget=wid_01, label="电话")

    # 局部钩子
    def clean_name(self):  # 函数名规则:clean_字段名
        """规定用户名不能为纯数字"""
        val = self.cleaned_data.get("name")
        if not val.isdigit():
            return val
        else:
            raise ValidationError("用户名不能是纯数字!")

    # 全局钩子
    def clean(self):
        """校验两次密码必须一致"""
        pwd = self.cleaned_data.get("pwd")
        r_pwd = self.cleaned_data.get("r_pwd")

        if pwd == r_pwd:
            return self.cleaned_data
        else:
            raise ValidationError('两次密码不一致!')


def register(request):
    if request.method == "POST":
        form = UserForm(request.POST)
        if form.is_valid():  # 字段合法
            print(1, form.cleaned_data)  # 全部干净的字段以及对应的值
            return HttpResponse("OK")
        else:
            clean_error = form.errors.get("__all__")
            print(clean_error)
            return render(request, "register.html", locals())

    form = UserForm()  # 实例化
    return render(request, "register.html", locals())
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h3>注册页面</h3>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-lg-offset-3">

            <form action="" method="post" novalidate>
                {% csrf_token %}

                {% for field in form %}
                    <div>
                        <label for="">{{ field.label }}</label>
                        {{ field }}
                        <span class="pull-right" style="color: red">
                            {% if field.label == '确认密码' %}
                                <span>{{ clean_error.0 }}</span>
                            {% endif %}
                            {{ field.errors.0 }}
                    </span>
                    </div>
                {% endfor %}
                <input type="submit" class="btn btn-default">
            </form>

        </div>
    </div>
</div>
</body>
</html>
register.html

 

8、Django组件——用户认证

一、auth模块

from django.contrib import auth

 (1)authenticate()
提供了用户认证,即验证用户名以及密码是否正确,通常须要username和password两个关键字参数。
若是认证信息有效,会返回一个 User 对象。当咱们试图登录一个从数据库中直接取出来不通过authenticate()的User对象就会报错。

user=authenticate(username='amy',password='123456')

(2)login(HttpRequest, user)  
该函数接受一个HttpRequest对象,以及一个认证了的User对象,
此函数使用django的session框架给某个已认证的用户附加上session id等信息。

from django.contrib.auth import authenticate, login

def login(request):
  username = request.POST.get('username')
  password = request.POST.get('password')
  user = authenticate(username=username, password=password)
  if user is not None:
    login(request, user)
    # Redirect to a success page.
    ...
  else:
    # Return an 'invalid login' error message.
    ...

(3)logout(request) 注销用户

当调用该函数时,当前请求的session信息会所有清除。该用户即便没有登陆,使用该函数也不会报错。

from django.contrib.auth import logout

def logout_view(request):
  logout(request)
  # Redirect to a success page.

二、user对象

user 对象属性:username, password(必填项),password是用哈希算法保存到数据库的。

(1) is_authenticated()
 用于检查用户是否已经经过了认证。返回值为 True或False 。
经过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是代表用户成功的经过了认证。

在后台用request.user.is_authenticated()判断用户是否已经登陆,若是True则能够向前台展现request.user.name
要求:
1 用户登录后才能访问某些页面,
2 若是用户没有登陆就访问该页面的话直接跳到登陆页面
3 用户在跳转的登录界面中完成登录后,自动访问跳转到以前访问的地址

方法1:

def my_view(request):
  if not request.user.is_authenticated():  # 若是没有登陆就跳转到登陆页面
    return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))

方法2:

使用装饰器login_requierd()

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
  ...

若用户没有登陆,则会跳转到django默认的登陆URL '/accounts/login/ ' (这个值能够在settings文件中经过LOGIN_URL进行修改)。并传递当前访问url的绝对路径 (登录成功后,会重定向到该路径)。

(2)create_user() 建立用户

from django.contrib.auth.models import User
user = User.objects.create_user(username='xxx',password='xxx',email='xxx'

(3)check_password(password) 校验密码
如,当用户须要修改密码的时候,首先要让他输入原来的密码 ,若是给定的字符串经过了密码检查,返回 True

(4)set_password()  修改密码

user = User.objects.get(username='amy')
user.set_password(password='111')
user.save

(5)示例

注册:

from django.contrib.auth.models import User

from django.shortcuts import render, HttpResponse, redirect
from app01 import models


def sign_up(request):
    state = None
    if request.method == 'POST':
        username = request.POST.get('username', '')
        password = request.POST.get('password', '')
        repeat_password = request.POST.get('repeat_password', '')
        email = request.POST.get('email', '')

        if not password == repeat_password:
            state = "两次密码不一致"
        else:
            # 检查用户名是否已经存在
            if User.objects.filter(username=username):
                state = '用户名已经存在!'
            else:
                new_user = User.objects.create_user(username=username, password=password, email=email)
                new_user.save()

                return redirect('/index/')

    content = {'state': state, 'user': None}
    return render(request, 'sign_up.html', {"content": content})

sign_up.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    {% csrf_token %}
    用户名:<input type="text" name="username"/>
    密码:<input type="password" name="password"/>
    确认密码:<input type="password" name="repeat_password"/>
    邮箱:<input type="email" name="email"/>
    <input type="submit" value="注册"/>
    {% if content.state %}
        <span style="color: red">{{ content.state }}</span>
    {% endif %}
</form>
</body>
</html>

修改密码:

from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login, logout

from django.shortcuts import render, HttpResponse, redirect
from app01 import models


@login_required
def set_password(request):
    """需改密码"""
    user = request.user
    state = None
    if request.method == 'POST':
        old_password = request.POST.get('old_password', '')
        new_password = request.POST.get('new_password', '')
        repeat_password = request.POST.get('repeat_password', '')
        if user.check_password(old_password):
            if not new_password:
                state = '不能为空'
            elif new_password != repeat_password:
                state = '两次密码不一致'
            else:
                user.set_password(new_password)
                user.save()
                return redirect("/accounts/login/")
        else:
            state = '密码错误'
    content = {
        'user': user,
        'state': state,
    }
    return render(request, 'set_password.html', {"content": content})


def login_view(request):
    """登陆"""
    next_url = request.GET.get("next")

    if request.method == "POST":
        username = request.POST.get("username")
        password = request.POST.get("password")

        user = authenticate(username=username, password=password)
        if user is not None:
            login(request, user)
            if next_url:
                return redirect(next_url)
            else:
                return HttpResponse("ok")
    return render(request, "login.html")
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    {% csrf_token %}
    <p>用户名:<input type="text" name="username"/></p>
    <p>密码:<input type="password" name="password"/></p>
    <p><input type="submit" value="登陆"/></p>
</form>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    {% csrf_token %}
    <p>旧密码:<input type="password" name="old_password"/></p>
    <p>新密码:<input type="password" name="new_password"/></p>
    <p>确认密码:<input type="password" name="repeat_password"/></p>
    <p><input type="submit" value="提交"/></p>
</form>
</body>
</html>
set_password.html

 

9、Django组件——中间件

一、中间件概念

中间件,是介于request与response处理之间的一道处理过程,相对比较轻量级,在全局上改变django的输入与输出,便可以对request和response做批量操做。

当请求进来时会穿过一层层的中间件到达视图,视图做相应处理后返回响应数据,响应数据再经过层层中间件返回给用户:

二、自定义中间件

(1)中间件中有五种方法

process_request()
process_view()
process_response()
process_exception()

(2)自定义中间件,即编写一个类,继承MiddlewareMixin

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class Md1(MiddlewareMixin):
    def process_request(self, request):
        print("Md1请求")

    def process_response(self, request, response):
        print("Md1返回")
        return response


class Md2(MiddlewareMixin):
    def process_request(self, request):
        print("Md2请求")
        # return HttpResponse("Md2中断")  # 加上这句,请求不会到达视图函数里面

    def process_response(self, request, response):
        print("Md2返回")
        return response
my_middlewares.py

settings.py:

from django.shortcuts import render, HttpResponse


def index(request):
    print("in view...")
    return HttpResponse("OK")
views.py

结果:

 

修改middleware以下:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class Md1(MiddlewareMixin):
    def process_request(self, request):
        print("Md1请求")
        # return HttpResponse("Md1中断")

    def process_response(self, request, response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("Md1 view")


class Md2(MiddlewareMixin):
    def process_request(self, request):
        print("Md2请求")
        # return HttpResponse("Md2中断")

    def process_response(self, request, response):
        print("Md2返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("Md2 view")
my_middlewares.py

结果:

结果:

流程:

在process_view中能够调用视图函数:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class Md1(MiddlewareMixin):
    def process_request(self, request):
        print("Md1请求")
        # return HttpResponse("Md1中断")

    def process_response(self, request, response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        # return HttpResponse("hello")
        response = callback(request, *callback_args, **callback_kwargs)
        return response


class Md2(MiddlewareMixin):
    def process_request(self, request):
        print("Md2请求")
        # return HttpResponse("Md2中断")

    def process_response(self, request, response):
        print("Md2返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("Md2 view")
my_middlewares.py

结果:

当process_view有返回值时,会越过其余的process_view以及视图函数,可是全部的process_response都还会执行。

修改middleware:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class Md1(MiddlewareMixin):

    def process_request(self, request):
        print("Md1请求")

    def process_response(self, request, response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("md1 process_view...")

    def process_exception(self, request, exception):
        print("md1 process_exception...")
        return HttpResponse("error")


class Md2(MiddlewareMixin):
    def process_request(self, request):
        print("Md2请求")

    def process_response(self, request, response):
        print("Md2返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("md2 process_view...")

    def process_exception(self, request, exception):
        print("md2 process_exception...")
        return HttpResponse("error")
my_middlewares.py

结果:

当视图函数出现异常时,process_exception就会被触发:

三、自带的中间件:

'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',

 

10、Django组件——分页器

一、数据量不大的状况

import random
from django.shortcuts import render, HttpResponse
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

from app01.models import *


def index(request):
    # 批量导入数据:
    # book_list = []
    # for i in range(100):
    #     book_list.append(Book(title="book" + str(i), price=30.00 + i * 2, publishDate="2012-10-23",
    #                           publish_id=random.randint(1, 3)))
    # Book.objects.bulk_create(book_list)  # 批量建立记录

    book_list = Book.objects.all()

    paginator = Paginator(book_list, 10)  # 每页10条

    print("count:", paginator.count)  # 数据总数
    print("num_pages", paginator.num_pages)  # 总页数
    print("page_range", paginator.page_range)  # 页码的列表

    page1 = paginator.page(1)  # 第1页的page对象
    for i in page1:  # 遍历第1页的全部数据对象
        print(i)

    print(page1.object_list)  # 第1页的全部数据

    page2 = paginator.page(2)

    print(page2.has_next())  # 是否有下一页
    print(page2.next_page_number())  # 下一页的页码
    print(page2.has_previous())  # 是否有上一页
    print(page2.previous_page_number())  # 上一页的页码

    paginator = Paginator(book_list, 10)
    page = request.GET.get('page', 1)
    current_page = int(page)

    try:
        book_list = paginator.page(page)
    except PageNotAnInteger:
        book_list = paginator.page(1)
    except EmptyPage:
        book_list = paginator.page(paginator.num_pages)

    return render(request, "index.html", {"book_list": book_list, "paginator": paginator, "currentPage": current_page})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>

<div class="container">

    <h4>分页器</h4>
    <ul>

        {% for book in book_list %}
            <li>{{ book.title }} -----{{ book.price }}</li>
        {% endfor %}

    </ul>
    <ul class="pagination" id="pager">

        {% if book_list.has_previous %}
            <li class="previous"><a href="/app02/index/?page={{ book_list.previous_page_number }}">上一页</a></li>
        {% else %}
            <li class="previous disabled"><a href="#">上一页</a></li>
        {% endif %}
        {% for num in paginator.page_range %}

            {% if num == currentPage %}
                <li class="item active"><a href="/app02/index/?page={{ num }}">{{ num }}</a></li>
            {% else %}
                <li class="item"><a href="/app02/index/?page={{ num }}">{{ num }}</a></li>

            {% endif %}
        {% endfor %}

        {% if book_list.has_next %}
            <li class="next"><a href="/app02/index/?page={{ book_list.next_page_number }}">下一页</a></li>
        {% else %}
            <li class="next disabled"><a href="#">下一页</a></li>
        {% endif %}

    </ul>
</div>

</body>
</html>
index.html

二、数据量大的状况

def index(request):
    book_list = Book.objects.all()
    paginator = Paginator(book_list, 5)
    page = request.GET.get('page', 1)
    currentPage = int(page)

    #  若是页数特别多时,限制显示的index
    if paginator.num_pages > 11:  # 只显示10页的index
        if currentPage - 5 < 1:  # 当前页码小于6
            pageRange = range(1, 11)
        elif currentPage + 5 > paginator.num_pages:  # 当前页码+5大于了总页码
            pageRange = range(currentPage - 5, paginator.num_pages + 1)
        else:
            pageRange = range(currentPage - 5, currentPage + 5)
    else:
        pageRange = paginator.page_range
    try:
        print("当前页码:", page)
        book_list = paginator.page(page)
    except PageNotAnInteger:
        book_list = paginator.page(1)
    except EmptyPage:
        book_list = paginator.page(paginator.num_pages)

    return render(request, "index.html", locals())
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>

<div class="container">

    <h4>分页器</h4>
    <ul>

        {% for book in book_list %}
            <li>{{ book.title }} -----{{ book.price }}</li>
        {% endfor %}

    </ul>
    <ul class="pagination" id="pager">

        {% if book_list.has_previous %}
            <li class="previous"><a href="/app02/index/?page={{ book_list.previous_page_number }}">上一页</a></li>
        {% else %}
            <li class="previous disabled"><a href="#">上一页</a></li>
        {% endif %}

        {% for num in pageRange %}

            {% if num == currentPage %}
                <li class="item active"><a href="/app02/index/?page={{ num }}">{{ num }}</a></li>
            {% else %}
                <li class="item"><a href="/app02/index/?page={{ num }}">{{ num }}</a></li>

            {% endif %}
        {% endfor %}

        {% if book_list.has_next %}
            <li class="next"><a href="/app02/index/?page={{ book_list.next_page_number }}">下一页</a></li>
        {% else %}
            <li class="next disabled"><a href="#">下一页</a></li>
        {% endif %}

    </ul>
</div>

</body>
</html>
index.html

能够将分页的代码写到一个文件中,要使用分页时直接调用既能够。

11、Django与Ajax

一、Ajax介绍

AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即便用Javascript语言与服务器进行异步交互,传输的数据为XML(固然,传输的数据不仅是XML,如今更多使用json数据)。

  • 同步交互:客户端发出一个请求后,须要等待服务器响应结束后,才能发出第二个请求;
  • 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就能够发出第二个请求。

Ajax特色:

  • 使用Javascript技术向服务器发送异步请求;
  • 浏览器页面局部刷新。

二、基于jQuery的Ajax实现

<button class="send_Ajax">send_Ajax</button>
<script>
       $(".send_Ajax").click(function(){
           $.ajax({
               url:"/handle_Ajax/",  # URL
               type:"POST",  # 请求方式
               data:{username:"xxx",password:123},  # 请求数据
               # 回调函数:成功执行,发生错误等状况发生时作些什么操做
               success:function(data){
                   console.log(data)
               },
               
               error: function (jqXHR, textStatus, err) {
                        console.log(arguments);
                    },
               complete: function (jqXHR, textStatus) {
                        console.log(textStatus);
                },
               statusCode: {
                    '403': function (jqXHR, textStatus, err) {
                          console.log(arguments);
                     },
                    '400': function (jqXHR, textStatus, err) {
                        console.log(arguments);
                    }
                }
           })
       })
</script>

三、应用场景

(1) 用户名是否已被注册
在注册表单中,当用户填写了用户名后,把光标移开后,会自动向服务器发送异步请求。

服务器返回true或false,返回true表示这个用户名已经被注册过,返回false表示没有注册过。

客户端获得服务器返回的结果后,肯定是否在用户名文本框后显示“用户名已被注册”的错误信息!
(2) 基于Ajax进行登陆验证
用户在表单输入用户名与密码,经过Ajax提交给服务器,服务器验证后返回响应信息,客户端经过响应信息肯定是否登陆成功,成功,则跳转到首页,不然,在页面上显示相应的错误信息。

四、请求头ContentType

ContentType指的是请求体的编码类型,常见的类型共有3种:

  • application/x-www-form-urlencoded

最多见的 POST 提交数据的方式,浏览器的原生表单,若是不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。

请求体:

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
...

user=amy&age=22
  • multipart/form-data

使用表单上传文件时,必须让表单的 enctype 等于 multipart/form-data

POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="user"

yuan
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png

PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

首先生成了一个 boundary 用于分割不一样的字段,为了不与正文内容重复,boundary 很长很复杂。而后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构相似的部分,每部分都是以 --boundary 开始,紧接着是内容描述信息,而后是回车,最后是字段具体内容(文本或二进制)。若是传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary-- 标示结束。

  • application/json

表示消息主体是序列化后的 JSON 字符串。

五、文件上传

(1)基于form表单的文件上传

def avatar(request):
    # print("body:", request.body)  # 原始的请求体数据
    print("get:", request.GET)  # GET请求数据
    print("post:", request.POST)  # POST请求数据
    print("files:", request.FILES)  # 上传的文件数据
    return render(request, "avatar.html")
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    用户名 <input type="text" name="user">
    头像 <input type="file" name="avatar">
    <input type="submit">
</form>
</body>
</html>
avatar.html

结果:

get: <QueryDict: {}>
post: <QueryDict: {'csrfmiddlewaretoken': ['vVPsFNXCJpl3P0aUIaS1ABYkczxtZmrIa1RJEsoqGIQ9zrJ6CITbm7LkulaLwp71'], 'user': ['qw']}>
files: <MultiValueDict: {'avatar': [<InMemoryUploadedFile: 照片.jpg (image/jpeg)>]}>

(2)基于Ajax的文件上传

def avatar(request):
    if request.is_ajax():
        print(request.GET)  # GET请求数据
        print(request.POST)  # POST请求数据
        print(request.FILES)  # 上传的文件数据
        return HttpResponse("ok")
    return render(request, "avatar.html")
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form>
    {% csrf_token %}
    用户名 <input type="text" id="user">
    头像 <input type="file" id="avatar">
    <input type="button" id="ajax-submit" value="ajax-submit">
</form>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script>
<script>

    $("#ajax-submit").click(function () {
        var formdata = new FormData();
        formdata.append("user", $("#user").val());
        formdata.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());
        formdata.append("avatar_img", $("#avatar")[0].files[0]);
        $.ajax({

            url: "",  //默认当前URL
            type: "post",
            data: formdata,
            processData: false,    // 不处理数据
            contentType: false,    // 不设置内容类型

            success: function (data) {
                console.log(data);
            }
        })
    })
</script>
</body>
</html>
avatar.html
相关文章
相关标签/搜索