1. HTTP协议

1)HTTP请求协议

请求格式

POST(方法) /form/entry(URI) HTTP/1.1(协议版本)

HOST:hackr.jp(服务端地址)

Connection:keep-alive

Content-Type:application/x-www-form-urlencoded

Content-Length:16

name=nero&age=25(内容实体)

请求方式:get与post请求

  • GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456。
  • POST方法是把提交的数据放在HTTP包的请求体中。
  • GET提交的数据大小有限制(因为浏览器对URL的长度有限制)。
  • GET与POST请求在服务端获取请求数据方式不同

2)响应协议

相应格式

HTTP/1.1(协议版本) 200(状态码) OK(状态码的原因短语)

Date:Tue, 10 Jul 2012 06:50:00 GMT(响应首部字段)

Content-Length:362

Content-Type:text/html

<html>...(主体)

响应状态码

状态码是当客户端向服务器端发送请求时,返回的请求结果。借助状态吗,用户可以知道服务器端是正常处理了请求还是出现了异常。响应状态吗分别有以下5种。

  类别 原因短语
1XX Informational(信息行状态码) 接收的请求正在处理
2XX Success(成功状态码) 请求正常处理完毕
3XX Redirection(重定向状态码) 需要进行附加操作以完成请求
4XX Client Error(客户端错误状态码) 服务器无法处理请求
5XX Server Error(服务器错误状态码) 服务器处理请求出错

2. Django的简介:

1)Django的MTV模式

Model(模型):负责业务对象与数据库的对象(ORM)

Template(模板):负责如何把页面展示个用户

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

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

一般用户通过浏览器向我们的服务器发起一个请求(request),这个请求会去访问视图函数。试图函数调用模型,模型去数据库查找数据,然后逐级返回,视图函数把返回的数据填充到模板空格中,最后返回网页给用户。

2) Django工程

(1)创建一个django工程:djando-admin.py startproject mysite

manage.py Django项目里面的工具,通过它可以调用django.shell和数据库等

settings.oy 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量

urls.py 负责把URL模式映射到应用程序

(2)在mysite目录下创建blog应用:python manage.py startapp blog

(3)启动djando项目:python manage.py runserver 8080

3) settings配置文件

TEMPLATES下的

'DIRS': [os.path.join(BASE_DIR, 'templates')]

做了一个路径拼接

3. 静态文件配置

在根目录下生成一个“static”目录,用于存储静态文件,那么需要在settings.py文件中添加如下代码

STATIC_URL = 'static' # 别名STATICFILES_DIRS=[os.path.join(BASE_DIR, "static"), # 真实的static文件夹目录路径
]

4.  Django URL路由系统

本质上是url模式与视图函数之间的对应

urlpatterns = [path(正则表达式, views视图函数, 参数, 别名),
]

1)路由系统的使用

正则表达式:一个正则表达式字符串

views视图函数:一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串

参数:可选的要传递给视图函数的默认参数(字典形式)

别名:一个可选的name参数

from django.contrib import admin
from django.urls import path
from blog import views
from django.conf.urls import url,includeurlpatterns = [path('admin/', admin.site.urls),path("current_time", views.current_time),path("user_info", views.user_info),# 按照位置接收参数,参数接受顺序必须一致url(r"^articles/([0-9]{4})/$", views.year_archive),# 按照参数名接收参数,有名分组url(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$", views.month_archive),# 参数三url(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$", views.month_archive,{"year": "1990", "month": "8"}),# 参数四url(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$", views.month_archive,{"year": "1990", "month": "8"}, name="articles"),# url映射分发url(r"^blog/", include('blog.urls'))]

注意:

  • 若要从URL中捕获一个值,只需要在它周围放置一对圆括号
  • 不需要添加一个前导的反斜杠,因为每个URL都有。例如,应该是^articles而非^/articles
  • 每个正则表达式前面的“r”是可选的但建议加上。它告诉Python这个字符串是“原始的”--字符串中任何字符都不应该转义

2)反向解析

在urls.py文件中设置别名

urlpatterns = [path("login/", views.login, name="Login"),
]

在html文件中填写别名

<form action="{% url ‘Login’ %}" method="post"><input type="text" placeholder="用户名" name="user"><input type="password" placeholder="密码" name="pwd"><input type="submit">
</form>

通过reverse方法可以反向解析出url

from django.urls import reverse
url = reverse('Login')
print(url)

3)名称空间

名称空间是表示标识符的可见范围。一个标识符可在多个命名空间中定义,它在不同名称空间中的含义是互不相干的。这样,在一个新的名称空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其他名称空间中。

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

projects的urls.py:

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

app01.urls:

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

app02.urls:

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

app01.views

from django.core.urlresolvers import reversedef index(request):return HttpResponse(reverse("app01:index"))

app02.views

from django.core.urlresolvers import reversedef index(request):return HttpResponse(reverse("app02:index"))

4)path方法

from django.urls import path
from . import viewsulrpatterns = [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),
]

(1)基本规则:

  • 使用尖括号(<>)从url中捕获值
  • 捕获值中可以包含一个转化器类型(converter type),比如使用<int:name>捕获一个整数变量。如果没有转化器,将匹配任何字符串,当然也包括了/字符。
  • 无需添加前导斜杠

(2)path转化器:

  • Django默认支持以下5个转化器
  • str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
  • int,匹配正整数,包含0
  • slug,匹配字母、数字以及斜杠、下划线组成的字符串
  • uuid,匹配格式化的uuid,如0145881rp-5615-1415-aef1-nvaogr425
  • path,匹配任何非空字符串,包含了路径分隔符

(3)path的自定义转换器

对于一些复杂或复用的需要,可以定义自己的转化器。转化器是一个类或接口,它有3个要求:

  • regex类属性,字符串类型
  • to_python(self, value)方法,value是由类属性regex所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中
  • to_url(self, value)方法,和to_python相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用

创建一个类

class MonConvert:regex = "[0-9]{2}"def to_python(self, value):return int(value)def to_url(self, value): # 反向解析return '%04d' % value

在url路径控制中注册定义的url转换器

register_converter(MonConvert, "mm")

在path方法中使用

path("articles/<mm:month>", views.mon_path)

5. Django视图函数

一个视图函数,简称视图,是一个简单的Python函数,它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中。

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

http请求:HttpRequest对象

Http响应:HttpResponse对象

所在位置:django.http

1) HttpRequest对象

(1)属性:

1. HttpRequest.path
一个字符串,表示请求页面的全路径,不包括域名2. HttpRequest.GET
一个类似于字典的对象,包含所有HTTP GET参数的类字典对象3. HttpRequest.POST
一个类似于字典的对象,包含所有HTTP POST参数的类字典对象。POST请求可以带有空的POST字典 -- 如果通过HTTP POST方法发送一个表单,但是表单中没有任何的数据,QueryDict对象依然会被创建。因此,不应该使用 if request.POST来检查使用的是否是POST方法;应该使用 if request.method == "POST"
另外:如果使用POST上传文件的话,文件信息将包含在FILES属性中。4. HttpRequest.body
一个字符串,代表请求报文的主体。在处理非HTTP形式的报文时非常有用,例如:二进制图片、XML、JSON等。但是如果要处理表单数据的时候,推荐使用HttpResponse.POST5. HttpRequest.method
一个字符串,表示请求使用的HTTP方法。必须使用大写。例如:“GET”、“POST”
method:请求中使用的http方法的字符串表示。全大写表示
if request.method == "GET":pass
elseif request.method == "POST":pass6. HttpRequest.encoding
一个字符串,表示提交的数据的编码方式(如果为None则表示使用DEFAULT_CHARSET的设置,默认为'utf-8')。这个属性是可写的,可以修改来修改访问表单数据使用的编码。
接下来对属性的任何访问(例如从GET或POST中读取数据)将使用新的encoding值。
如果知道表单数据的编码不是DEFAULT_CHARSET,则使用它。7. HttpRequest.META
一个标准的Python,包含所有的HTTP首部。具体的头部信息取决于客户端和服务器8. HttpRequest.FILES
包含所有上传文件的类字典对象;FILES中的每个key都是<input type="file" name=""/>标签中name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys:filename:上传文件名,用字符串表示content_type:上传文件的Content Typecontent:上传文件的原始内容
注意:FILES只有请求的方法为POST且提交的<form>带有enctype="multipart/form-data"的情况下才会包含数据。否则,FILES将为一个空的类似于字典的对象。9. HttpRequest.COOKIES
包含所有cookies的标准Python字典对象;keys和values都是字符串10. HttpRequest.session
一个即可读又可写的类似于字典的对象,表示当前的会话。只有当Django启动会话的支持时才可用。11. HttpRequest.user
一个django.contrib.auth.models.User对象,代表当前登录的用户。如果访问用户当前没有登录,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你可以通过user的is_authenticated()方法来辨别用户是否登录:if request.user.is_authenticated(): 只有激活Django中的AuthenticationMiddleware时该属性才可用

(2)request常用方法

1. HttpRequest.get_full_path()
返回path,如果可以将加上查询的字符串
例如:“/index”2. HttpRequest.is_ajax()
如果请求是通过XMLHttpRequest发起的,则返回true,方法是检查HTTP_X_REQUESTED_WIDTH相应的首部是否是字符串“XMLHttpRequest”。
大部分现代的JavaScript库都会发送这个头部。如果编写自己的XMLHttpRequest调用(在浏览器端),必须手工设置这个值来让is_ajax()可以工作。
如果一个响应需要根据请求是否是通过AJAX发起的,并且正在使用某种形式的缓存例如Django的cache middleware,应该使用vary_on_headers('HTTP_X_REQUESTED_WITH')装饰你的视图以让响应能够正确的缓存。

2)HttpResponse对象:

每个view请求处理方法必须返回一个HttpResponse对象。HttpResponse类在django.http.HttpResponse

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

页面渲染:render(), render_to_response(),

页面跳转:redirect()

locals():可以直接将函数中所有的变量传给模板

相应对象主要有三种形式:

  • HttpResponse()
  • render()
  • redirect

(1)render()

render(request, template_name[, context])
结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的HttpResponse对象。

参数:

  • request:用于生成相应的请求对象
  • template_name:要使用的模板的完整名称,可选的参数
  • context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。

render方法就是将一个模板页面中的模板,最终渲染成一个html页面作为响应体。

(2)redirect()

传递要重定向的一个硬编码的URL

def my_view(request):...return redirect("/index")

6. 模板语言

1)模板语法之变量

在Django模板中遍历复杂数据结构的关键是句点字符。万能的句点号,列表、字典、属性、类都可以用.来取到。 语法:

{{ var_name }}

def index(request):import datetimes = "hello"l = [11, 22, 33]dic = {"name": "adam", "age": 24}date = datetime.date(1993, 5, 2)class Person(object):def __init__(self, name):self.name = namep1 = Person("Bruce" ,25)p2 = Person("Chris", 28)p3 = Person("David", 33)person_list = [p1, p2, p3]return render(request, "index.html". {"l": l, "data": data, "person_list": person_list})

template:

<h1>{{ l }}</h1>
<h2>{{ l.2 }}</h2>
<h3>{{ dic.name }}</h3>
<h4>{{ person_list.0.name }}</h4>

注意:句点符也可以用来应用对象的方法(无参数方法):

<h5>字典:{{ dic.name.upper }}</h5>

2)模板之过滤器

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

使用案例:

  • {{ ship_date|date:"FJ,Y"}},ship_date变量传给date过滤器,date过滤器通过使用“FJ,Y”这几个参数来格式化日期数据。
  • {{ obj|upper }}
  • {{ obj|lower }}
1. default
如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。例如:
{{ value|default:"nothing"}}2. length
返回值得长度。它对字符串和列表都起作用。例如:
{{ value|length }}
如果value是['a', 'b', 'c', 'd'],name输出为43. filesizeformat
将值格式化为一个“人类可读的”文件尺寸(例如“13KB”,“4.1MB”,“1.2GB”)
{{ value|filesizeformat }}4. date
如果value = datetime.datetime.now()
{{ value|date:"Y-m-d" }}5. slice
如果value = "hello world"
{{ value|slice:"2:-1"}}6. truncatechars
如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
参数:要截断的字符数
{{ value|truncatechars:9 }}7. safe
Django的模板中会对HTML标签和JS等语法标签进行自动转义,为了安全。但有时我们不希望这些HTML元素被转义。
value = "<a href=''>点击</a>"
{{ value|safe }}

其它常用过滤器:

add:给变量加上相应的值

addslashes:给变量中的引号前加上斜线

capfirst:首字母大写

cut:从字符串中移除指定的字符

default_if_none:如果值是None就替换成设置的默认值,否则就使用本来的值

3)模板之标签

一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加在其后的变量将使用到的额外信息到模板中。一些标签需要开始和结束标签

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

{% for person in person_list %}<p>{{ person }}</p>
{% endfor %}

可以利用{% for obj in list reversed %}反向完成循环

遍历一个字典:

{% for key, val in dic.items %}<p>{{ key }}:{{ val }}</p>
{% endfor %}

注:循环序号可以通过{{ forloop }}显示

  • forloop.counter 从1开始
  • forloop.counter0 从0开始
  • forloop.revcounter
  • forloop.revcounter0
  • forloop.first
  • forloop.last

(2)for ... empty

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

{% for person in person_list %}<p>{{ person.name }}
{% empty %}<p>sorry, no person here</p>
{% endfor %}

(3)if标签

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

{% if num > 100 or num < 0 %}<p>Hello World</p>
{% elif num > 60 and num <= 100 %}<p>Buy</p>
{% else %}<p>Bye</p>
{% endif %}

(4)with

使用一个简单的名字缓存一个复杂的变量

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

(5)csrf_token

{% csrf_token %}(加在form标签最后一行)

用于生成csrf_token的标签,用于防止跨站攻击验证

(6){% url %}

引用路由配置的地址

<form action={% url 别名 %}>

4)模板语言之自定义标签与过滤器

a. 在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag

b. 在app中创建templatetags模块(模块名只能是templatetags)

c. 创建任意.py文件,如my_tags.py

from django import template
from django.utils.safestring import mark_saferegister = 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)

d. 在使用自定义的html文件中导入之前创建的my_tags.py文件名

{% load my_tags %}

e. 使用simple_tag和filter

{% load my_tags %}{{ num|filter_multi: 2}}{% simple_tag_multi 2 5 %} # 参数不限,但不能放在if for语句中
{% simple_tag_multi num 5 %}

注意:filter可以用在if等语句后,simple_tag不可以

5)模板语言之继承

Django模板引擎中最强大也是最复杂的就是模板继承。模板继承可以创建以基本的“骨架”模板,它包含站点中的全部元素,并且可以定义能够被子模板覆盖的blocks。

<html>
<head><link rel="stylesheet" href="style.css"><title>{% block title %}My Amazing Site(% endblock %)</title>
</head><body><div id="sidebar">{% block sidebar %}<ul><li><a href="/">Home</li><li><a href="/blog">Blog</li></ul>{% endblock%}<div><div id="content">{% block content %}{% endclock%}</div>
</body>
</html>

上面这个模板叫作base.html。在这个例子中,block标签定义了3个可以被子模板内容填充的block。block告诉模板引擎:子模板可能会覆盖掉模板中的这些位置。

子模板可能如下:

{% extends "base.html" %}{% block title%}My Amazing Blog
{% endblock %}{% block content %}{% for blog in blogs %}<h2>{{ blog.title }}</h2><p>{{ blog.body }}</p>{% endfor %}
{% endblock %}

extends标签是这里的关键。它告诉模板引擎这个模板“继承”了另一个模板。

请注意,子模板并没有定义sidebar block,所以系统使用了父模板中的值。

这种方式使代码得到最大程度的复用,并且使得添加内容到共享的内容区域更加简单

使用继承的一些提示:

  • 如果在模板中使用{% extends %}标签,它必须是模板中的第一个标签。其他的任何情况下,模板继承都将无法工作
  • 在base模板中设置越多的{% block %}标签越好。子模板不必定义全部父模板中的blocks,所以,可以在大多数blocks中填充合理的默认内容。
  • 如果发现在大量的模板中复制内容,那可能意味着应该把内容移动到父模板中的一个{% block %}中。
  • 如需父模板中的内容没,加入{{ block.super }}标签
  • 为了更好的可读性,也可以给{% endblock %}标签一个名字。
{% block content%}
...
{% endblock content%}
  • 不能在一个模板中定义多个相同名字的block标签

7. djando ORM

ORM实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大地减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动。

ORM是“对象-关系-映射”的简称。

1)常用字段

1. CharField
字符串字段,用于较短的字符串
CharField要求必须有一个参数maxlength,用于从数据层和Django校验层限制该字段所允许的最大字符数。2. IntegerField
用于保存一个整数3. FloatField
一个浮点数,必须提供一个整数
max_digits 总位数
decimal_places 小数位数4. AutoField
一个IntegerField,添加记录时它会自动增长,通常不需要直接使用这个字段;
自定义一个主键:my_id=models.AutoField(primary_key=True)
如果不指定主键的话,系统会自动添加一个关键字段到model5. 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 当对象首次被创建时,自动将该字段的值设置为当前时间,通常用于表示对象创建时间9. DateTimeField
一个日期时间字段,类似DateField支持同样的附加选项10. ImageField
类似FileField,不过要校验上传对象是否是一个合法图片,它有两个可选参数:height_field和width_field,如果提供这两个参数,则图片将按提供的高度和宽度规格保存11. FileField
一个文件上传字段
要求一个必须带有的参数:upload_to,一个用于保存上载文件的本地文件系统路径。这个路径必须包含strftime
该格式将被上载文件的date/time注意:在一个model使用FileField或ImageField需以下步骤:
(1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件。(出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对WEB服务器用户帐号是可写的.
(2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT).12. URLFIeld
用于保存URL。若verify_exists参数为True(默认),给定的URL会事先检查是否存在。admin用一个<input type='text'>文本框表示该字段保存的数据13. NullBooleanField
类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项
admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据.14. SlugField15. XMLField
一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema 为文件系统路径.16. FilePathField
可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的.
path    必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目.
match    可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名.
注意这个正则表达式只会应用到 base filename 而不是路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif.
recursive    可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录.17. IPAddressField
一个字符串形式的 IP 地址, (i.e. "24.124.1.30").18. CommaSeparatedIntegerField
用于存放逗号分隔的整数值. 类似 CharField, 必须要有maxlength参数.

2)常用参数

1. null
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.2. blank
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。3. default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。4. primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,否则没必要设置任何一个字段的primary_key=True。5. unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的6. choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。

3)settings配置

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

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

注意1: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()

最后通过两条数据库迁移命令即可在指定的数据库中创建表 :

python manage.py makemigrations
python manage.py migrate

注意2:确保配置文件中的INSTALLED_APPS中写入我们创建的app名称

注意3:如果报错如下:

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

注意4: 如果想打印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',},}
}  

一. 单表操作

1)创建表,创建名为book的app,在book下的models.py中创建模型:

from django.db import models# Create your models here.class Book(models.Model):id = models.AutoField(primary_key=True)title = models.CharField(max_length=32)pub_date = models.DateField()price = models.DecimalField(max_digits = 8, decimal_places = 2)publisher = models.CharField(max_length = 32)

2)添加记录

方式1:

book=Book(id=1, title="Python COre", price=100, pub_date="2012-10-08", publisher="人民出版社")book.save()

方式2(推荐使用这种方式):

book_obj = Book.objects.create(id = 2,title="PHP",price=50,pub_date="2012-03-01",publisher="邮电出版社"
)

方式3:

Book.objects.create(**{"title":"A", "price":1, "color":"green"})

3)删

删除方法就是delete()。它运行时立即删除对象而不返回任何值

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

可以一次性删除多个对象。每个QuerySet都有一个delete()方法,它一次性删除QuerySet中所有的对象。

要注意的是:delete()方法是QuerySet上的方法,但并不适用于Manager本身。这是一种保护机制,是为了避免意外调用Entry.objects.delete()方法导致所有的纪录被误删除。如果确认要删除所有的对象,纳闷必须显式地调用:

Entry.objects.all().delete()

如果不想级联删除,可以设置为:

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

4)改(update和save)

save方式:

book = Book.objects.get(title="A")

book.price = 30

book.save()

update方式(这种方式效率更高):

Book.objects.filter(title="C").update(price=25) # 不能用get方法

注意:update方式不能使用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象

注意,这里因为update返回的是一个整型,所以没法用query属性;对于每次创建一个对象,想显示对应的raw sql,需要在settings加上日志记录部分:

LOGGING={'version':1,'disable_existing_loggers':False,'handlers':{'console':{'level':'DEBUG','class':'logging.StreamHandler',},},'loggers':{'django.db.backends':{'handlers':['console'],'propagate':True,'level':'DEGUG',},}}

5)查

(1)查询API:

(1)filter(**kwargs):它包含了与所给筛选条件相匹配的对象,返回值为queryset对象
book_obj = Book.objects.filter(price=50)(2)all():查询所有结果,返回值为queryset对象
book_obj = Book.objects.all()(3)get(**kwargs):返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。返回值为model对象
book_obj = Book.objects.get(id = 2)下面的方法都是对查询的结果再进行处理:比如objects.filter.values()(4)values(*field):返回一个ValueQuerySet--一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
ret = Book.objects.all().values("price")(5)exclude(**kwargs):它包含了与所给筛选条件不匹配的对象(6)order_by(*field):对查询结果排序,倒序:order_by("-id")(7)reverse():对查询结果反向排序(8)distinct():从返回结果中剔除重复记录(9)values_list(*field):它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列(10)count():返回数据库中匹配查询(QuerySet)的对象数量(11)first():返回第一个条记录(12)last():返回最后一条记录(13)exists():如果QuerySet包含数据,就返回True,否则返回False

(2)基于双下划线的模糊查询

Book.objects.filter(price__in=[100,200,300])Book.objects.filter(price__gt=100)Book.objects.filter(price__lt=100)Book.objects.filter(price__range=[100,200])Book.objects.filter(title__contains="python")Book.objects.filter(title__icontains="python")Book.objects.filter(title__startwith="py")Book.objects.filter(title__endwith="on")Book.objects.filter(pub_date__year=2012, pub_date__month=10)

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

(4)QuerySet特点:可迭代的、可切片

objs = models.Book.objects.all()
for obj in objs:print(obj)print(objs[1:4])
print(objs[::-1])

(5)正向查询:

book=models.Book.objects.filter(id-2)[0]
authors=models.Author.objects.filter(id_gt=2)
book.author.add(*authors)
book.author.remove(*author)

obj.book_set.values("title").distinct()

(6)反向查询:

author=models.Author.objects.filter(id=3)[0]
books=models.Book.objects.filter(id_gt=2)
author.book_set.add(*books)

二. 多表操作

一对多:一旦确定表关系是一对多:在多对应的表中创建关联字段

publisher = models.ForeignKey(to="Publisher", to_field="nid", on_delete=models.CASCADE)

多对多:一旦确定表关系是多对多:创建第三张关系表

authors = models.ManyToMany(to="Author",)

一对一:一旦确定表关系是一对一:在两张表中的任意一张中建立关联字段+Unique

authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)

1)创建模型

from django.db import models# Create your models here.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.CASCADE)# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表authors=models.ManyToManyField(to='Author',)

2)添加表记录

(1)一对多

方式1:publish_obj=Publish.objects.get(nid=1)book_obj=Book.objects.create(title="西游记",publishDate="2012-12-12",price=100,publish=publish_obj)方式2:book_obj=Book.objects.create(title="西游记",publishDate="2012-12-12",price=100,publish_id=1) 

(2)多对多

# 当前生成的书籍对象
book_obj=Book.objects.create(title="追风筝的人",price=200,publishDate="2012-11-12",publish_id=1)# 为书籍绑定的做作者对象
yuan=Author.objects.filter(name="yuan").first() # 在Author表中主键为2的纪录
egon=Author.objects.filter(name="alex").first() # 在Author表中主键为1的纪录# 绑定多对多关系,即向关系表book_authors中添加纪录
book_obj.authors.add(yuan,egon)    #  将某些特定的 model 对象添加到被关联对象集合中。 book_obj.authors.add(*[])

多对多关系其它常用API:

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

3)跨表查询

(1)基于对象的查询

a. 一对多查询:

正向查询(按字段查询)

# 查询主键为1的书籍的出版社所在的城市
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish 是主键为1的书籍对象关联的出版社对象
print(book_obj.publish.city) 

反向查询(按表名查询)

publish=Publish.objects.get(name="苹果出版社")
#publish.book_set.all() : 与苹果出版社关联的所有书籍对象集合
book_list=publish.book_set.all()
for book_obj in book_list:print(book_obj.title)

b. 多对多查询

正向查询(按字段查询)

book_obj=Book.objects.filter(title="西游记").first()
authors=book_obj.authors.all()
for author_obj in authors:print(author_obj.name,author_obj.authorDetail.telephone)

反向查询(按表名查询)

author_obj=Author.objects.get(name="egon")book_list=author_obj.book_set.all()        #与egon作者相关的所有书籍for book_obj in book_list:print(book_obj.title)

c. 一对一查询

正向查询(按字段查询)

egon=Author.objects.filter(name="egon").first()
print(egon.authorDetail.telephone)

反向查询(按表名查询)

authorDetail_list=AuthorDetail.objects.filter(addr="beijing")
for obj in authorDetail_list:print(obj.author.name)

(2)基于双下划线的查询(Join查询)

关键点:正向查询按字段,反向查询按表名小写用来告诉ORM引擎join哪张表

a. 一对多查询

# 正向查询 按字段:publish
queryResult=Book.objects.filter(publish__name="苹果出版社").values_list("title","price")# 反向查询 按表名:book
queryResult=Publish.objects.filter(name="苹果出版社").values_list("book__title","book__price")
 

b. 多对多查询

# 正向查询 按字段:authors:
queryResult=Book.objects.filter(authors__name="yuan").values_list("title")# 反向查询 按表名:book
queryResult=Author.objects.filter(name="yuan").values_list("book__title","book__price")

c. 一对一查询

# 正向查询
ret=Author.objects.filter(name="alex").values("authordetail__telephone")# 反向查询
ret=AuthorDetail.objects.filter(author__name="alex").values("telephone")

d. 进阶(连续跨表)

练习: 查询人民出版社出版过的所有书籍的名字以及作者的姓名# 正向查询
queryResult=Book.objects.filter(publish__name="人民出版社").values_list("title","authors__name")
# 反向查询
queryResult=Publish.objects.filter(name="人民出版社").values_list("book__title","book__authors__age","book__authors__name")手机号以151开头的作者出版过的所有书籍名称以及出版社名称# 方式1:
queryResult=Book.objects.filter(authors__authorDetail__telephone__regex="151").values_list("title","publish__name")# 方式2:
ret=Author.objects.filter(authordetail__telephone__startswith="151").values("book__title","book__publish__name")

(3)聚合查询

aggregate(*args, **kwargs)# 计算所有图书的平均价格
from django.db.models import Avg
Book.objects.all().aggregate(Avg('price'))aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
Book.objects.aggregate(average_price=Avg('price'))如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
from django.db.models import Avg, Max, Min
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))

(4)分组

a. 单表分组查询

单表模型.objects.values("group by的字段").annotate(聚合函数(“统计字段”))

查询每一个部门名称以及对应的员工数emp:id  name age   salary    dep
1   alex  12   2000     销售部
2   egon  22   3000     人事部
3   wen   22   5000     人事部sql语句:
select dep,Count(*) from emp group by dep;ORM:
emp.objects.values("dep").annotate(c=Count("id")

注意:在单表分组下,按着主键进行group by是没有任何意义的。

b. 多表分组查询

跨表的分组查询的模型:

每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")

每一个后的表模型.objects.annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")

# 查询每个出版社的名称以及出版的书籍个数
ret=Publish.objects.values("name").annotate(c=Count("book__title"))ret=Publish.objects.values("nid").annotate(c=Count("book__title")).values("name", "c")
# 查询每个作者的名字以及出版过的书籍的最高价格
ret = Author.objects.values("pk").annotate(max_price=Max("book_price")).values("name", "max_price")
emp:id  name age   salary   dep_id
1   alex  12   2000       1
2   egon  22   3000       2
3   wen   22   5000       2depid   name
1    销售部
2    人事部emp-dep:id  name age   salary   dep_id   id   name
1   alex  12   2000       1      1    销售部
2   egon  22   3000       2      2    人事部
3   wen   22   5000       2      2    人事部sql语句:
select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.idORM:
dep.objetcs.values("id").annotate(c=Count("emp__name")).values("name","c")
# 统计不止一个作者的图书
ret = Book.objects.values("pk").annotate(c=Count("authors__name")).(c__gt=1).values("title", "c")

annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。

总结:跨表分组查询本质就是将关联表join成一张表,再按照单表的思路进行分组查询。

(5)F查询和Q查询:

a. F查询

如果我们要对两个字段的值作比较,仅仅靠单一的关键字参数查询已经很难满足查询要求。Django提供F()来做这样的比较。

# 查询评论数大于收藏数的书籍
from django.db.models import F
Book.objects.filter(commnetNum__gt=F('keepNum'))

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作

# 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commnetNum__gt=F('keepNum')*2)

修改操作也可以使用F函数,比如将每一本书的价格提高30元

Book.objects.all().update(price=F("price")+30) 

b. Q查询

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

from django.db.models import Q
Q(title__startswith='Py')

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

bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))

你可以组合& 和|  操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询:

bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")

查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。

bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),title__icontains="python")

ORM补充

模型之间的三种关系:一对一,一对多,多对多

一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;

一对多:就是主外键关系(foreign key)

(1)models.Book.objects.create(title="Python",price=12,publish_id=2)(2)(推荐)models.Book.objects.create(title="Python",price=12,publish=obj)

多对多:彼此一对多,自动创建第三张表(当然也可以自己创建第三张表)

自己创建第三张表

class Book2Author(models.Model):author=models.ForeignKey("Author")book=models.ForeignKey("Book")# 创建联合唯一索引class Meta:unique_together=["author","book"]

8. Ajax

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

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

AJAX除了异步的特点外,还有一个就是:浏览器页面局部刷新;(这一特点给用户的感受是在不知不觉中完成请求和响应过程)

1)优点:

  • AJAX使用Javascript技术向服务器发送异步请求
  • AJAX无须刷新整个页面

基于jquery的Ajax实现

<button class="send_Ajax">send_Ajax</button>
<script>$(".send_Ajax").click(function(){$.ajax({url:"/handle_Ajax/",type:"POST",data:{username:"Yuan",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>

2)请求头ContentType

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

(1)application/x-www-form-urlencoded

这应该是最常见的 POST 提交数据的方式了。浏览器的原生 <form> 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。请求类似于下面这样(无关的请求头在本文中都省略掉了)

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

(2) multipart/form-data

这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 <form> 表单的 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/pngPNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

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

这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。

上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生 <form> 表单也只支持这两种方式(通过 <form> 元素的 enctype 属性指定,默认为 application/x-www-form-urlencoded。其实 enctype 还支持 text/plain,不过用得非常少)。

随着越来越多的 Web 站点,尤其是 WebApp,全部使用 Ajax 进行数据交互之后,我们完全可以定义新的数据提交方式,给开发带来更多便利。

(3) application/json

application/json 这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。

传入Django后台时,需要从request.body中取值

基于form表单的文件上传

模板部分:

<form action="" method="post" enctype="multipart/form-data">用户名 <input type="text" name="user">头像 <input type="file" name="avatar"><input type="submit">
</form>

视图部分:

def index(request):print(request.body)   # 原始的请求体数据print(request.GET)    # GET请求数据print(request.POST)   # POST请求数据print(request.FILES)  # 上传的文件数据return render(request,"index.html")

模板:

<form>用户名 <input type="text" id="user">头像 <input type="file" id="avatar"><input type="button" id="ajax-submit" value="ajax-submit">
</form><script>$("#ajax-submit").click(function(){var formdata=new FormData();formdata.append("user",$("#user").val());formdata.append("avatar_img",$("#avatar")[0].files[0]);$.ajax({url:"",type:"post",data:formdata,processData: false ,    // 不处理数据contentType: false,    // 不设置内容类型success:function(data){console.log(data)}})})</script>

9. 分页器

views视图

from django.shortcuts import render,HttpResponse# Create your views here.
from app01.models import *
from django.core.paginator import Paginator, EmptyPage, PageNotAnIntegerdef index(request):'''批量导入数据:Booklist=[]for i in range(100):Booklist.append(Book(title="book"+str(i),price=30+i*i))Book.objects.bulk_create(Booklist)''''''
分页器的使用:book_list=Book.objects.all()paginator = Paginator(book_list, 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()) #上一页的页码# 抛错#page=paginator.page(12)   # error:EmptyPage#page=paginator.page("z")   # error:PageNotAnInteger'''book_list=Book.objects.all()paginator = Paginator(book_list, 10)page = request.GET.get('page',1)currentPage=int(page)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",{"book_list":book_list,"paginator":paginator,"currentPage":currentPage})

index.html

<!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="/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="/index/?page={{ num }}">{{ num }}</a></li>{% else %}<li class="item"><a href="/index/?page={{ num }}">{{ num }}</a></li>{% endif %}{% endfor %}{% if book_list.has_next %}<li class="next"><a href="/index/?page={{ book_list.next_page_number }}">下一页</a></li>{% else %}<li class="next disabled"><a href="#">下一页</a></li>{% endif %}</ul>
</div></body>
</html>

扩展:

def index(request):book_list=Book.objects.all()paginator = Paginator(book_list, 15)page = request.GET.get('page',1)currentPage=int(page)#  如果页数十分多时,换另外一种显示方式if paginator.num_pages>11:if currentPage-5<1:pageRange=range(1,11)elif currentPage+5>paginator.num_pages:pageRange=range(currentPage-5,paginator.num_pages+1)else:pageRange=range(currentPage-5,currentPage+5)else:pageRange=paginator.page_rangetry: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())

10. forms组件

1)检验字段功能

模型: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

# forms组件
from django.forms import widgetswid_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(form.cleaned_data)       # 所有干净的字段以及对应的值else:print(form.cleaned_data)       #print(form.errors)             # ErrorDict : {"校验错误的字段":["错误信息",]}print(form.errors.get("name")) # ErrorList ["错误信息",]return HttpResponse("OK")form=UserForm()return render(request,"register.html",locals())

2)渲染标签功能

渲染方式一

<!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><input type="submit" class="btn btn-default pull-right"></form></div></div>
</div></body>
</html>

渲染方式二

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

渲染方式三(不推荐使用)

<form action="" method="post">{% csrf_token %}{{ form.as_p }}<input type="submit" class="btn btn-default pull-right"></form>

注意:form表单的name属性值应该与forms组件字段名称一致

3)forms参数的简单应用

from django import forms
from django.forms import widgetsclass UserForm(forms.Form):name = forms.CharField(min_length=4, label="用户名",error_messages={"required": "该字段不能为空"},widget=widgets.TextInput(attrs={"class": "form-control"}))pwd = forms.CharField(min_length=4, label="密码",error_messages={"required": "该字段不能为空"},widget=widgets.PasswordInput(attrs={"class": "form-control"}))r_pwd = forms.CharField(min_length=4, label="确认密码")email = forms.EmailField(label="邮箱")tel = forms.CharField(label="手机号")

4)局部与全局钩子

视图

# forms组件
from django.forms import widgetswid_01=widgets.TextInput(attrs={"class":"form-control"})
wid_02=widgets.PasswordInput(attrs={"class":"form-control"})from django.core.exceptions import ValidationError
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 clean_name(self):val=self.cleaned_data.get("name")if not val.isdigit():return valelse: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_dataelse:raise ValidationError('两次密码不一致!')def register(request):if request.method=="POST":form=UserForm(request.POST)if form.is_valid():print(form.cleaned_data)       # 所有干净的字段以及对应的值else:clean_error=form.errors.get("__all__")return render(request,"register.html",locals())form=UserForm()return render(request,"register.html",locals())

模板

<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 == 'r_pwd' %}<span>{{ clean_error.0 }}</span>{% endif %}{{ field.errors.0 }}</span></div>{% endfor %}<input type="submit" class="btn btn-default"></form>

加入单选列表与多选列表的Form组件

from django import forms
from django.forms import widgets
from .models import Publish, Authorclass BookForm(forms.Form):title = forms.CharField(max_length=32, label="书籍名称")price = forms.DecimalField(max_digits=8, decimal_places=2, label="价格")date = forms.DateField(label="日期",widget=widgets.TextInput(attrs={"type": "date"}))publish = forms.ModelChoiceField(queryset=Publish.objects.all())authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())

11. Cookie

Cookie:具体一个浏览器针对一个服务器存储key-value

Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是key-value结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端了!

1)Cookie规范

  • Cookie大小上限为4KB
  • 一个服务器最多在客户端浏览器上保存20个Cookie
  • 一个浏览器最多保存300个Cookie

上面的数据只是HTTP的Cookie规范,但在浏览器大战的今天,一些浏览器为了打败对手,为了展现自己的能力起见,可能对Cookie规范“扩展”了一些,例如每个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现把你硬盘占满的可能! 
注意,不同浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用FireFox访问服务器时,不可能把IE保存的Cookie发送给服务器。

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

3)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失效的实际日期/时间。
date = datetime.datetime(year=2018, month=12, day=10), expires=datepath='/',           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)

12. Session

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

django中的session语法

1、设置Sessions值request.session['session_name'] ="admin"
2、获取Sessions值session_name = request.session["session_name"]
3、删除Sessions值del request.session["session_name"]
4、flush()删除当前的会话数据并删除会话的Cookie。这用于确保前面的会话数据不可以再次被用户的浏览器访问
5、get(key, default=None)fav_color = request.session.get('fav_color', 'red')6、pop(key)fav_color = request.session.pop('fav_color')7、keys()8、items()9、setdefault()10 用户session的随机字符串request.session.session_key# 将所有Session失效日期小于当前日期的数据删除request.session.clear_expired()# 检查 用户session的随机字符串 在数据库中是否request.session.exists("session_key")# 删除当前用户的所有Session数据request.session.delete("session_key")request.session.set_expiry(value)* 如果value是个整数,session会在些秒数后失效。* 如果value是个datatime或timedelta,session就会在这个时间后失效。* 如果value是0,用户关闭浏览器session就会失效。* 如果value是None,session会依赖全局session失效策略。

session配置

Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。a. 配置 settings.pySESSION_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,默认修改之后才保存(默认)

13. auth用户认证

用户认证组件:

功能:用session记录登录验证状态

前提:用户表:django自导的auth_user

创建创建用户:python manage.py createsuperuser

1)auth模块

from django.contrib import auth

django.contrib.auth中提供了许多方法,这里主要介绍其中的三个:

(1)authenticate()

提供了用户认证,即验证用户名以及密码是否正确,一般需要username  password两个关键字参数

如果认证信息有效,会返回一个  User  对象。authenticate()会在User 对象上设置一个属性标识那种认证后端认证了该用户,且该信息在后面的登录过程中是需要的。当我们试图登陆一个从数据库中直接取出来不经过authenticate()的User对象会报错的!

user = authenticate(username='someone',password='somepassword')

(2)login(HttpRequest, user)

该函数接受一个HttpRequest对象,以及一个认证了的User对象

此函数使用django的session框架给某个已认证的用户附加上session id等信息。

from django.contrib.auth import authenticate, logindef my_view(request):username = request.POST['username']password = request.POST['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)注销用户

from django.contrib.auth import logoutdef logout_view(request):logout(request)# Redirect to a success page.

该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。

2) User对象

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

(1)user对象的is_authenticate()

如果是真正的 User 对象,返回值恒为 True 。 用于检查用户是否已经通过了认证。
通过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是表明用户成功的通过了认证。 这个方法很重要, 在后台用request.user.is_authenticated()判断用户是否已经登录,如果true则可以向前台展示request.user.name

要求:

1  用户登陆后才能访问某些页面,

2  如果用户没有登录就访问该页面的话直接跳到登录页面

3  用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址

方法一:

def my_view(request):if not request.user.is_authenticated():return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))

方法二:

django已经为我们设计好了一个用于此种情况的装饰器: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='',password='',email='')

(3)check_password(passwd)

用户需要修改密码的时候 首先要让他输入原来的密码 ,如果给定的字符串通过了密码检查,返回 True

(4)修改密码

使用 set_password() 来修改密码

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

(5)简单示例

注册:

def sign_up(request):state = Noneif request.method == 'POST':password = request.POST.get('password', '')repeat_password = request.POST.get('repeat_password', '')email=request.POST.get('email', '')username = request.POST.get('username', '')if User.objects.filter(username=username):state = 'user_exist'else:new_user = User.objects.create_user(username=username, password=password,email=email)new_user.save()return redirect('/book/')content = {'state': state,'user': None,}return render(request, 'sign_up.html', content) 

修改密码:

@login_required
def set_password(request):user = request.userstate = Noneif 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 = 'empty'elif new_password != repeat_password:state = 'repeat_error'else:user.set_password(new_password)user.save()return redirect("/log_in/")else:state = 'password_error'content = {'user': user,'state': state,}return render(request, 'set_password.html', content)

15. 中间件

1)中间件的概念:中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。

如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。

可能你还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。

Django默认的Middleware

MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]

2)自定义中间件

中间件中一共有四个方法:

process_requestprocess_viewprocess_exceptionprocess_response

(1)process_request, process_response

当用户发起请求的时候会依次经过所有的的中间件,这个时候的请求时process_request,最后到达views的函数中,views函数处理后,在依次穿过中间件,这个时候是process_response,最后返回给请求者。

需要导入

from django.utils.deprecation import MiddlewareMixin

in views:

def index(request):print("view函数...")return HttpResponse("OK")

in Mymiddlewares.py:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponseclass Md1(MiddlewareMixin):def process_request(self,request):print("Md1请求")def process_response(self,request,response):print("Md1返回")return responseclass Md2(MiddlewareMixin):def process_request(self,request):print("Md2请求")#return HttpResponse("Md2中断")def process_response(self,request,response):print("Md2返回")return response

结果:

Md1请求
Md2请求
view函数...
Md2返回
Md1返回

注意:如果当请求到达请求2的时候直接不符合条件返回,即return HttpResponse("Md2中断"),程序将把请求直接发给中间件2返回,然后依次返回到请求者

(2)process_view

process_view(self, request, callback, callback_args, callback_kwargs)

Mymiddlewares.py修改如下

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponseclass Md1(MiddlewareMixin):def process_request(self,request):print("Md1请求")#return HttpResponse("Md1中断")def process_response(self,request,response):print("Md1返回")return responsedef process_view(self, request, callback, callback_args, callback_kwargs):print("Md1view")class Md2(MiddlewareMixin):def process_request(self,request):print("Md2请求")return HttpResponse("Md2中断")def process_response(self,request,response):print("Md2返回")return responsedef process_view(self, request, callback, callback_args, callback_kwargs):print("Md2view")

当最后一个中间的process_request到达路由关系映射之后,返回到中间件1的process_view,然后依次往下,到达views函数,最后通过process_response依次返回到达用户。

process_view可以用来调用视图函数:

class Md1(MiddlewareMixin):def process_request(self,request):print("Md1请求")#return HttpResponse("Md1中断")def process_response(self,request,response):print("Md1返回")return responsedef process_view(self, request, callback, callback_args, callback_kwargs):# return HttpResponse("hello")response=callback(request,*callback_args,**callback_kwargs)return response

注意:process_view如果有返回值,会越过其他的process_view以及视图函数,但是所有的process_response都还会执行。

(3)process_exception

process_exception(self, request, exception)

示例修改如下:

class Md1(MiddlewareMixin):def process_request(self,request):print("Md1请求")#return HttpResponse("Md1中断")def process_response(self,request,response):print("Md1返回")return responsedef process_view(self, request, callback, callback_args, callback_kwargs):# return HttpResponse("hello")# response=callback(request,*callback_args,**callback_kwargs)# return responseprint("md1 process_view...")def process_exception(self):print("md1 process_exception...")class Md2(MiddlewareMixin):def process_request(self,request):print("Md2请求")# return HttpResponse("Md2中断")def process_response(self,request,response):print("Md2返回")return responsedef process_view(self, request, callback, callback_args, callback_kwargs):print("md2 process_view...")def process_exception(self):print("md1 process_exception...")

2)应用案例

(1)做IP访问频率限制

某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。

(2)URL访问过滤

如果用户访问的是login视图(放过)

如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!

16. ModelForm

ModelForm:

from django.forms import ModelForm
from .models import *
from django.forms import widgetsclass BookForm(ModelForm):class Meta:model = Bookfields = "__all__"labels = {"title": "书籍名称","price": "价格","date": "出版日期","publish": "出版社","authors": "作者",}widgets = {"title": widgets.TextInput(attrs={"class": "form-control"}),"price": widgets.TextInput(attrs={"class": "form-control"}),"date": widgets.TextInput(attrs={"class": "form-control"}),"publish": widgets.TextInput(attrs={"class": "form-control"}),"authors": widgets.TextInput(attrs={"class": "form-control"}),}

views文件:

def edit_book(request, edit_book_id):edit_book = models.Book.objects.filter(pk=edit_book_id).first()if request.method == "POST":form = BookForm(request.POST, instance=edit_book)if form.is_valid():form.save()return redirect("/books/")form = BookForm(instance=edit_book)return render(request, 'edit_book.html', {"edit_book": edit_book, "form": form})

form.html模板

<form action="" method="post">{% csrf_token %}<div>{{ form.title.label }}{{ form.title }}</div><div>{{ form.price.label }}{{ form.price }}</div><div>{{ form.date.label }}{{ form.date }}</div><div>{{ form.publish.label }}{{ form.publish }}</div><div>{{ form.authors.label }}{{ form.authors }}</div><input type="submit" value="提交">
</form>

17. 跨域请求的实现

1)JSONP方法

利用JSONP简单实现跨域请求:

<script>$(".get_service").click(function(){$.ajax({url: "http://1277.0.0.9999/service", // 跨域请求的urltype: "get",dataType: "jsonp", // 伪造ajax,基于script标签来实现,必须有的参数jsonp: "jsonp_key", // jsonp发送的数据key,jsonpCallback: "jsonp_value", // jsonp发送的数据value,如果不填,jquery会自己伪造一个随机字符串success: function (data) {console.log(data); // 成功返回数据后执行的操作}})})</script>

实际应用案例:

$(".get_service-4").click(function () {$.ajax({url:"http://www.jxntv.cn/data/jmd-jxtv2.html",type:"get",dataType:"jsonp",     // 伪造ajax  基于scriptjsonp: 'callbacks',jsonpCallback:"list",success:function (data) {//console.log(data.data);var html="";$.each(data.data,function (index,weekday) {console.log(weekday); // {week: "周一", list: Array(19)}html+='<p>'+weekday.week+'</p>';$.each(weekday.list,function (j,show) {html+= '<p><a href='+show.link+'>'+show.name+'</a></p>'})});$("body").append(html)}})})

2)core方法

# 通过设置Access-Control-Allow-Origin参数,让浏览器取消拦截
response = HttpResponse(json.dumps(info))
response["Access-Control-Allow-Origin"] = "*"

18. Admin

Django admin是django自带的一个后台app,提供了后台的管理功能

Django 自动管理工具是 django.contrib 的一部分。你可以在项目的 settings.py 中的 INSTALLED_APPS 看到它

django.contrib是一套庞大的功能集,它是Django基本代码的组成部分。

1)认识ModelAdmin

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

2)注册model类到admin的两种方式:

(1)使用register方法

admin.site.register(models.UserInfo)
方式一:class UserAdmin(admin.ModelAdmin):list_display = ('user', 'pwd',)admin.site.register(models.UserInfo, UserAdmin) # 第一个参数可以是列表

(2)使用register的装饰器

方式二:@admin.register(models.UserInfo)                # 第一个参数可以是列表class UserAdmin(admin.ModelAdmin):list_display = ('user', 'pwd',)

3)ModelAdmin中提供了大量的可定制功能

(1)list_display:列表时,定制显示的列。注意:不能放多对多字段

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):list_display = ('user', 'pwd', 'xxxxx')def xxxxx(self, obj):return "xxxxx"

(2)list_display_links,列表时,定制列可以点击跳转

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):list_display = ('user', 'pwd', 'xxxxx')list_display_links = ('pwd',)

(3)list_filter:列表时,定制右侧快速筛选

(4)list_select_related,列表时,连表查询是否自动select_related

(5)list_editable,列表时,可以编辑的列

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):list_display = ('user', 'pwd','ug',)list_editable = ('ug',)

(6)search_fields,列表时,模糊搜索的功能

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):search_fields = ('user', 'pwd')

(7)date_hierarchy,列表时,对Date和DateTime类型进行搜索

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):date_hierarchy = 'ctime'

(8)inlines,详细页面,如果有其他表和当前表做FK,那么详细页面可以进行动态增加和删除(不常用)

class UserInfoInline(admin.StackedInline): # TabularInlineextra = 0model = models.UserInfoclass GroupAdminMode(admin.ModelAdmin):list_display = ('id', 'title',)inlines = [UserInfoInline, ]

(9)action,列表时,定制action中的操作

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):# 定制Action行为具体方法def func(self, request, queryset):print(self, request, queryset)print(request.POST.getlist('_selected_action'))func.short_description = "中文显示自定义Actions"actions = [func, ]# Action选项都是在页面上方显示actions_on_top = True# Action选项都是在页面下方显示actions_on_bottom = False# 是否显示选择个数actions_selection_counter = True

(10)定制HTML模板

add_form_template = None
change_form_template = None
change_list_template = None
delete_confirmation_template = None
delete_selected_confirmation_template = None
object_history_template = None

(11)raw_id_fields,详细页面,针对FK和M2M字段变成以Input框形式

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):raw_id_fields = ('FK字段', 'M2M字段',)

(12)fields,详细页面时,显示字段的字段

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):fields = ('user',)

(13)exclude,详细页面时,排除的字段

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):exclude = ('user',)

(14)readonly_fields,详细页面时,只读字段

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):readonly_fields = ('user',)

(15)fieldsets,详细页面时,使用fieldsets标签对数据进行分割显示

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):fieldsets = (('基本数据', {'fields': ('user', 'pwd', 'ctime',)}),('其他', {'classes': ('collapse', 'wide', 'extrapretty'),  # 'collapse','wide', 'extrapretty''fields': ('user', 'pwd'),}),)

(16)详细页面时,M2M显示时,数据移动选择(方向:上下和左右)

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):filter_vertical = ("m2m字段",) # 或filter_horizontal = ("m2m字段",)

(17)ordering,列表时,数据排序规则

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):ordering = ('-id',)或def get_ordering(self, request):return ['-id', ]

(18)radio_fields,详细页面时,使用radio显示选项(FK默认使用select)

radio_fields = {"ug": admin.VERTICAL} # 或admin.HORIZONTAL

(19)form = ModelForm,用于定制用户请求时候表单验证

from app01 import models
from django.forms import ModelForm
from django.forms import fieldsclass MyForm(ModelForm):others = fields.CharField()class Meta:model = models = models.UserInfofields = "__all__"@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):form = MyForm

(20)empty_value_display = "列数据为空时,显示默认值"

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):empty_value_display = "列数据为空时,默认显示"list_display = ('user','pwd','up')def up(self,obj):return obj.userup.empty_value_display = "指定列数据为空时,默认显示"

4)为项目后台创建超级管理员:python manage.py createsuperuser

from django.contrib import admin# Register your models here.
from blog.models import *class MyAdmin(admin.ModelAdmin):# 用以展示数据list_display = ('username', 'gender', 'email')# 指定搜索字段search_fields = ("username")# 指定列表过滤器list_filter = ("gender")# 指定排序字段ordering = ("-username")# 隐藏字段的设置fieldsets = ((None, {'fields': ('username', "gender")}),("Advanced options", {'classes':('collapse',),'fields':('email', )}))admin.site.register(UserInfo, MyAdmin)

17. 基于mysql的数据库连接

1)在app应用下的__init__.py文件下写入

import pymysqlpymysql.install_as_MySQLdb()

2)在settings.py文件中加入mysql的配置

DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': "ORM","HOST": "127.0.0.1","PORT": "3306","USER": "root","PASSWORD": "root",}
}

dataType:预期服务器返回的数据类型,服务器端返回的数据会根据这个值解析后,传递给回调函数。默认不需要显性指定这个属性,ajax会根据服务器返回的contentType来进行转换:比如我们的服务器相应的contentType为json格式,这时ajax方法就会对相应的内容进行一个json格式的转换,if转换成功,我们在success的回调函数里就会得到一个json格式的对象;转换失败就会触发error这个回调函数。如果我们明确地指定目标类型,就可以使用dataType。比如服务器响应(Response Headers)的contentType是json格式,但我们希望得到的是json字符串,那就可以把dataType: text,这样ajax方法就不会把返回的数据进行json格式的转换。

18. JSON与JSONP

一. JSON

JSON是一种轻量级的数据交换格式

JSON是用字符串来表示Javascript对象,Javascript可以执行这个字符串,得到一个Javascript对象

json就是js对象的一种表现形式(字符串的形式)

1)python与json类型转换时的对应

python        json

dict             object

list,tuple     array

str,unicode   string

int,long,float    number

True            true

False           false

None          null

2)ajax中执行的函数

beforeSend 前置处理,发送之前的函数

success 成功执行后的函数

error 发送错误后执行的函数

complete 不管成功与否都会执行的函数

function testData(){$.ajax('ajax_get',{beforeSend: function(jqXHR, settings){console.log(arguments);console.log('beforeSend');jqXHR.setRequestHeader('test', 'haha');jqXHR.testData = {a:1, b:2};},error: function(){},success: function(data){console.log(data);},complete: function(xhr){console.log(xhr);console.log(xhr.testData)}});}

状态码:

statusCode:{'403': function(jqXHR, textStatus, err){console.log(arguments);console.log('status code 403');console.log("ahha403");},"400": function(){console.log('status code 400');console.log("hhh400")}}

二. JSONP

以下为JSONP的一个简单例子:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON数据作为参数传递,完成回调

<script>function fun1(arg){alert("hello" + arg);}
</script>
<script src="http://127.0.0.1:8001/get_byjsonp/"></script>
def get_byjsonp(req):print("Hello JSONP")return HttpResponse('func1("Steve")')

可以通过javascript动态地创建script标签,这样就可以灵活调用远程服务了。

<script>function addScriptTag(src){var script = document.createElement('script');script.setAttribute("type", "text/javascript");script.src = src;document.body.appendChild(script);}function fun1(arg){alert("Hello" + arg);}window.onload = function(){addScriptTag("http://127.0.0.1:8002/get_byjsonp?callback=fetch");}
</script>

jQuery对JSONP的实现

<script type="text/javascript">$.getJSON("http://127.0.0.1:8001/get_byjsonp?callback=?", function(arg){alert("hello" + arg);});
</script>

注意:在url后面必须添加一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个问号是内部自动生成的一个回调函数。

可以使用$.ajax方法来实现:

$.ajax({url:"http://127.0.0.1:8001/get_byjsonp",dataType:"jsonp",jsonp:'callback',success: function(data){alert(data);}});
def get_byjsonp(req):callback = req.GET.get("callback")return HttpResponse("%s('yuan')" % callback)

19. 循环实现评论数据结构

ret = []comment_list_dict = {}for row in comment_list:row.update({'children': []})comment_list_dict[row['id']] = rowfor item in comment_list:parent_row = comment_list_dict.get(item['parent_id'])if not parent_row:ret.append(item)else:parent_row['children'].append(item)

20. Model&Form&ModelForm:

Model:用于数据库操作

Form:用于用户请求的验证

ModelForm:用于数据库操作(部分),用于用户请求的验证(部分)

1)Model操作:

a. 数据表操作

Code First:创建类->自动生成表

DB First:创建表->自动生成类

字段:数字、字符串、时间、文件、关系

参数:用以指定生成数据库列信息;用于验证(admin、ModelForm);关系(一对一,一对多,多对多)

model数据库查询进阶操作

获取个数

models.tb.objects.filter(name='seven').count()

大于,小于

models.tb.objects.filter(id__gt=1) # 获取id大于1的值

models.tb.objects.filter(id__gte=1) # 获取id大于等于1的值

models.tb.objects.filter(id__lt=1) # 获取id小于10的值

models.tb.objects.filter(id__lte=1) # 获取id小于等于10的值

models.tb.objects.filter(id__lt=1, id__gt=1) # 获取id大于1且小于10的值

in

models.tb.objects.filter(id__in=[11,22,33]) # 获取id等于11,22,33的数据

models.tb.objects.exclude(id__in=[11,22,33])# not in

isnull

models.tb.objects.filter(pub_date__isnull=True)

contains

models.tb.objects.filter(name__contains="ven")

models.tb.objects.filter(name__icontains="ven") # icontains大小写不敏感

models.tb.objects.exclude(name__icontains="ven")

range

models.tb.objects.filter(id__range=[1,2]) # 范围between and

其他类似

startswith, istartswith, endswith, iendswith

order by

models.tb.objects.filter(name='seven').order_by('id') # asc

models.tb.objects.filter(name='seven').order_by('-id') # desc

group by

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

models.tb.objects.filter(c1=1).values('id').annotate(c=Count('num'))

select "app_tb"."id", count("app_tb", "num") as "c" from "app_tb" where "app_tb"."c1" = 1 group by "app_tb"."id"

limit, offset

models.tb.objects.all()[10:20]

regex正则匹配,iregex不区分大小写

Entry.objects.get(title__regex=r'^(An?|The)+')

Entry.objects.get(title__iregex=r'^(an?|the)+')

date

Entry.objects.filter(pub_date__date=datetime.date(2005,1,1))

Entry.objects.filter(pub_date__date__gt=datetime.date(2005,1,1))

year

Entry.objects.filter(pub_date__year=2005)

Entry.objects.filter(pub_date__year__gte=2005)

Python的web框架Django(1):HTTP、简介、静态文件配置、路由系统、视图函数、模板语言、ORM、Ajax、分页器、forms、Cookie、Session、中间件、ModelForm相关推荐

  1. python搭建django框架,Python之Web框架Django项目搭建全过程

    Python之Web框架Django项目搭建全过程 IDE说明: Win7系统 Python:3.5 Django:1.10 Pymysql:0.7.10 Mysql:5.5 注:可通过pip fre ...

  2. python的Web框架Django运行报错:Invalid HTTP_HOST header: 'xxx.xx.xxx.xxx:8000'. You may need to add 'xxx.xx'

    python的Web框架Django运行报错:Invalid HTTP_HOST header: 'xxx.xx.xxx.xxx:8000'. You may need to add 'xxx.xx' ...

  3. python bottle web框架简介

    Bottle 是一个快速,简单,轻量级的 Python WSGI Web 框架.单一文件,只依赖 Python 标准库 .bottle很适合会一点python基础的人使用,因为这框架用起来很简单,只要 ...

  4. django 业务逻辑写在view里吗?_Python Web框架Django简介

    引言 我们都知道,Django是一种基于Python的Web开发框架. 那么,什么是Web开发?Web开发指的是开发基于B/S架构,通过前后端的配合,将后台服务器的数据在浏览器上展现给前台用户的应用. ...

  5. python django 动态网页_Django-手撸简易web框架-实现动态网页-wsgiref初识-jinja2初识-python主流web框架对比-00...

    自己动手实现一个简易版本的web框架 在了解python的三大web框架之前,我们先自己动手实现一个. 备注: 这部分重在掌握实现思路,代码不是重点 代码中也有许多细节并未考虑,重在实现思路 手撸一个 ...

  6. Python Web框架Django学习(二)

    python web框架Django学习(二) 目录:  三.Django创建APP  四.创建登录页面,实现用户交互,后台管理用户(非数据库方式) ========================= ...

  7. Python自动化开发学习的第十一周----WEB框架--Django基础

    WEB框架的本质 对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. #!/usr/bin/env python #coding:utf-8imp ...

  8. Web框架Django使用概览

    Web框架Django使用概览 标签: django python 1 开始一个新项目 django的安装比较简单,在命令行里执行下面的命令. pip install django 为了能使用djan ...

  9. 比我的脸还干的gan货——Python Flask Web 框架入门

    Flask是一个轻量级的基于Python的web框架. 本文适合有一定HTML.Python.网络基础的同学阅读. 1. 简介 这份文档中的代码使用 Python 3 运行. 是的,所以读者需要自己在 ...

最新文章

  1. 【多标签文本分类】Semantic-Unit-Based Dilated Convolution for Multi-Label Text Classification
  2. 该怎么写Angular JS
  3. SQL Server Guid和NEWSEQUENTIALID函数
  4. java 输出ssl握手信息_使用Java的SSL握手错误
  5. GeneXus笔记本—城市级联下拉
  6. 将MongoDB集成到您的Spring项目中
  7. leetcode701. 二叉搜索树中的插入操作(dfs)
  8. 深圳不完全启示录之初来乍到----1
  9. 一年级abb式词语并造句_一年级语文ABB式词语专项练习附答案,考考孩子!
  10. 侯捷大师畅谈技术人生与读书感悟
  11. 原生js添加鼠标事件的兼容性写法
  12. py验证码-联众验证码接入
  13. linu修改open files无效_安卓容器app如何使用 容器app修改机型方法【详解】
  14. Euraka的搭建和使用
  15. 两台ROS相互通信,并测试节点
  16. win7 Host 与virtualbox 中的 ubuntu 11.04 共享文件夹
  17. 云数据库PostgreSQL新人怎样上手
  18. vlan间路由的实现(思科模拟器)
  19. linux内核开发(基础)
  20. 市场调研-全球与中国沉鱼饲料市场现状及未来发展趋势

热门文章

  1. EEMD(Ensemble Empirical Mode Decomposition)集合经验模式分解代码
  2. [转]Nginx基本功能极速入门
  3. Python format函数——学习笔记
  4. mac电脑无法读取移动硬盘
  5. web安全攻防实训笔记
  6. Oracle函数之DECODE函数
  7. 三相的稳压电源输出电压是多少?有什么优点?
  8. Mysql:You can't specify target table for update in FROM clause的解决办法
  9. 了解“感应雷”危害,针对性防护
  10. 0基础自学破解长达1天后的经验总结