当前位置: 首页 > news >正文

Django

Django 简介

基本介绍

Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。

使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 Django 本身基于 MVC 模型,即 Model(模型)+ View(视图)+ Controller(控制器)设计模式,MVC 模式使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。

MVC 优势:

  • 低耦合

  • 开发快捷

  • 部署方便

  • 可重用性高

  • 维护成本低

  • ...

Python 加 Django 是快速开发、设计、部署网站的最佳组合。

特点

  • 强大的数据库功能

  • 自带强大的后台功能

  • 优雅的网址

MVC 与 MTV模型

MVC 模型

MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

MVC 以一种插件式的、松耦合的方式连接在一起。

  • 模型(M)- 编写程序应有的功能,负责业务对象与数据库的映射(ORM)。

  • 视图(V)- 图形界面,负责与用户的交互(页面)。

  • 控制器(C)- 负责转发请求,对请求进行处理。

用户操作流程图:

MTV 模型

Django 的 MTV 模式本质上和 MVC 是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django 的 MTV 分别是指:

  • M 表示模型(Model):编写程序应有的功能,负责业务对象与数据库的映射(ORM)。

  • T 表示模板 (Template):负责如何把页面(html)展示给用户。

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

除了以上三层之外,还需要一个 URL 分发器,它的作用是将一个个 URL 的页面请求分发给不同的 View 处理,View 再调用相应的 Model 和 Template,MTV 的响应模式如下所示:

简易图:

用户操作流程图:

解析:

用户通过浏览器向我们的服务器发起一个请求(request),这个请求会去访问视图函数:

  • a.如果不涉及到数据调用,那么这个时候视图函数直接返回一个模板也就是一个网页给用户。

  • b.如果涉及到数据调用,那么视图函数调用模型,模型去数据库查找数据,然后逐级返回。

视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。

参考地址:

[1]  https://www.cnblogs.com/liuhui0308/p/12189658.html 

Django 创建第一个项目

本章我们将介绍Django 管理工具及如何使用 Django 来创建项目,第一个项目我们以 HelloWorld 来命令项目。

Django 管理工具

安装 Django 之后,您现在应该已经有了可用的管理工具 django-admin,Windows 如果没有配置环境变量可以用 django-admin。

我们可以来看下django-admin 的命令介绍:

$ django-admin
​
Type 'django-admin help <subcommand>' for help on a specific subcommand.
​
Available subcommands:
​
[django]checkcompilemessagescreatecachetabledbshelldiffsettingsdumpdataflushinspectdbloaddatamakemessagesmakemigrationsmigraterunserversendtestemailshellshowmigrationssqlflushsqlmigratesqlsequenceresetsquashmigrationsstartappstartprojecttesttestserver
……省略部分……

创建第一个项目

使用 django-admin 来创建 HelloWorld 项目:

django-admin startproject HelloWorld

创建完成后我们可以查看下项目的目录结构:

$ cd HelloWorld/
$ tree
.
|-- HelloWorld
|   |-- __init__.py
|   |-- asgi.py
|   |-- settings.py
|   |-- urls.py
|   `-- wsgi.py
`-- manage.py

目录说明:

  • HelloWorld: 项目的容器。

  • manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。

  • HelloWorld/init.py: 一个空文件,告诉 Python 该目录是一个 Python 包。

  • HelloWorld/asgi.py: 一个 ASGI 兼容的 Web 服务器的入口,以便运行你的项目。

  • HelloWorld/settings.py: 该 Django 项目的设置/配置。

  • HelloWorld/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。

  • HelloWorld/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。

接下来我们进入 HelloWorld 目录输入以下命令,启动服务器:

python3 manage.py runserver 0.0.0.0:8000

0.0.0.0 让其它电脑可连接到开发服务器,8000 为端口号。如果不说明,那么端口号默认为 8000。

在浏览器输入你服务器的 ip(这里我们输入本机 IP 地址: 127.0.0.1:8000) 及端口号,如果正常启动,输出结果如下:

视图和 URL 配置

在先前创建的 HelloWorld 目录下的 HelloWorld 目录新建一个 views.py 文件,并输入代码:

from django.http import HttpResponsedef hello(request):return HttpResponse("Hello world ! ")

接着,绑定 URL 与视图函数。打开 urls.py 文件,删除原来代码,将以下代码复制粘贴到 urls.py 文件中:

from django.conf.urls import urlfrom . import viewsurlpatterns = [url(r'^$', views.hello),
]

整个目录结构如下:

$ tree
.
|-- HelloWorld
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- settings.py
|   |-- settings.pyc
|   |-- urls.py              # url 配置
|   |-- urls.pyc
|   |-- views.py              # 添加的视图文件
|   |-- views.pyc             # 编译后的视图文件
|   |-- wsgi.py
|   `-- wsgi.pyc
`-- manage.py

完成后,启动 Django 开发服务器,并在浏览器访问打开浏览器并访问:

from django.urls import pathfrom . import viewsurlpatterns = [path('hello/', views.hello),
]

通过浏览器打开 http://127.0.0.1:8000/hello,输出结果如下:

注意:项目中如果代码有改动,服务器会自动监测代码的改动并自动重新载入,所以如果你已经启动了服务器则不需手动重启。

path() 函数

Django path() 可以接收四个参数,分别是两个必选参数:route、view 和两个可选参数:kwargs、name。

语法格式:

path(route, view, kwargs=None, name=None)
  • route: 字符串,表示 URL 规则,与之匹配的 URL 会执行对应的第二个参数 view。

  • view: 用于执行与正则表达式匹配的 URL 请求。

  • kwargs: 视图使用的字典类型的参数。

  • name: 用来反向获取 URL。

Django2. 0中可以使用 re_path() 方法来兼容 1.x 版本中的 url() 方法,一些正则表达式的规则也可以通过 re_path() 来实现 。

from django.urls import include, re_path
​
urlpatterns = [re_path(r'^index/$', views.index, name='index'),re_path(r'^bio/(?P<username>\w+)/$', views.bio, name='bio'),re_path(r'^weblog/', include('blog.urls')),...
]

Django 模板

在上一章节中我们使用 django.http.HttpResponse() 来输出 "Hello World!"。该方式将数据与视图混合在一起,不符合 Django 的 MVC 思想。

本章节我们将为大家详细介绍 Django 模板的应用,模板是一个文本,用于分离文档的表现形式和内容。

模板应用实例

我们接着上一章节的项目将在 HelloWorld 目录底下创建 templates 目录并建立 runoob.html文件,整个目录结构如下:

HelloWorld/
|-- HelloWorld
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- settings.py
|   |-- settings.pyc
|   |-- urls.py
|   |-- urls.pyc
|   |-- views.py
|   |-- views.pyc
|   |-- wsgi.py
|   `-- wsgi.pyc
|-- manage.py
`-- templates`-- runoob.html

runoob.html 文件代码如下:

<h1>{{ hello }}</h1>

从模板中我们知道变量使用了双括号。

接下来我们需要向Django说明模板文件的路径,修改HelloWorld/settings.py,修改 TEMPLATES 中的 DIRS 为 [os.path.join(BASE_DIR, 'templates')],如下所示:

TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [os.path.join(BASE_DIR, 'templates')],       # 修改位置'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request','django.contrib.auth.context_processors.auth','django.contrib.messages.context_processors.messages',],},},
]

我们现在修改 views.py,增加一个新的对象,用于向模板提交数据:

from django.shortcuts import renderdef runoob(request):context          = {}context['hello'] = 'Hello World!'return render(request, 'runoob.html', context)
from django.urls import pathfrom . import viewsurlpatterns = [path('runoob/', views.runoob),
]

可以看到,我们这里使用 render 来替代之前使用的 HttpResponse。render 还使用了一个字典 context 作为参数。

context 字典中元素的键值 hello 对应了模板中的变量 {{ hello }}。

再次访问 http://127.0.0.1:8000/runoob,可以看到页面:

这样我们就完成了使用模板来输出数据,从而实现数据与视图分离。

Django 模板标签

变量

模板语法:

view:{"HTML变量名" : "views变量名"}
HTML:{{变量名}}
from django.shortcuts import render
​
def runoob(request):views_name = "菜鸟教程"return  render(request,"runoob.html", {"name":views_name})

templates 中的 runoob.html :

<p>{{ name }}</p>

再次访问 http://127.0.0.1:8000/runoob,可以看到页面:

列表

templates 中的 runoob.html中,可以用 . 索引下标取出对应的元素。

from django.shortcuts import render
​
def runoob(request):views_list = ["菜鸟教程1","菜鸟教程2","菜鸟教程3"]return render(request, "runoob.html", {"views_list": views_list})
<p>{{ views_list }}</p>   # 取出整个列表
<p>{{ views_list.0 }}</p> # 取出列表的第一个元素

再次访问 http://127.0.0.1:8000/runoob,可以看到页面:

字典

templates 中的 runoob.html中,可以用 .键 取出对应的值。

from django.shortcuts import render
​
def runoob(request):views_dict = {"name":"菜鸟教程"}return render(request, "runoob.html", {"views_dict": views_dict})
<p>{{ views_dict }}</p>
<p>{{ views_dict.name }}</p>

再次访问 http://127.0.0.1:8000/runoob,可以看到页面:

过滤器

模板语法:

{{ 变量名 | 过滤器:可选参数 }}

模板过滤器可以在变量被显示前修改它,过滤器使用管道字符,如下所示:

{{ name|lower }}

{{ name }} 变量被过滤器 lower 处理后,文档大写转换文本为小写。

过滤管道可以被* 套接* ,既是说,一个过滤器管道的输出又可以作为下一个管道的输入:

{{ my_list|first|upper }}

以上实例将第一个元素并将其转化为大写。

有些过滤器有参数。 过滤器的参数跟随冒号之后并且总是以双引号包含。 例如:

{{ bio|truncatewords:"30" }}

这个将显示变量 bio 的前30个词。

其他过滤器:

  • addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。

  • date : 按指定的格式字符串参数格式化 date 或者 datetime 对象,{{ pub_date|date:"F j, Y" }}

{{ pub_date|date:"F j, Y" }}
  • length : 返回变量的长度。

default

default 为变量提供一个默认值。

如果 views 传的变量的布尔值是 false,则使用指定的默认值。

以下值为 false:

0  0.0  False  0j  ""  []  ()  set()  {}  None
from django.shortcuts import render
​
def runoob(request):name =0return render(request, "runoob.html", {"name": name})
{{ name|default:"菜鸟教程666" }}

再次访问 http://127.0.0.1:8000/runoob,可以看到页面:

length

返回对象的长度,适用于字符串和列表。

字典返回的是键值对的数量,集合返回的是去重后的长度。

from django.shortcuts import renderdef runoob(request):name ="菜鸟教程"return render(request, "runoob.html", {"name": name})
{{ name|length}}

再次访问 http://127.0.0.1:8000/runoob,可以看到页面:

filesizeformat

以更易读的方式显示文件的大小(即'13 KB', '4.1 MB', '102 bytes'等)。

字典返回的是键值对的数量,集合返回的是去重后的长度。

from django.shortcuts import renderdef runoob(request):num=1024return render(request, "runoob.html", {"num": num})
{{ num|filesizeformat}}

再次访问 http://127.0.0.1:8000/runoob,可以看到页面:

date

根据给定格式对一个日期变量进行格式化。

格式 Y-m-d H:i:s返回 年-月-日 小时:分钟:秒 的格式时间。

from django.shortcuts import renderdef runoob(request):import datetimenow  =datetime.datetime.now()return render(request, "runoob.html", {"time": now})
{{ time|date:"Y-m-d" }}

再次访问 http://127.0.0.1:8000/runoob,可以看到页面:

truncatechars

如果字符串包含的字符总个数多于指定的字符数量,那么会被截断掉后面的部分。

截断的字符串将以 ... 结尾。

from django.shortcuts import renderdef runoob(request):views_str = "菜鸟教程"return render(request, "runoob.html", {"views_str": views_str})
{{ views_str|truncatechars:2}}

再访问访问 http://127.0.0.1:8000/runoob,可以看到页面:

safe

将字符串标记为安全,不需要转义。

要保证 views.py 传过来的数据绝对安全,才能用 safe。

和后端 views.py 的 mark_safe 效果相同。

Django 会自动对 views.py 传到HTML文件中的标签语法进行转义,令其语义失效。加 safe 过滤器是告诉 Django 该数据是安全的,不必对其进行转义,可以让该数据语义生效。

from django.shortcuts import renderdef runoob(request):views_str = "<a href='https://www.runoob.com/'>点击跳转</a>"return render(request, "runoob.html", {"views_str": views_str})
{{ views_str|safe }}

再访问访问 http://127.0.0.1:8000/runoob,可以看到页面:

if/else 标签

基本语法格式如下:

{% if condition %}... display
{% endif %}

或者:

{% if condition1 %}... display 1
{% elif condition2 %}... display 2
{% else %}... display 3
{% endif %}

根据条件判断是否输出。if/else 支持嵌套。

{% if %} 标签接受 and , or 或者 not 关键字来对多个变量做判断 ,或者对变量取反( not ),例如:

{% if athlete_list and coach_list %}athletes 和 coaches 变量都是可用的。
{% endif %}
from django.shortcuts import renderdef runoob(request):views_num = 88return render(request, "runoob.html", {"num": views_num})
{%if num > 90 and num <= 100 %}
优秀
{% elif num > 60 and num <= 90 %}
合格
{% else %}
一边玩去~
{% endif %}

再访问访问 http://127.0.0.1:8000/runoob,可以看到页面:

for 标签

{% for %} 允许我们在一个序列上迭代。

与 Python 的 for 语句的情形类似,循环语法是 for X in Y ,Y 是要迭代的序列而 X 是在每一个特定的循环中使用的变量名称。

每一次循环中,模板系统会渲染在 {% for %} 和 {% endfor %} 之间的所有内容。

例如,给定一个运动员列表 athlete_list 变量,我们可以使用下面的代码来显示这个列表:

<ul>
{% for athlete in athlete_list %}<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
from django.shortcuts import renderdef runoob(request):views_list = ["菜鸟教程","菜鸟教程1","菜鸟教程2","菜鸟教程3",]return render(request, "runoob.html", {"views_list": views_list})
{% for i in views_list %}
{{ i }}
{% endfor %}

再访问访问 http://127.0.0.1:8000/runoob,可以看到页面:

给标签增加一个 reversed 使得该列表被反向迭代:

{% for athlete in athlete_list reversed %}
...
{% endfor %}
{% for i in views_list  reversed%}
{{ i }}
{% endfor %}

再访问访问 http://127.0.0.1:8000/runoob,可以看到页面:

遍历字典: 可以直接用字典 .items 方法,用变量的解包分别获取键和值。

from django.shortcuts import renderdef runoob(request):views_dict = {"name":"菜鸟教程","age":18}return render(request, "runoob.html", {"views_dict": views_dict})
{% for i,j in views_dict.items %}
{{ i }}---{{ j }}
{% endfor %}

再访问访问 http://127.0.0.1:8000/runoob,可以看到页面:

在 {% for %} 标签里可以通过 {{forloop}} 变量获取循环序号。

  • forloop.counter: 顺序获取循环序号,从 1 开始计算

  • forloop.counter0: 顺序获取循环序号,从 0 开始计算

  • forloop.revcounter: 倒叙获取循环序号,结尾序号为 1

  • forloop.revcounter0: 倒叙获取循环序号,结尾序号为 0

  • forloop.first(一般配合if标签使用): 第一条数据返回 True,其他数据返回 False

  • forloop.last(一般配合if标签使用): 最后一条数据返回 True,其他数据返回 False

from django.shortcuts import renderdef runoob(request):views_list = ["a", "b", "c", "d", "e"]return render(request, "runoob.html", {"listvar": views_list})
{% for i in listvar %}{{ forloop.counter }}{{ forloop.counter0 }}{{ forloop.revcounter }}{{ forloop.revcounter0 }}{{ forloop.first }}{{ forloop.last }}
{% endfor %}

再访问访问 http://127.0.0.1:8000/runoob,可以看到页面:

{% empty %}

可选的 {% empty %} 从句:在循环为空的时候执行(即 in 后面的参数布尔值为 False )。

from django.shortcuts import renderdef runoob(request):views_list = []return render(request, "runoob.html", {"listvar": views_list})
{% for i in listvar %}{{ forloop.counter0 }}
{% empty %}空空如也~
{% endfor %}

再访问访问 http://127.0.0.1:8000/runoob,可以看到页面:

可以嵌套使用 {% for %} 标签:

{% for athlete in athlete_list %}<h1>{{ athlete.name }}</h1><ul>{% for sport in athlete.sports_played %}<li>{{ sport }}</li>{% endfor %}</ul>
{% endfor %}

ifequal/ifnotequal 标签

{% ifequal %} 标签比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中所有的值。

下面的例子比较两个模板变量 user 和 currentuser :

{% ifequal user currentuser %}<h1>Welcome!</h1>
{% endifequal %}

和 {% if %} 类似, {% ifequal %} 支持可选的 {% else%} 标签:8

{% ifequal section 'sitenews' %}<h1>Site News</h1>
{% else %}<h1>No News Here</h1>
{% endifequal %}

注释标签

Django 注释使用 {# #}。

{# 这是一个注释 #}

include 标签

{% include %} 标签允许在模板中包含其它的模板的内容。

下面这个例子都包含了 nav.html 模板:

{% include "nav.html" %}

csrf_token

csrf_token 用于form表单中,作用是跨站请求伪造保护。

如果不用{% csrf_token %}标签,在用 form 表单时,要再次跳转页面会报403权限错误。

用了{% csrf_token %}标签,在 form 表单提交数据时,才会成功。

解析:

首先,向服务器发送请求,获取登录页面,此时中间件 csrf 会自动生成一个隐藏input标签,该标签里的 value 属性的值是一个随机的字符串,用户获取到登录页面的同时也获取到了这个隐藏的input标签。

然后,等用户需要用到form表单提交数据的时候,会携带这个 input 标签一起提交给中间件 csrf,原因是 form 表单提交数据时,会包括所有的 input 标签,中间件 csrf 接收到数据时,会判断,这个随机字符串是不是第一次它发给用户的那个,如果是,则数据提交成功,如果不是,则返回403权限错误。

自定义标签和过滤器

1、在应用目录下创建 templatetags 目录(与 templates 目录同级,目录名只能是 templatetags)。

HelloWorld/
|-- HelloWorld
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- settings.py
...
|-- manage.py
`-- templatetags
`-- templates

2、在 templatetags 目录下创建任意 py 文件,如:my_tags.py

3、my_tags.py 文件代码如下:

from django import templateregister = template.Library()   #register的名字是固定的,不可改变

修改 settings.py 文件的 TEMPLATES 选项配置,添加 libraries 配置:

...
TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [BASE_DIR, "/templates",],'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request','django.contrib.auth.context_processors.auth','django.contrib.messages.context_processors.messages',],"libraries":{                          # 添加这边三行配置'my_tags':'templatetags.my_tags'   # 添加这边三行配置        }                                      # 添加这边三行配置},},
]
...

4、利用装饰器 @register.filter 自定义过滤器。

注意:装饰器的参数最多只能有 2 个。

@register.filter
def my_filter(v1, v2):return v1 * v2

5、利用装饰器 @register.simple_tag 自定义标签。

@register.simple_tag
def my_tag1(v1, v2, v3):return v1 * v2 * v3

6、在使用自定义标签和过滤器前,要在 html 文件 body 的最上方中导入该 py 文件。

{% load my_tags %}

7、在 HTML 中使用自定义过滤器。

{{ 11|my_filter:22 }}

8、在 HTML 中使用自定义标签。

{% my_tag1 11 22 33 %}

9、语义化标签

在该 py 文件中导入 mark_safe。

from django.utils.safestring import mark_safe

定义标签时,用上 mark_safe 方法,令标签语义化,相当于 jQuery 中的 html() 方法。

和前端HTML文件中的过滤器 safe 效果一样。

@register.simple_tag
def my_html(v1, v2):temp_html = "<input type='text' id='%s' class='%s' />" %(v1, v2)return mark_safe(temp_html)

在HTML中使用该自定义标签,在页面中动态创建标签。

{% my_html "zzz" "xxx" %}

配置静态文件

1、在项目根目录下创建 statics 目录。

2、在 settings 文件的最下方配置添加以下配置:

STATIC_URL = '/static/' # 别名 
STATICFILES_DIRS = [ os.path.join(BASE_DIR, "statics"), 
]

3、在 statics 目录下创建 css 目录,js 目录,images 目录,plugins 目录, 分别放 css文件,js文件,图片,插件。

4、把 bootstrap 框架放入插件目录 plugins。

5、在 HTML 文件的 head 标签中引入 bootstrap。

注意:此时引用路径中的要用配置文件中的别名 static,而不是目录 statics。

<link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7/dist/css/bootstrap.css">

在模板中使用需要加入 {% load static %} 代码,以下实例我们从静态目录中引入图片。

from django.shortcuts import renderdef runoob(request):name ="菜鸟教程"return render(request, "runoob.html", {"name": name})
{% load static %}
{{name}}<img src="{% static "images/runoob-logo.png" %}" alt="runoob-logo">

再访问访问 http://127.0.0.1:8000/runoob,可以看到页面:

模板继承

模板可以用继承的方式来实现复用,减少冗余内容。

网页的头部和尾部内容一般都是一致的,我们就可以通过模板继承来实现复用。

父模板用于放置可重复利用的内容,子模板继承父模板的内容,并放置自己的内容。

父模板

标签 block...endblock: 父模板中的预留区域,该区域留给子模板填充差异性的内容,不同预留区域名字不能相同。

{% block 名称 %} 
预留给子模板的区域,可以设置设置默认内容
{% endblock 名称 %}

子模板

子模板使用标签 extends 继承父模板:

{% extends "父模板路径"%} 

子模板如果没有设置父模板预留区域的内容,则使用在父模板设置的默认内容,当然也可以都不设置,就为空。

子模板设置父模板预留区域的内容:

{ % block 名称 % }
内容 
{% endblock 名称 %}

接下来我们先创建之前项目的 templates 目录中添加 base.html 文件,

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body><h1>Hello World!</h1><p>菜鸟教程 Django 测试。</p>{% block mainbody %}<p>original</p>{% endblock %}
</body>
</html>

以上代码中,名为 mainbody 的 block 标签是可以被继承者们替换掉的部分。

所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。

runoob.html 中继承 base.html,并替换特定 block,runoob.html 修改后的代码如下:

{%extends "base.html" %}{% block mainbody %}
<p>继承了 base.html 文件</p>
{% endblock %}

第一行代码说明 runoob.html 继承了 base.html 文件。可以看到,这里相同名字的 block 标签用以替换 base.html 的相应 block。

重新访问地址 http://127.0.0.1:8000/runoob,输出结果如下:

Django 模型

Django 对各种数据库提供了很好的支持,包括:PostgreSQL、MySQL、SQLite、Oracle。

Django 为这些数据库提供了统一的调用API。 我们可以根据自己业务需求选择不同的数据库。

MySQL 是 Web 应用中最常用的数据库。本章节我们将以 Mysql 作为实例进行介绍。你可以通过本站的 MySQL 教程 了解更多 MySQL 的基础知识。

如果你没安装 mysql 驱动,可以执行以下命令安装:

sudo pip3 install pymysql

Django ORM

Django 模型使用自带的 ORM。

对象关系映射(Object Relational Mapping,简称 ORM )用于实现面向对象编程语言里不同类型系统的数据之间的转换。

ORM 在业务逻辑层和数据库层之间充当了桥梁的作用。

ORM 是通过使用描述对象和数据库之间的映射的元数据,将程序中的对象自动持久化到数据库中。

使用 ORM 的好处:

  • 提高开发效率。

  • 不同数据库可以平滑切换。

使用 ORM 的缺点:

  • ORM 代码转换为 SQL 语句时,需要花费一定的时间,执行效率会有所降低。

  • 长期写 ORM 代码,会降低编写 SQL 语句的能力。

ORM 解析过程:

  • 1、ORM 会将 Python 代码转成为 SQL 语句。

  • 2、SQL 语句通过 pymysql 传送到数据库服务端。

  • 3、在数据库中执行 SQL 语句并将结果返回。

ORM 对应关系表:

数据库配置

Django 如何使用 mysql 数据库

创建 MySQL 数据库( ORM 无法操作到数据库级别,只能操作到数据表)语法:

create database 数据库名称 default charset=utf8; # 防止编码问题,指定为 utf8

例如我们创建一个名为 runoob 数据库,编码指定为 utf8:

create database runoob default charset=utf8;   

我们在项目的 settings.py 文件中找到 DATABASES 配置项,将其信息修改为:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql',    # 数据库引擎'NAME': 'runoob', # 数据库名称'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址 127.0.0.1 'PORT': 3306, # 端口 'USER': 'root',  # 数据库用户名'PASSWORD': '123456', # 数据库密码}  
}

如果你使用了 Python2.x 版本这里添加了中文注释,所以你需要在 HelloWorld/settings.py 文件头部添加 # -- coding: UTF-8 --。

上面包含数据库名称和用户的信息,它们与 MySQL 中对应数据库和用户的设置相同。Django 根据这一设置,与 MySQL 中相应的数据库和用户连接起来。

接下来,告诉 Django 使用 pymysql 模块连接 mysql 数据库:

# 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()

定义模型

创建 APP

Django 规定,如果要使用模型,必须要创建一个 app。我们使用以下命令创建一个 TestModel 的 app:

django-admin.py startapp TestModel

目录结构如下:

HelloWorld
|-- HelloWorld
|-- manage.py
...
|-- TestModel
|   |-- __init__.py
|   |-- admin.py
|   |-- models.py
|   |-- tests.py
|   `-- views.py

我们修改 TestModel/models.py 文件,代码如下:

# models.py
from django.db import modelsclass Test(models.Model):name = models.CharField(max_length=20)

以上的类名代表了数据库表名,且继承了models.Model,类里面的字段代表数据表中的字段(name),数据类型则由CharField(相当于varchar)、DateField(相当于datetime), max_length 参数限定长度。

接下来在 settings.py 中找到INSTALLED_APPS这一项,如下:

INSTALLED_APPS = ('django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','TestModel',               # 添加此项
)

在命令行中运行:

$ python3 manage.py migrate   # 创建表结构$ python3 manage.py makemigrations TestModel  # 让 Django 知道我们在我们的模型有一些变更
$ python3 manage.py migrate TestModel   # 创建表结构

看到几行 "Creating table…" 的字样,你的数据表就创建好了。

Creating tables ...
……
Creating table TestModel_test  #我们自定义的表
……

表名组成结构为:应用名_类名(如:TestModel_test)。

注意:尽管我们没有在 models 给表设置主键,但是 Django 会自动添加一个 id 作为主键。

常见报错信息

如果执行以上命令时会出现如下报错信息:

原因是 MySQLclient 目前只支持到 Python3.4,因此如果使用的更高版本的 python,需要修改如下:

通过报错信息的文件路径找到 ...site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql 这个路径里的 base.py 文件,把这两行代码注释掉(代码在文件开头部分):

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

一般点报错的代码文件路径信息,会自动跳转到报错文件中行数,此时我们在报错的代码行数注释掉。

如果出现以下报错信息:

我们点报错的代码文件路径,跳转到报错文件中行数,此时我们在报错的代码行数之前加上:

query = query.encode()

数据库操作

接下来我们在 HelloWorld 目录中添加 testdb.py 文件(下面介绍),并修改 urls.py:

from django.urls import pathfrom . import views,testdburlpatterns = [path('runoob/', views.runoob),path('testdb/', testdb.testdb),
]

添加数据

添加数据需要先创建对象,然后再执行 save 函数,相当于SQL中的INSERT:

# -*- coding: utf-8 -*-from django.http import HttpResponsefrom TestModel.models import Test# 数据库操作
def testdb(request):test1 = Test(name='runoob')test1.save()return HttpResponse("<p>数据添加成功!</p>")

访问 http://127.0.0.1:8000/testdb 就可以看到数据添加成功的提示。

输出结果如下:

获取数据

Django提供了多种方式来获取数据库的内容,如下代码所示:

# -*- coding: utf-8 -*-from django.http import HttpResponsefrom TestModel.models import Test# 数据库操作
def testdb(request):# 初始化response = ""response1 = ""# 通过objects这个模型管理器的all()获得所有数据行,相当于SQL中的SELECT * FROMlist = Test.objects.all()# filter相当于SQL中的WHERE,可设置条件过滤结果response2 = Test.objects.filter(id=1) # 获取单个对象response3 = Test.objects.get(id=1) # 限制返回的数据 相当于 SQL 中的 OFFSET 0 LIMIT 2;Test.objects.order_by('name')[0:2]#数据排序Test.objects.order_by("id")# 上面的方法可以连锁使用Test.objects.filter(name="runoob").order_by("id")# 输出所有数据for var in list:response1 += var.name + " "response = response1return HttpResponse("<p>" + response + "</p>")

更新数据

修改数据可以使用 save() 或 update():

# -*- coding: utf-8 -*-from django.http import HttpResponsefrom TestModel.models import Test# 数据库操作
def testdb(request):# 修改其中一个id=1的name字段,再save,相当于SQL中的UPDATEtest1 = Test.objects.get(id=1)test1.name = 'Google'test1.save()# 另外一种方式#Test.objects.filter(id=1).update(name='Google')# 修改所有的列# Test.objects.all().update(name='Google')return HttpResponse("<p>修改成功</p>")

删除数据

删除数据库中的对象只需调用该对象的delete()方法即可:

# -*- coding: utf-8 -*-from django.http import HttpResponsefrom TestModel.models import Test# 数据库操作
def testdb(request):# 删除id=1的数据test1 = Test.objects.get(id=1)test1.delete()# 另外一种方式# Test.objects.filter(id=1).delete()# 删除所有数据# Test.objects.all().delete()return HttpResponse("<p>删除成功</p>")

Django 表单

HTML表单是网站交互性的经典方式。 本章将介绍如何用Django对用户提交的表单数据进行处理。


HTTP 请求

HTTP协议以"请求-回复"的方式工作。客户发送请求时,可以在请求中附加数据。服务器通过解析请求,就可以获得客户传来的数据,并根据URL来提供特定的服务。

GET 方法

我们在之前的项目中创建一个 search.py 文件,用于接收用户的请求:

from django.http import HttpResponse
from django.shortcuts import render
# 表单
def search_form(request):return render(request, 'search_form.html')# 接收请求数据
def search(request):  request.encoding='utf-8'if 'q' in request.GET and request.GET['q']:message = '你搜索的内容为: ' + request.GET['q']else:message = '你提交了空表单'return HttpResponse(message)

在模板目录 templates 中添加 search_form.html 表单:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body><form action="/search/" method="get"><input type="text" name="q"><input type="submit" value="搜索"></form>
</body>
</html>

urls.py 规则修改为如下形式:

from django.conf.urls import url
from . import views,testdb,searchurlpatterns = [url(r'^hello/$', views.runoob),url(r'^testdb/$', testdb.testdb),url(r'^search-form/$', search.search_form),url(r'^search/$', search.search),
]

访问地址 http://127.0.0.1:8000/search-form/ 并搜索,结果如下所示:

POST 方法

上面我们使用了GET方法。视图显示和请求处理分成两个函数处理。

提交数据时更常用POST方法。我们下面使用该方法,并用一个URL和处理函数,同时显示视图和处理请求。

我们在 templates 创建 post.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body><form action="/search-post/" method="post">{% csrf_token %}<input type="text" name="q"><input type="submit" value="搜索"></form><p>{{ rlt }}</p>
</body>
</html>

在模板的末尾,我们增加一个 rlt 记号,为表格处理结果预留位置。

表格后面还有一个{% csrf_token %}的标签。csrf 全称是 Cross Site Request Forgery。这是Django提供的防止伪装提交请求的功能。POST 方法提交的表格,必须有此标签。

在HelloWorld目录下新建 search2.py 文件并使用 search_post 函数来处理 POST 请求:

# -*- coding: utf-8 -*-from django.shortcuts import render
from django.views.decorators import csrf# 接收POST请求数据
def search_post(request):ctx ={}if request.POST:ctx['rlt'] = request.POST['q']return render(request, "post.html", ctx)

urls.py 规则修改为如下形式:

from django.conf.urls import url
from . import views,testdb,search,search2urlpatterns = [url(r'^hello/$', views.hello),url(r'^testdb/$', testdb.testdb),url(r'^search-form/$', search.search_form),url(r'^search/$', search.search),url(r'^search-post/$', search2.search_post),
]

访问 http://127.0.0.1:8000/search-post/ 显示结果如下:

完成以上实例后,我们的目录结构为:

HelloWorld
|-- HelloWorld
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- search.py
|   |-- search.pyc
|   |-- search2.py
|   |-- search2.pyc
|   |-- settings.py
|   |-- settings.pyc
|   |-- testdb.py
|   |-- testdb.pyc
|   |-- urls.py
|   |-- urls.pyc
|   |-- views.py
|   |-- views.pyc
|   |-- wsgi.py
|   `-- wsgi.pyc
|-- TestModel
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- admin.py
|   |-- admin.pyc
|   |-- apps.py
|   |-- migrations
|   |   |-- 0001_initial.py
|   |   |-- 0001_initial.pyc
|   |   |-- __init__.py
|   |   `-- __init__.pyc
|   |-- models.py
|   |-- models.pyc
|   |-- tests.py
|   `-- views.py
|-- db.sqlite3
|-- manage.py
`-- templates|-- base.html|-- hello.html|-- post.html`-- search_form.html

Request 对象

每个视图函数的第一个参数是一个 HttpRequest 对象,就像下面这个 runoob() 函数:

from django.http import HttpResponsedef runoob(request):return HttpResponse("Hello world")

HttpRequest对象包含当前请求URL的一些信息:

属性描述
path请求页面的全路径,不包括域名—例如, "/hello/"。
method请求中使用的HTTP方法的字符串表示。全大写表示。例如:if request.method == 'GET': do_something() elif request.method == 'POST': do_something_else()
GET包含所有HTTP GET参数的类字典对象。参见QueryDict 文档。
POST包含所有HTTP POST参数的类字典对象。参见QueryDict 文档。服务器收到空的POST请求的情况也是有可能发生的。也就是说,表单form通过HTTP POST方法提交请求,但是表单中可以没有数据。因此,不能使用语句if request.POST来判断是否使用HTTP POST方法;应该使用if request.method == "POST" (参见本表的method属性)。注意: POST不包括file-upload信息。参见FILES属性。
REQUEST为了方便,该属性是POST和GET属性的集合体,但是有特殊性,先查找POST属性,然后再查找GET属性。借鉴PHP's $_REQUEST。例如,如果GET = {"name": "john"} 和POST = {"age": '34'},则 REQUEST["name"] 的值是"john", REQUEST["age"]的值是"34".强烈建议使用GET and POST,因为这两个属性更加显式化,写出的代码也更易理解。
COOKIES包含所有cookies的标准Python字典对象。Keys和values都是字符串。
FILES包含所有上传文件的类字典对象。FILES中的每个Key都是<input type="file" name="" />标签中name属性的值. FILES中的每个value 同时也是一个标准Python字典对象,包含下面三个Keys:filename: 上传文件名,用Python字符串表示content-type: 上传文件的Content typecontent: 上传文件的原始内容注意:只有在请求方法是POST,并且请求页面中<form>有enctype="multipart/form-data"属性时FILES才拥有数据。否则,FILES 是一个空字典。
META包含所有可用HTTP头部信息的字典。 例如:CONTENT_LENGTHCONTENT_TYPEQUERY_STRING: 未解析的原始查询字符串REMOTE_ADDR: 客户端IP地址REMOTE_HOST: 客户端主机名SERVER_NAME: 服务器主机名SERVER_PORT: 服务器端口META 中这些头加上前缀 HTTP_ 为 Key, 冒号(:)后面的为 Value, 例如:HTTP_ACCEPT_ENCODINGHTTP_ACCEPT_LANGUAGEHTTP_HOST: 客户发送的HTTP主机头信息HTTP_REFERER: referring页HTTP_USER_AGENT: 客户端的user-agent字符串HTTP_X_BENDER: X-Bender头信息
user是一个django.contrib.auth.models.User 对象,代表当前登录的用户。如果访问用户当前没有登录,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你可以通过user的is_authenticated()方法来辨别用户是否登录:if request.user.is_authenticated(): # Do something for logged-in users. else: # Do something for anonymous users.只有激活Django中的AuthenticationMiddleware时该属性才可用
session唯一可读写的属性,代表当前会话的字典对象。只有激活Django中的session支持时该属性才可用。
raw_post_data原始HTTP POST数据,未解析过。 高级处理时会有用处。

Request对象也有一些有用的方法:

方法描述
getitem(key)返回GET/POST的键值,先取POST,后取GET。如果键不存在抛出 KeyError。 这是我们可以使用字典语法访问HttpRequest对象。 例如,request["foo"]等同于先request.POST["foo"] 然后 request.GET["foo"]的操作。
has_key()检查request.GET or request.POST中是否包含参数指定的Key。
get_full_path()返回包含查询字符串的请求路径。例如, "/music/bands/the_beatles/?print=true"
is_secure()如果请求是安全的,返回True,就是说,发出的是HTTPS请求。

QueryDict对象

在HttpRequest对象中, GET和POST属性是django.http.QueryDict类的实例。

QueryDict类似字典的自定义类,用来处理单键对应多值的情况。

QueryDict实现所有标准的词典方法。还包括一些特有的方法:

方法描述
getitem和标准字典的处理有一点不同,就是,如果Key对应多个Value,getitem()返回最后一个value。
setitem设置参数指定key的value列表(一个Python list)。注意:它只能在一个mutable QueryDict 对象上被调用(就是通过copy()产生的一个QueryDict对象的拷贝).
get()如果key对应多个value,get()返回最后一个value。
update()参数可以是QueryDict,也可以是标准字典。和标准字典的update方法不同,该方法添加字典 items,而不是替换它们:>>> q = QueryDict('a=1') >>> q = q.copy() # to make it mutable >>> q.update({'a': '2'}) >>> q.getlist('a') ['1', '2'] >>> q['a'] # returns the last ['2']
items()和标准字典的items()方法有一点不同,该方法使用单值逻辑的getitem():>>> q = QueryDict('a=1&a=2&a=3') >>> q.items() [('a', '3')]
values()和标准字典的values()方法有一点不同,该方法使用单值逻辑的getitem():

此外, QueryDict也有一些方法,如下表:

方法描述
copy()返回对象的拷贝,内部实现是用Python标准库的copy.deepcopy()。该拷贝是mutable(可更改的) — 就是说,可以更改该拷贝的值。
getlist(key)返回和参数key对应的所有值,作为一个Python list返回。如果key不存在,则返回空list。 It's guaranteed to return a list of some sort..
setlist(key,list_)设置key的值为list_ (unlike setitem()).
appendlist(key,item)添加item到和key关联的内部list.
setlistdefault(key,list)和setdefault有一点不同,它接受list而不是单个value作为参数。
lists()和items()有一点不同, 它会返回key的所有值,作为一个list, 例如:>>> q = QueryDict('a=1&a=2&a=3') >>> q.lists() [('a', ['1', '2', '3'])]
urlencode()返回一个以查询字符串格式进行格式化后的字符串(例如:"a=2&b=3&b=5")。

Django 视图

视图层

一个视图函数,简称视图,是一个简单的 Python 函数,它接受 Web 请求并且返回 Web 响应。

响应可以是一个 HTML 页面、一个 404 错误页面、重定向页面、XML 文档、或者一张图片...

无论视图本身包含什么逻辑,都要返回响应。代码写在哪里都可以,只要在 Python 目录下面,一般放在项目的 views.py 文件中。

每个视图函数都负责返回一个 HttpResponse 对象,对象中包含生成的响应。

视图层中有两个重要的对象:请求对象(request)与响应对象(HttpResponse)。


请求对象: HttpRequest 对象(简称 request 对象)

以下介绍几个常用的 request 属性。

1、GET

数据类型是 QueryDict,一个类似于字典的对象,包含 HTTP GET 的所有参数。

有相同的键,就把所有的值放到对应的列表里。

取值格式:对象.方法。

get():返回字符串,如果该键对应有多个值,取出该键的最后一个值。

def runoob(request):name = request.GET.get("name")return HttpResponse('姓名:{}'.format(name))

2、POST

数据类型是 QueryDict,一个类似于字典的对象,包含 HTTP POST 的所有参数。

常用于 form 表单,form 表单里的标签 name 属性对应参数的键,value 属性对应参数的值。

取值格式: 对象.方法。

get():返回字符串,如果该键对应有多个值,取出该键的最后一个值。

def runoob(request):name = request.POST.get("name")return HttpResponse('姓名:{}'.format(name))

3、body

数据类型是二进制字节流,是原生请求体里的参数内容,在 HTTP 中用于 POST,因为 GET 没有请求体。

在 HTTP 中不常用,而在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML、Json 等。

def runoob(request):name = request.bodyprint(name)return HttpResponse("菜鸟教程")

4、path

获取 URL 中的路径部分,数据类型是字符串。

def runoob(request):name = request.pathprint(name)return HttpResponse("菜鸟教程")

5、method

获取当前请求的方式,数据类型是字符串,且结果为大写。

def runoob(request):name = request.methodprint(name)return HttpResponse("菜鸟教程")

响应对象:HttpResponse 对象

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

HttpResponse(): 返回文本,参数为字符串,字符串中写文本内容。如果参数为字符串里含有 html 标签,也可以渲染。

def runoob(request):# return HttpResponse("菜鸟教程")return HttpResponse("<a href='https://www.runoob.com/'>菜鸟教程</a>")

render(): 返回文本,第一个参数为 request,第二个参数为字符串(页面名称),第三个参数为字典(可选参数,向页面传递的参数:键为页面参数名,值为views参数名)。

def runoob(request):name ="菜鸟教程"return render(request,"runoob.html",{"name":name})

redirect():重定向,跳转新页面。参数为字符串,字符串中填写页面路径。一般用于 form 表单提交后,跳转到新页面。

def runoob(request):return redirect("/index/")

render 和 redirect 是在 HttpResponse 的基础上进行了封装:

  • render:底层返回的也是 HttpResponse 对象

  • redirect:底层继承的是 HttpResponse 对象

Django 路由

路由简单的来说就是根据用户请求的 URL 链接来判断对应的处理程序,并返回处理结果,也就是 URL 与 Django 的视图建立映射关系。

Django 路由在 urls.py 配置,urls.py 中的每一条配置对应相应的处理方法。

Django 不同版本 urls.py 配置有点不一样:

Django1.1.x 版本

url() 方法:普通路径和正则路径均可使用,需要自己手动添加正则首位限制符号。

from django.conf.urls import url # 用 url 需要引入urlpatterns = [url(r'^admin/$', admin.site.urls),url(r'^index/$', views.index), # 普通路径url(r'^articles/([0-9]{4})/$', views.articles), # 正则路径
]

Django 2.2.x 之后的版本

  • path:用于普通路径,不需要自己手动添加正则首位限制符号,底层已经添加。

  • re_path:用于正则路径,需要自己手动添加正则首位限制符号。

from django.urls import re_path # 用re_path 需要引入
urlpatterns = [path('admin/', admin.site.urls),path('index/', views.index), # 普通路径re_path(r'^articles/([0-9]{4})/$', views.articles), # 正则路径
]

总结:Django1.1.x 版本中的 url 和 Django 2.2.x 版本中的 re_path 用法相同。

正则路径中的分组

正则路径中的无名分组

无名分组按位置传参,一一对应。

views 中除了 request,其他形参的数量要与 urls 中的分组数量一致。

urlpatterns = [path('admin/', admin.site.urls),re_path("^index/([0-9]{4})/$", views.index),
]
from django.shortcuts import HttpResponsedef index(request,year):print(year) # 一个形参代表路径中一个分组的内容,按顺序匹配return HttpResponse('菜鸟教程')

正则路径中的有名分组

语法:

(?P<组名>正则表达式)

有名分组按关键字传参,与位置顺序无关。

views 中除了 request,其他形参的数量要与 urls 中的分组数量一致, 并且 views 中的形参名称要与 urls 中的组名对应。

urlpatterns = [path('admin/', admin.site.urls),re_path("^index/(?P[0-9]{4})/(?P[0-9]{2})/$", views.index),
]
from django.shortcuts import HttpResponse
def index(request, year, month):print(year,month) # 一个形参代表路径中一个分组的内容,按关键字对应匹配return HttpResponse('菜鸟教程')

正则路径中的有名分组

路由分发(include)

存在问题:Django 项目里多个app目录共用一个 urls 容易造成混淆,后期维护也不方便。

解决:使用路由分发(include),让每个app目录都单独拥有自己的 urls。

步骤:

  • 1、在每个 app 目录里都创建一个 urls.py 文件。

  • 2、在项目名称目录下的 urls 文件里,统一将路径分发给各个 app 目录。

from django.contrib import admin
from django.urls import path,include # 从 django.urls 引入 include
urlpatterns = [path('admin/', admin.site.urls),path("app01/", include("app01.urls")),path("app02/", include("app02.urls")),
]

在各自 app 目录下,写自己的 urls.py 文件,进行路径跳转。

app01 目录:

from django.urls import path,re_path 
from app01 import views # 从自己的 app 目录引入 views 
urlpatterns = [ re_path(r'^login/(?P<m>[0-9]{2})/$', views.index, ),
] 

app02 目录:

from django.urls import path,re_path
from app02 import views # 从自己的 app 目录引入views 
urlpatterns = [ re_path("^xxx/(?P[0-9]{4})/$", views.xxx), 
]

在各自 app 目录下的 views.py 文件中写各自的视图函数。

反向解析

随着功能的增加,路由层的 url 发生变化,就需要去更改对应的视图层和模板层的 url,非常麻烦,不便维护。

这时我们可以利用反向解析,当路由层 url 发生改变,在视图层和模板层动态反向解析出更改后的 url,免去修改的操作。

反向解析一般用在模板中的超链接及视图中的重定向。

普通路径

在 urls.py 中给路由起别名,name="路由别名"

path("login1/", views.login, name="login")

在 views.py 中,从 django.urls 中引入 reverse,利用 reverse("路由别名") 反向解析:

return redirect(reverse("login"))

在模板 templates 中的 HTML 文件中,利用 {% url "路由别名" %} 反向解析。

<form action="{% url 'login' %}" method="post"> 

正则路径(无名分组)

在 urls.py 中给路由起别名,name="路由别名"

re_path(r"^login/([0-9]{2})/$", views.login, name="login")

在 views.py 中,从 django.urls 中引入 reverse,利用 reverse("路由别名",args=(符合正则匹配的参数,)) 反向解析。

return redirect(reverse("login",args=(10,)))

在模板 templates 中的 HTML 文件中利用 {% url "路由别名" 符合正则匹配的参数 %} 反向解析。

<form action="{% url 'login' 10 %}" method="post"> 

正则路径(有名分组)

在 urls.py 中给路由起别名,name="路由别名"

re_path(r"^login/(?P<year>[0-9]{4})/$", views.login, name="login")

在 views.py 中,从 django.urls 中引入 reverse,利用 reverse("路由别名",kwargs={"分组名":符合正则匹配的参数}) 反向解析。

return redirect(reverse("login",kwargs={"year":3333}))

在模板 templates 中的 HTML 文件中,利用 {% url "路由别名" 分组名=符合正则匹配的参数 %} 反向解析。

<form action="{% url 'login' year=3333 %}" method="post">

命名空间

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

一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。

一个新的命名空间中可定义任何标识符,它们不会与任何重复的标识符发生冲突,因为重复的定义都处于其它命名空间中。

存在问题:路由别名 name 没有作用域,Django 在反向解析 URL 时,会在项目全局顺序搜索,当查找到第一个路由别名 name 指定 URL 时,立即返回。当在不同的 app 目录下的urls 中定义相同的路由别名 name 时,可能会导致 URL 反向解析错误。

解决:使用命名空间。

普通路径

定义命名空间(include 里面是一个元组)格式如下:

include(("app名称:urls","app名称"))

实例:

path("app01/", include(("app01.urls","app01"))) 
path("app02/", include(("app02.urls","app02")))

在 app01/urls.py 中起相同的路由别名。

path("login/", views.login, name="login")

在 views.py 中使用名称空间,语法格式如下:

reverse("app名称:路由别名")

实例:

return redirect(reverse("app01:login")

在 templates 模板的 HTML 文件中使用名称空间,语法格式如下:

{% url "app名称:路由别名" %}

实例:

<form action="{% url 'app01:login' %}" method="post">

Django Admin 管理工具

Django 提供了基于 web 的管理工具。

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

INSTALLED_APPS = ('django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles',
)

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

激活管理工具

通常我们在生成项目时会在 urls.py 中自动设置好,我们只需去掉注释即可。

配置项如下所示:

# urls.py
from django.conf.urls import url
from django.contrib import adminurlpatterns = [url(r'^admin/', admin.site.urls),
]

当这一切都配置好后,Django 管理工具就可以运行了。


使用管理工具

启动开发服务器,然后在浏览器中访问 http://127.0.0.1:8000/admin/,得到如下界面:

你可以通过命令 python manage.py createsuperuser 来创建超级用户,如下所示:

# python manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@runoob.com
Password:
Password (again):
Superuser created successfully.
[root@solar HelloWorld]#

之后输入用户名密码登录,界面如下:

为了让 admin 界面管理某个数据模型,我们需要先注册该数据模型到 admin。比如,我们之前在 TestModel 中已经创建了模型 Test 。修改 TestModel/admin.py:

from django.contrib import admin
from TestModel.models import Test# Register your models here.
admin.site.register(Test)

刷新后即可看到 Testmodel 数据表:

复杂模型

管理页面的功能强大,完全有能力处理更加复杂的数据模型。

先在 TestModel/models.py 中增加一个更复杂的数据模型:

from django.db import models# Create your models here.
class Test(models.Model):name = models.CharField(max_length=20)class Contact(models.Model):name   = models.CharField(max_length=200)age    = models.IntegerField(default=0)email  = models.EmailField()def __unicode__(self):return self.nameclass Tag(models.Model):contact = models.ForeignKey(Contact, on_delete=models.CASCADE,)name    = models.CharField(max_length=50)def __unicode__(self):return self.name

这里有两个表。Tag 以 Contact 为外部键。一个 Contact 可以对应多个 Tag。

我们还可以看到许多在之前没有见过的属性类型,比如 IntegerField 用于存储整数。

在 TestModel/admin.py 注册多个模型并显示:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag# Register your models here.
admin.site.register([Test, Contact, Tag])

刷新管理页面,显示结果如下:

在以上管理工具我们就能进行复杂模型操作。

如果你之前还未创建表结构,可使用以下命令创建:

$ python manage.py makemigrations TestModel  # 让 Django 知道我们在我们的模型有一些变更
$ python manage.py migrate TestModel   # 创建表结构

自定义表单

我们可以自定义管理页面,来取代默认的页面。比如上面的 "add" 页面。我们想只显示 name 和 email 部分。修改 TestModel/admin.py:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag# Register your models here.
class ContactAdmin(admin.ModelAdmin):fields = ('name', 'email')admin.site.register(Contact, ContactAdmin)
admin.site.register([Test, Tag])

以上代码定义了一个 ContactAdmin 类,用以说明管理页面的显示格式。

里面的 fields 属性定义了要显示的字段。

由于该类对应的是 Contact 数据模型,我们在注册的时候,需要将它们一起注册。显示效果如下:

我们还可以将输入栏分块,每个栏也可以定义自己的格式。修改 TestModel/admin.py为:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag# Register your models here.
class ContactAdmin(admin.ModelAdmin):fieldsets = (['Main',{'fields':('name','email'),}],['Advance',{'classes': ('collapse',), # CSS'fields': ('age',),}])admin.site.register(Contact, ContactAdmin)
admin.site.register([Test, Tag])

上面的栏目分为了 Main 和 Advance 两部分。classes 说明它所在的部分的 CSS 格式。这里让 Advance 部分隐藏:

Advance 部分旁边有一个 Show 按钮,用于展开,展开后可点击 Hide 将其隐藏,如下图所示:

内联(Inline)显示

上面的 Contact 是 Tag 的外部键,所以有外部参考的关系。

而在默认的页面显示中,将两者分离开来,无法体现出两者的从属关系。我们可以使用内联显示,让 Tag 附加在 Contact 的编辑页面上显示。

修改TestModel/admin.py:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag# Register your models here.
class TagInline(admin.TabularInline):model = Tagclass ContactAdmin(admin.ModelAdmin):inlines = [TagInline]  # Inlinefieldsets = (['Main',{'fields':('name','email'),}],['Advance',{'classes': ('collapse',),'fields': ('age',),}])admin.site.register(Contact, ContactAdmin)
admin.site.register([Test])

列表页的显示

在 Contact 输入数条记录后,Contact 的列表页看起来如下:

我们也可以自定义该页面的显示,比如在列表中显示更多的栏目,只需要在 ContactAdmin 中增加 list_display 属性:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag# Register your models here.
class TagInline(admin.TabularInline):model = Tagclass ContactAdmin(admin.ModelAdmin):list_display = ('name','age', 'email') # listinlines = [TagInline]  # Inlinefieldsets = (['Main',{'fields':('name','email'),}],['Advance',{'classes': ('collapse',),'fields': ('age',),}])admin.site.register(Contact, ContactAdmin)
admin.site.register([Test])

搜索功能在管理大量记录时非常有,我们可以使用 search_fields 为该列表页增加搜索栏:

from django.contrib import admin
from TestModel.models import Test,Contact,Tag# Register your models here.
class TagInline(admin.TabularInline):model = Tagclass ContactAdmin(admin.ModelAdmin):list_display = ('name','age', 'email') # listsearch_fields = ('name',)inlines = [TagInline]  # Inlinefieldsets = (['Main',{'fields':('name','email'),}],['Advance',{'classes': ('collapse',),'fields': ('age',),}])admin.site.register(Contact, ContactAdmin)
admin.site.register([Test])

在本实例中我们搜索了 name 为 runoob 的记录,显示结果如下:

Django ORM - 单表实例

阅读本章节前你需要先阅读了 Django 模型 进行基础配置及了解常见问题的解决方案。

接下来我们重新创建一个项目 app01(如果之前已创建过,忽略以下操作):

django-admin.py startproject app01

接下来在 settings.py 中找到 INSTALLED_APPS 这一项,如下:

INSTALLED_APPS = ('django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01',               # 添加此项
)

接下来,告诉 Django 使用 pymysql 模块连接 mysql 数据库:

# 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()

创建模型

在项目中的 models.py 中添加以下类:

class Book(models.Model):id = models.AutoField(primary_key=True) # id 会自动创建,可以手动写入title = models.CharField(max_length=32) # 书籍名称price = models.DecimalField(max_digits=5, decimal_places=2) # 书籍价格publish = models.CharField(max_length=32) # 出版社名称pub_date = models.DateField() # 出版时间

然后在命令行执行以下命令:

$ python3 manage.py migrate   # 创建表结构$ python3 manage.py makemigrations app01  # 让 Django 知道我们在我们的模型有一些变更
$ python3 manage.py migrate app01   # 创建表结构

常见报错信息

如果执行以上命令时会出现如下报错信息:

原因是 MySQLclient 目前只支持到 Python3.4,因此如果使用的更高版本的 python,需要修改如下:

通过报错信息的文件路径找到 ...site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql 这个路径里的 base.py 文件,把这两行代码注释掉(代码在文件开头部分):

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

一般点报错的代码文件路径信息,会自动跳转到报错文件中行数,此时我们在报错的代码行数注释掉。

这时数据库 runoob 就会创建一个 app01_book 的表。

接下来我们在app01 项目里添加 views.py 和 models.py 文件,app01 项目目录结构:

app01
|-- app01
|   |-- __init__.py
|   |-- __pycache__
|   |-- asgi.py
|   |-- migrations
|   |-- models.py
|   |-- settings.py
|   |-- urls.py
|   |-- views.py
|   `-- wsgi.py

数据库添加

规则配置:

from django.contrib import admin
from django.urls import path
from . import viewsurlpatterns = [path('add_book/', views.add_book),
]

方式一:模型类实例化对象

需从 app 目录引入 models.py 文件:

from app 目录 import models

并且实例化对象后要执行 对象.save() 才能在数据库中新增成功。

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):book = models.Book(title="菜鸟教程",price=300,publish="菜鸟出版社",pub_date="2008-8-8") book.save()return HttpResponse("<p>数据添加成功!</p>")

方式二:通过 ORM 提供的 objects 提供的方法 create 来实现(推荐)

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):books = models.Book.objects.create(title="如来神掌",price=200,publish="功夫出版社",pub_date="2010-10-10") print(books, type(books)) # Book object (18) return HttpResponse("<p>数据添加成功!</p>")

查找

使用 all() 方法来查询所有内容。

返回的是 QuerySet 类型数据,类似于 list,里面放的是一个个模型类的对象,可用索引下标取出模型类的对象。

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):books = models.Book.objects.all() print(books,type(books)) # QuerySet类型,类似于list,访问 url 时数据显示在命令行窗口中。return HttpResponse("<p>查找成功!</p>")

filter() 方法用于查询符合条件的数据。

返回的是 QuerySet 类型数据,类似于 list,里面放的是满足条件的模型类的对象,可用索引下标取出模型类的对象。

pk=3 的意思是主键 primary key=3,相当于 id=3。

因为 id 在 pycharm 里有特殊含义,是看内存地址的内置函数 id(),因此用 pk。

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):books = models.Book.objects.filter(pk=5)print(books)print("//")books = models.Book.objects.filter(publish='菜鸟出版社', price=300)print(books, type(books))  # QuerySet类型,类似于list。return HttpResponse("<p>查找成功!</p>")

exclude() 方法用于查询不符合条件的数据。

返回的是 QuerySet 类型数据,类似于 list,里面放的是不满足条件的模型类的对象,可用索引下标取出模型类的对象。

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):books = models.Book.objects.exclude(pk=5)print(books)print("//")books = models.Book.objects.exclude(publish='菜鸟出版社', price=300)print(books, type(books))  # QuerySet类型,类似于list。return HttpResponse("<p>查找成功!</p>")

get() 方法用于查询符合条件的返回模型类的对象符合条件的对象只能为一个,如果符合筛选条件的对象超过了一个或者没有一个都会抛出错误。

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):books = models.Book.objects.get(pk=5)books = models.Book.objects.get(pk=18)  # 报错,没有符合条件的对象books = models.Book.objects.get(price=200)  # 报错,符合条件的对象超过一个print(books, type(books))  # 模型类的对象return HttpResponse("<p>查找成功!</p>")

order_by() 方法用于对查询结果进行排序。

返回的是 QuerySet类型数据,类似于list,里面放的是排序后的模型类的对象,可用索引下标取出模型类的对象。

注意:

  • a、参数的字段名要加引号。

  • b、降序为在字段前面加个负号 -。

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):books = models.Book.objects.order_by("price") # 查询所有,按照价格升序排列 books = models.Book.objects.order_by("-price") # 查询所有,按照价格降序排列return HttpResponse("<p>查找成功!</p>")

reverse() 方法用于对查询结果进行反转。

返回的是 QuerySe t类型数据,类似于 list,里面放的是反转后的模型类的对象,可用索引下标取出模型类的对象。

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):# 按照价格升序排列:降序再反转books = models.Book.objects.order_by("-price").reverse()return HttpResponse("<p>查找成功!</p>")

count() 方法用于查询数据的数量返回的数据是整数。

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):books = models.Book.objects.count() # 查询所有数据的数量 books = models.Book.objects.filter(price=200).count() # 查询符合条件数据的数量return HttpResponse("<p>查找成功!</p>")

first() 方法返回第一条数据返回的数据是模型类的对象也可以用索引下标 [0]。

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):books = models.Book.objects.first() # 返回所有数据的第一条数据return HttpResponse("<p>查找成功!</p>")

last() 方法返回最后一条数据返回的数据是模型类的对象不能用索引下标 [-1],ORM 没有逆序索引。

from django.shortcuts import render,HttpResponse
from app01 import models 
def add_book(request):books = models.Book.objects.last() # 返回所有数据的最后一条数据return HttpResponse("<p>查找成功!</p>")

exists() 方法用于判断查询的结果 QuerySet 列表里是否有数据。

返回的数据类型是布尔,有为 true,没有为 false。

注意:判断的数据类型只能为 QuerySet 类型数据,不能为整型和模型类的对象。

from django.shortcuts import render,HttpResponse
from app01 import models
def add_book(request):books = models.Book.objects.exists()# 报错,判断的数据类型只能为QuerySet类型数据,不能为整型books = models.Book.objects.count().exists()# 报错,判断的数据类型只能为QuerySet类型数据,不能为模型类对象books = models.Book.objects.first().exists()  return HttpResponse("<p>查找成功!</p>")

values() 方法用于查询部分字段的数据。

返回的是 QuerySet 类型数据,类似于 list,里面不是模型类的对象,而是一个可迭代的字典序列,字典里的键是字段,值是数据。

注意:

  • 参数的字段名要加引号

  • 想要字段名和数据用 values

from django.shortcuts import render,HttpResponse
from app01 import models
def add_book(request):# 查询所有的id字段和price字段的数据books = models.Book.objects.values("pk","price")print(books[0]["price"],type(books)) # 得到的是第一条记录的price字段的数据return HttpResponse("<p>查找成功!</p>")

values_list() 方法用于查询部分字段的数据。

返回的是 QuerySet 类型数据,类似于 list,里面不是模型类的对象,而是一个个元组,元组里放的是查询字段对应的数据。

注意:

  • 参数的字段名要加引号

  • 只想要数据用 values_list

from django.shortcuts import render,HttpResponse
from app01 import models
def add_book(request):# 查询所有的price字段和publish字段的数据books = models.Book.objects.values_list("price","publish")print(books)print(books[0][0],type(books)) # 得到的是第一条记录的price字段的数据return HttpResponse("<p>查找成功!</p>")

distinct() 方法用于对数据进行去重。

返回的是 QuerySet 类型数据。

注意:

  • 对模型类的对象去重没有意义,因为每个对象都是一个不一样的存在。

  • distinct() 一般是联合 values 或者 values_list 使用。

from django.shortcuts import render,HttpResponse
from app01 import models
def add_book(request):# 查询一共有多少个出版社books = models.Book.objects.values_list("publish").distinct() # 对模型类的对象去重没有意义,因为每个对象都是一个不一样的存在。books = models.Book.objects.distinct()return HttpResponse("<p>查找成功!</p>")

filter() 方法基于双下划线的模糊查询(exclude 同理)。

注意:filter 中运算符号只能使用等于号 = ,不能使用大于号 > ,小于号 < ,等等其他符号。

__in 用于读取区间,= 号后面为列表 。

from django.shortcuts import render,HttpResponse
from app01 import models
def add_book(request):# 查询价格为200或者300的数据books = models.Book.objects.filter(price__in=[200,300])return HttpResponse("<p>查找成功!</p>")

__gt 大于号 ,= 号后面为数字。

# 查询价格大于200的数据 
books = models.Book.objects.filter(price__gt=200)

__gte

大于等于,= 号后面为数字。

# 查询价格大于等于200的数据 
books = models.Book.objects.filter(price__gte=200)

__lt 小于,=号后面为数字。

# 查询价格小于300的数据 
books=models.Book.objects.filter(price__lt=300)

__lte 小于等于,= 号后面为数字。

# 查询价格小于等于300的数据 
books=models.Book.objects.filter(price__lte=300)

__range 在 ... 之间,左闭右闭区间,= 号后面为两个元素的列表。

books=models.Book.objects.filter(price__range=[200,300])

__contains 包含,= 号后面为字符串。

books=models.Book.objects.filter(title__contains="菜")

__icontains 不区分大小写的包含,= 号后面为字符串。

books=models.Book.objects.filter(title__icontains="python") # 不区分大小写

__startswith 以指定字符开头,= 号后面为字符串。

books=models.Book.objects.filter(title__startswith="菜")

__endswith 以指定字符结尾,= 号后面为字符串。

books=models.Book.objects.filter(title__endswith="教程")

__year 是 DateField 数据类型的年份,= 号后面为数字。

books=models.Book.objects.filter(pub_date__year=2008) 

__month 是DateField 数据类型的月份,= 号后面为数字。

books=models.Book.objects.filter(pub_date__month=10) 

__day 是DateField 数据类型的天数,= 号后面为数字。

books=models.Book.objects.filter(pub_date__day=01)

删除

方式一:使用模型类的 对象.delete()。

返回值:元组,第一个元素为受影响的行数。

books=models.Book.objects.filter(pk=8).first().delete()

方式二:使用 QuerySet 类型数据.delete()(推荐)

返回值:元组,第一个元素为受影响的行数。

books=models.Book.objects.filter(pk__in=[1,2]).delete()

注意:

  • a. Django 删除数据时,会模仿 SQL约束 ON DELETE CASCADE 的行为,也就是删除一个对象时也会删除与它相关联的外键对象。

  • b. delete() 方法是 QuerySet 数据类型的方法,但并不适用于 Manager 本身。也就是想要删除所有数据,不能不写 all。

books=models.Book.objects.delete()  # 报错
books=models.Book.objects.all().delete()   # 删除成功

修改

方式一:

模型类的对象.属性 = 更改的属性值
模型类的对象.save()

返回值:编辑的模型类的对象。

books = models.Book.objects.filter(pk=7).first() 
books.price = 400 
books.save()

方式二:QuerySet 类型数据.update(字段名=更改的数据)(推荐)

返回值:整数,受影响的行数

from django.shortcuts import render,HttpResponse
from app01 import models
def add_book(request):books = models.Book.objects.filter(pk__in=[7,8]).update(price=888)return HttpResponse(books)

Django ORM – 多表实例

表与表之间的关系可分为以下三种:

  • 一对一: 一个人对应一个身份证号码,数据字段设置 unique。

  • 一对多: 一个家庭有多个人,一般通过外键来实现。

  • 多对多: 一个学生有多门课程,一个课程有很多学生,一般通过第三个表来实现关联。

创建模型

接下来我们来看下多表多实例。

 
class Book(models.Model):title = models.CharField(max_length=32)price = models.DecimalField(max_digits=5, decimal_places=2)pub_date = models.DateField()publish = models.ForeignKey("Publish", on_delete=models.CASCADE)authors = models.ManyToManyField("Author")class Publish(models.Model):name = models.CharField(max_length=32)city = models.CharField(max_length=64)email = models.EmailField()class Author(models.Model):name = models.CharField(max_length=32)age = models.SmallIntegerField()au_detail = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE)class AuthorDetail(models.Model):gender_choices = ((0, "女"),(1, "男"),(2, "保密"),)gender = models.SmallIntegerField(choices=gender_choices)tel = models.CharField(max_length=32)addr = models.CharField(max_length=64)birthday = models.DateField()

说明:

  • 1、EmailField 数据类型是邮箱格式,底层继承 CharField,进行了封装,相当于 MySQL 中的 varchar。

  • 2、Django1.1 版本不需要联级删除:on_delete=models.CASCADE,Django2.2 需要。

  • 3、一般不需要设置联级更新.

  • 4、外键在一对多的多中设置:models.ForeignKey("关联类名", on_delete=models.CASCADE)。

  • 5、OneToOneField = ForeignKey(...,unique=True)设置一对一。

  • 6、若有模型类存在外键,创建数据时,要先创建外键关联的模型类的数据,不然创建包含外键的模型类的数据时,外键的关联模型类的数据会找不到。

表结构

书籍表 Book:title 、 price 、 pub_date 、 publish(外键,多对一) 、 authors(多对多)

出版社表 Publish:name 、 city 、 email

作者表 Author:name 、 age 、 au_detail(一对一)

作者详情表 AuthorDetail:gender 、 tel 、 addr 、 birthday

以下是表格关联说明:

插入数据

我们在 MySQL 中执行以下 SQL 插入操作:

insert into app01_publish(name,city,email) values ("华山出版社", "华山", "hs@163.com"), ("明教出版社", "黑木崖", "mj@163.com")# 先插入 authordetail 表中多数据
insert into app01_authordetail(gender,tel,addr,birthday) values (1,13432335433,"华山","1994-5-23"), (1,13943454554,"黑木崖","1961-8-13"), (0,13878934322,"黑木崖","1996-5-20") # 再将数据插入 author,这样 author 才能找到 authordetail 
insert into app01_author(name,age,au_detail_id) values ("令狐冲",25,1), ("任我行",58,2), ("任盈盈",23,3)

ORM - 添加数据

一对多(外键 ForeignKey)

方式一: 传对象的形式,返回值的数据类型是对象,书籍对象。

步骤:

  • a. 获取出版社对象

  • b. 给书籍的出版社属性 pulish 传出版社对象

def add_book(request):#  获取出版社对象pub_obj = models.Publish.objects.filter(pk=1).first()#  给书籍的出版社属性publish传出版社对象book = models.Book.objects.create(title="菜鸟教程", price=200, pub_date="2010-10-10", publish=pub_obj)print(book, type(book))return HttpResponse(book)

方式二: 传对象 id 的形式(由于传过来的数据一般是 id,所以传对象 id 是常用的)。

一对多中,设置外键属性的类(多的表)中,MySQL 中显示的字段名是:外键属性名_id

返回值的数据类型是对象,书籍对象。

步骤:

  • a. 获取出版社对象的 id

  • b. 给书籍的关联出版社字段 pulish_id 传出版社对象的 id

def add_book(request):#  获取出版社对象pub_obj = models.Publish.objects.filter(pk=1).first()#  获取出版社对象的idpk = pub_obj.pk#  给书籍的关联出版社字段 publish_id 传出版社对象的idbook = models.Book.objects.create(title="冲灵剑法", price=100, pub_date="2004-04-04", publish_id=pk)print(book, type(book))return HttpResponse(book)

多对多(ManyToManyField):在第三张关系表中新增数据

方式一: 传对象形式,无返回值。

步骤:

  • a. 获取作者对象

  • b. 获取书籍对象

  • c. 给书籍对象的 authors 属性用 add 方法传作者对象

def add_book(request):#  获取作者对象chong = models.Author.objects.filter(name="令狐冲").first()ying = models.Author.objects.filter(name="任盈盈").first()#  获取书籍对象book = models.Book.objects.filter(title="菜鸟教程").first()#  给书籍对象的 authors 属性用 add 方法传作者对象book.authors.add(chong, ying)return HttpResponse(book)

方式二: 传对象id形式,无返回值。

步骤:

  • a. 获取作者对象的 id

  • b. 获取书籍对象

  • c. 给书籍对象的 authors 属性用 add 方法传作者对象的 id

def add_book(request):#  获取作者对象chong = models.Author.objects.filter(name="令狐冲").first()#  获取作者对象的idpk = chong.pk#  获取书籍对象book = models.Book.objects.filter(title="冲灵剑法").first()#  给书籍对象的 authors 属性用 add 方法传作者对象的idbook.authors.add(pk)

关联管理器(对象调用)

前提:

  • 多对多(双向均有关联管理器)

  • 一对多(只有多的那个类的对象有关联管理器,即反向才有)

语法格式:

正向:属性名
反向:小写类名加 _set

注意:一对多只能反向

常用方法:

add():用于多对多,把指定的模型对象添加到关联对象集(关系表)中。

注意:add() 在一对多(即外键)中,只能传对象( QuerySet数据类型),不能传 id([id表])。

*[ ] 的使用:

# 方式一:传对象

book_obj = models.Book.objects.get(id=10)
author_list = models.Author.objects.filter(id__gt=2)
book_obj.authors.add(*author_list)  # 将 id 大于2的作者对象添加到这本书的作者集合中
# 方式二:传对象 id
book_obj.authors.add(*[1,3]) # 将 id=1 和 id=3 的作者对象添加到这本书的作者集合中
return HttpResponse("ok")

反向:小写表名_set

ying = models.Author.objects.filter(name="任盈盈").first()
book = models.Book.objects.filter(title="冲灵剑法").first()
ying.book_set.add(book)
return HttpResponse("ok")

create():创建一个新的对象,并同时将它添加到关联对象集之中。

返回新创建的对象。

pub = models.Publish.objects.filter(name="明教出版社").first()
wo = models.Author.objects.filter(name="任我行").first()
book = wo.book_set.create(title="吸星大法", price=300, pub_date="1999-9-19", publish=pub)
print(book, type(book))
return HttpResponse("ok")

remove():从关联对象集中移除执行的模型对象。

对于 ForeignKey 对象,这个方法仅在 null=True(可以为空)时存在,无返回值。

author_obj =models.Author.objects.get(id=1)
book_obj = models.Book.objects.get(id=11)
author_obj.book_set.remove(book_obj)
return HttpResponse("ok")

clear():从关联对象集中移除一切对象,删除关联,不会删除对象。

对于 ForeignKey 对象,这个方法仅在 null=True(可以为空)时存在。

无返回值。

#  清空独孤九剑关联的所有作者
book = models.Book.objects.filter(title="菜鸟教程").first()
book.authors.clear()

ORM 查询

基于对象的跨表查询。

正向:属性名称
反向:小写类名_set

一对多

查询主键为 1 的书籍的出版社所在的城市(正向)。

book = models.Book.objects.filter(pk=10).first()
res = book.publish.city
print(res, type(res))
return HttpResponse("ok")

查询明教出版社出版的书籍名(反向)。

反向:对象.小写类名_set(pub.book_set) 可以跳转到关联的表(书籍表)。

pub.book_set.all():取出书籍表的所有书籍对象,在一个 QuerySet 里,遍历取出一个个书籍对象。

pub = models.Publish.objects.filter(name="明教出版社").first()
res = pub.book_set.all()
for i in res:print(i.title)
return HttpResponse("ok")

一对一

查询令狐冲的电话(正向)

正向:对象.属性 (author.au_detail) 可以跳转到关联的表(作者详情表)

author = models.Author.objects.filter(name="令狐冲").first()
res = author.au_detail.tel
print(res, type(res))
return HttpResponse("ok")

查询所有住址在黑木崖的作者的姓名(反向)。

一对一的反向,用 对象.小写类名 即可,不用加 _set。

反向:对象.小写类名(addr.author)可以跳转到关联的表(作者表)。

addr = models.AuthorDetail.objects.filter(addr="黑木崖").first()
res = addr.author.name
print(res, type(res))
return HttpResponse("ok")

多对多

菜鸟教程所有作者的名字以及手机号(正向)。

正向:对象.属性(book.authors)可以跳转到关联的表(作者表)。

作者表里没有作者电话,因此再次通过对象.属性(i.au_detail)跳转到关联的表(作者详情表)。

book = models.Book.objects.filter(title="菜鸟教程").first()
res = book.authors.all()
for i in res:print(i.name, i.au_detail.tel)
return HttpResponse("ok")

查询任我行出过的所有书籍的名字(反向)。

author = models.Author.objects.filter(name="任我行").first()
res = author.book_set.all()
for i in res:print(i.title)
return HttpResponse("ok")

基于双下划线的跨表查询

正向:属性名称跨表的属性名称 反向:小写类名跨表的属性名称

一对多

查询菜鸟出版社出版过的所有书籍的名字与价格。

res = models.Book.objects.filter(publish__name="菜鸟出版社").values_list("title", "price")

反向:通过 小写类名跨表的属性名称(booktitle,book__price) 跨表获取数据。

res = models.Publish.objects.filter(name="菜鸟出版社").values_list("book__title","book__price")
return HttpResponse("ok")

多对多

查询任我行出过的所有书籍的名字。

正向:通过 属性名称跨表的属性名称(authorsname) 跨表获取数据:

res = models.Book.objects.filter(authors__name="任我行").values_list("title")

反向:通过 小写类名跨表的属性名称(booktitle) 跨表获取数据:

res = models.Author.objects.filter(name="任我行").values_list("book__title")

一对一

查询任我行的手机号。

正向:通过 属性名称跨表的属性名称(au_detailtel) 跨表获取数据。

res = models.Author.objects.filter(name="任我行").values_list("au_detail__tel")

反向:通过 小写类名跨表的属性名称(authorname) 跨表获取数据。

res = models.AuthorDetail.objects.filter(author__name="任我行").values_list("tel")

Django ORM – 多表实例(聚合与分组查询)

聚合查询(aggregate)

聚合查询函数是对一组值执行计算,并返回单个值。

Django 使用聚合查询前要先从 django.db.models 引入 Avg、Max、Min、Count、Sum(首字母大写)。

from django.db.models import Avg,Max,Min,Count,Sum  #   引入函数

聚合查询返回值的数据类型是字典。

聚合函数 aggregate() 是 QuerySet 的一个终止子句, 生成的一个汇总值,相当于 count()。

使用 aggregate() 后,数据类型就变为字典,不能再使用 QuerySet 数据类型的一些 API 了。

日期数据类型(DateField)可以用 Max 和 Min。

返回的字典中:键的名称默认是(属性名称加上__聚合函数名),值是计算出来的聚合值。

如果要自定义返回字典的键的名称,可以起别名:

aggregate(别名 = 聚合函数名("属性名称"))

计算所有图书的平均价格:

from django.db.models import Avg,Max,Min,Count,Sum  #   引入函数
...
res = models.Book.objects.aggregate(Avg("price"))
print(res, type(res))
...

计算所有图书的数量、最贵价格和最便宜价格:

res=models.Book.objects.aggregate(c=Count("id"),max=Max("price"),min=Min("price"))
print(res,type(res)

分组查询(annotate)

分组查询一般会用到聚合函数,所以使用前要先从 django.db.models 引入 Avg,Max,Min,Count,Sum(首字母大写)。

from django.db.models import Avg,Max,Min,Count,Sum  #   引入函数

返回值:

  • 分组后,用 values 取值,则返回值是 QuerySet 数据类型里面为一个个字典;

  • 分组后,用 values_list 取值,则返回值是 QuerySet 数据类型里面为一个个元组。

MySQL 中的 limit 相当于 ORM 中的 QuerySet 数据类型的切片。

注意:

annotate 里面放聚合函数。

  • values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组。

  • values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名。

准备数据和创建模型

class Emp(models.Model):name = models.CharField(max_length=32)age = models.IntegerField()salary = models.DecimalField(max_digits=8, decimal_places=2)dep = models.CharField(max_length=32)province = models.CharField(max_length=32)class Emps(models.Model):name = models.CharField(max_length=32)age = models.IntegerField()salary =     models.DecimalField(max_digits=8, decimal_places=2)dep = models.ForeignKey("Dep", on_delete=models.CASCADE)province = models.CharField(max_length=32)
class Dep(models.Model):title = models.CharField(max_length=32)
INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('1', '令狐冲', '24', '6000.00', '销售部', '河南'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('2', '任盈盈', '18', '8000.00', '关公部', '广东'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('3', '任我行', '56', '10000.00', '销售部', '广东'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('4', '岳灵珊', '19', '6000.00', '关公部', '河南'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('5', '小龙女', '20', '8000.00', '关公部', '河北'); INSERT INTO `app01_dep` (`id`, `title`) VALUES ('1', '销售部'); 
INSERT INTO `app01_dep` (`id`, `title`) VALUES ('2', '关公部'); 
INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('2', '令狐冲', '24', '8000.00', '河南', '1'); 
INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('3', '任盈盈', '18', '9000.00', '广东', '2'); 
INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('4', '任我行', '57', '10000.00', '广东', '1');INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('5', '岳灵珊', '19', '6000.00', '河南', '2');INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('6', '小龙女', '20', '8000.00', '河北', '2');

统计每一个出版社的最便宜的书的价格:

res = models.Publish.objects.values("name").annotate(in_price = Min("book__price"))
print(res)

命令行中可以看到以下输出:

<QuerySet [{'name': '菜鸟出版社', 'in_price': Decimal('100.00')}, {'name': '明教出版社', 'in_price': Decimal('300.00')}]>

统计每一本书的作者个数:

res = models.Book.objects.annotate(c = Count("authors__name")).values("title","c")
print(res)

命令行中可以看到以下输出:

<QuerySet [{'title': '菜鸟教程', 'c': 1}, {'title': '吸星大法', 'c': 1}, {'title': '冲灵剑法', 'c': 1}]>

统计每一本以"菜"开头的书籍的作者个数:

res = models.Book.objects.filter(title__startswith="菜").annotate(c = Count("authors__name")).values("title","c")
print(res)

统计不止一个作者的图书名称:

res = models.Book.objects.annotate(c = Count("authors__name")).filter(c__gt=0).values("title","c")
print(res)

命令行中可以看到以下输出:

<QuerySet [{'title': '菜鸟教程', 'c': 1}, {'title': '吸星大法', 'c': 1}, {'title': '冲灵剑法', 'c': 1}]>

根据一本图书作者数量的多少对查询集 QuerySet 进行降序排序:

res = models.Book.objects.annotate(c = Count("authors__name")).order_by("-c").values("title","c")
print(res)

查询各个作者出的书的总价格:

res = models.Author.objects.annotate(all = Sum("book__price")).values("name","all")
print(res)

F() 查询

F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

之前构造的过滤器都只是将字段值与某个常量做比较,如果想要对两个字段的值做比较,就需要用到 F()。

使用前要先从 django.db.models 引入 F:

from django.db.models import F

用法:

F("字段名称")

F 动态获取对象字段的值,可以进行运算。

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

修改操作(update)也可以使用 F() 函数。

查询工资大于年龄的人:

from django.db.models import F
...
book=models.Emp.objects.filter(salary__gt=F("age")).values("name","age")
...

将每一本书的价格提高100元:

res = models.Book.objects.update(price=F("price")+100)
print(res)

Q() 查询

使用前要先从 django.db.models 引入 Q:

from django.db.models import Q

用法:

Q(条件判断)

例如:

Q(title__startswith="菜")

之前构造的过滤器里的多个条件的关系都是 and,如果需要执行更复杂的查询(例如 or 语句),就可以使用 Q 。

Q 对象可以使用 & | ~ (与 或 非)操作符进行组合。

优先级从高到低:~ & |。

可以混合使用 Q 对象和关键字参数,Q 对象和关键字参数是用"and"拼在一起的(即将逗号看成 and ),但是 Q 对象必须位于所有关键字参数的前面。

查询价格大于 350 或者名称以菜开头的书籍的名称和价格。

...
res=models.Book.objects.filter(Q(price__gt=350)|Q(title__startswith="菜")).values("title","price")
print(res)
...

查询以"菜"结尾或者不是 2010 年 10 月份的书籍:

res = models.Book.objects.filter(Q(title__endswith="菜") | ~Q(Q(pub_date__year=2010) & Q(pub_date__month=10)))
print(res)  

查询出版日期是 2004 或者 1999 年,并且书名中包含有"菜"的书籍。

Q 对象和关键字混合使用,Q 对象要在所有关键字的前面:

res = models.Book.objects.filter(Q(pub_date__year=2004) | Q(pub_date__year=1999), title__contains="菜")
print(res)  

Django Form 组件

Django Form 组件用于对页面进行初始化,生成 HTML 标签,此外还可以对用户提交对数据进行校验(显示错误信息)。

报错信息显示顺序:

  • 先显示字段属性中的错误信息,然后再显示局部钩子的错误信息。

  • 若显示了字段属性的错误信息,就不会显示局部钩子的错误信息。

  • 若有全局钩子,则全局钩子是等所有的数据都校验完,才开始进行校验,并且全局钩子的错误信息一定会显示。

使用 Form 组件,需要先导入 forms:

from django import forms

接下来我们在 app01 目录下创建一个 My_forms.py:

from django import forms
from django.core.exceptions import ValidationError
from app01 import modelsclass EmpForm(forms.Form):name = forms.CharField(min_length=4, label="姓名", error_messages={"min_length": "你太短了", "required": "该字段不能为空!"})age = forms.IntegerField(label="年龄")salary = forms.DecimalField(label="工资")

字段属性:

  • label:输入框前面的文本信息。

  • error_message:自定义显示的错误信息,属性值是字典, 其中 required 为设置不能为空时显示的错误信息的 key。

from django.shortcuts import render, HttpResponse
from app01.My_Forms import EmpForm
from app01 import models
from django.core.exceptions import ValidationError
# Create your views here.def add_emp(request):if request.method == "GET":form = EmpForm()return render(request, "add_emp.html", {"form": form})else:form = EmpForm(request.POST)if form.is_valid():  # 进行数据校验# 校验成功data = form.cleaned_data  # 校验成功的值,会放在cleaned_data里。data.pop('r_salary')print(data)models.Emp.objects.create(**data)return HttpResponse('ok')# return render(request, "add_emp.html", {"form": form})else:print(form.errors)    # 打印错误信息clean_errors = form.errors.get("__all__")print(222, clean_errors)return render(request, "add_emp.html", {"form": form, "clean_errors": clean_errors})

app01/urls.py 文件添加以下规则:

path('add_emp/', views.add_emp)

HTML 模版:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title></title>
</head>
<body><h3>添加员工</h3>{#1、自己手动写HTML页面#}
<form action="" method="post"><p>姓名:<input type="text" name="name"></p><p>年龄:<input type="text" name="age"></p><p>工资:<input type="text" name="salary"></p><input type="submit">
</form>{#2、通过form对象的as_p方法实现#}
{#<form action="" method="post" novalidate>#}
{#    {% csrf_token %}#}
{#    {{ form.as_p }}#}
{#    <input type="submit">#}
{#</form>#}{#3、手动获取form对象的字段#}
{#<form action="" method="post" novalidate>#}
{#    {% csrf_token %}#}
{#    <div>#}
{#        <label for="id_{{ form.name.name }}">姓名</label>#}
{#        {{ form.name }} <span>{{ form.name.errors.0 }}</span>#}
{#    </div>#}
{#    <div>#}
{#        <label for="id_{{ form.age.name }}">年龄</label>#}
{#        {{ form.age }} <span>{{ form.age.errors.0 }}</span>#}
{#    </div>#}
{#    <div>#}
{#        <label for="id_salary">工资</label>#}
{#        {{ form.salary }} <span>{{ form.salary.errors.0 }}</span>#}
{#    </div>#}
{#    <input type="submit">#}
{#</form>#}{#4、用for循环展示所有字段#}
{#<form action="" method="post" novalidate>#}
{#    {% csrf_token %}#}
{#    {% for field in form %}#}
{#        <div>#}
{#            <label for="id_{{ field.name }}">{{ field.label }}</label>#}
{#            {{ field }} <span>{{ field.errors.0 }}</span>#}
{#        </div>#}
{#    {% endfor %}#}
{#    <input type="submit">#}
{#</form>#}</body>
</html>

局部钩子和全局钩子

定义 Form 类:

from django import forms
from django.core.exceptions import ValidationError
from app01 import models
class EmpForm(forms.Form):name = forms.CharField(min_length=5, label="姓名", error_messages={"required": "该字段不能为空!","min_length": "用户名太短。"})age = forms.IntegerField(label="年龄")salary = forms.DecimalField(max_digits=5, decimal_places=2, label="工资")r_salary = forms.DecimalField(max_digits=5, decimal_places=2, label="请再输入工资")def clean_name(self):  # 局部钩子val = self.cleaned_data.get("name")if val.isdigit():raise ValidationError("用户名不能是纯数字")elif models.Emp.objects.filter(name=val):raise ValidationError("用户名已存在!")else:return valdef clean(self):  # 全局钩子 确认两次输入的工资是否一致。val = self.cleaned_data.get("salary")r_val = self.cleaned_data.get("r_salary")if val == r_val:return self.cleaned_dataelse:raise ValidationError("请确认工资是否一致。")

views.py 文件代码:

def add_emp(request):if request.method == "GET":form = EmpForm()  # 初始化form对象return render(request, "add_emp.html", {"form":form})else:form = EmpForm(request.POST)  # 将数据传给form对象if form.is_valid():  # 进行校验data = form.cleaned_datadata.pop("r_salary")models.Emp.objects.create(**data)return redirect("/index/")else:  # 校验失败clear_errors = form.errors.get("__all__")  # 获取全局钩子错误信息return render(request, "add_emp.html", {"form": form, "clear_errors": clear_errors})

模板文件代码如下:

<form action="" method="post" novalidate>{% csrf_token %}<div><label for="id_{{ form.name.name }}">姓名</label>{{ form.name }} <span>{{ form.name.errors.0 }}</span></div><div><label for="id_{{ form.age.name }}">年龄</label>{{ form.age }} <span>{{ form.age.errors.0 }}</span></div><div><label for="id_salary">工资</label>{{ form.salary }} <span>{{ form.salary.errors.0 }}{{ clear_errors.0 }}</span></div><div><label for="id_r_salary">请再输入工资</label>{{ form.r_salary }} <span>{{ form.r_salary.errors.0 }}{{ clear_errors.0 }}</span></div><input type="submit">
</form>

Django 用户认证(Auth)组件

Django 用户认证(Auth)组件一般用在用户的登录注册上,用于判断当前的用户是否合法,并跳转到登陆成功或失败页面。

Django 用户认证(Auth)组件需要导入 auth 模块:

# 认证模块
from django.contrib import auth# 对应数据库
from django.contrib.auth.models import User

返回值是用户对象。

创建用户对象的三种方法:

  • create():创建一个普通用户,密码是明文的。

  • create_user():创建一个普通用户,密码是密文的。

  • create_superuser():创建一个超级用户,密码是密文的,要多传一个邮箱 email 参数。

参数:

  • username: 用户名。

  • password:密码。

  • email:邮箱 (create_superuser 方法要多加一个 email)。

from django.contrib.auth.models import User 
User.objects.create(username='runboo',password='123')

from django.contrib.auth.models import User 
User.objects.create_user(username='runbooo',password='123')

from django.contrib.auth.models import User User.objects.create_superuser(username='runboooo',password='123',email='runboo@163.com')

验证用户的用户名和密码使用 authenticate() 方法,从需要 auth_user 表中过滤出用户对象。

使用前要导入:

from django.contrib import auth

参数:

  • username:用户名

  • password:密码

返回值:如果验证成功,就返回用户对象,反之,返回 None。

def login(request):if request.method == "GET":return render(request, "login.html")username = request.POST.get("username")password = request.POST.get("pwd")valid_num = request.POST.get("valid_num")keep_str = request.session.get("keep_str")if keep_str.upper() == valid_num.upper():user_obj = auth.authenticate(username=username, password=password)print(user_obj.username)

给验证成功的用户加 session,将 request.user 赋值为用户对象。

登陆使用 login() 方法。

使用前要导入:

from django.contrib import auth

参数:

  • request:用户对象

返回值:None

def login(request):if request.method == "GET":return render(request, "login.html")username = request.POST.get("username")password = request.POST.get("pwd")valid_num = request.POST.get("valid_num")keep_str = request.session.get("keep_str")if keep_str.upper() == valid_num.upper():user_obj = auth.authenticate(username=username, password=password)print(user_obj.username)if not user_obj:return redirect("/login/")else:
​auth.login(request, user_obj)path = request.GET.get("next") or "/index/"print(path)return redirect(path)else:return redirect("/login/")

注销用户使用 logout() 方法,需要清空 session 信息,将 request.user 赋值为匿名用户。

使用前要导入:

from django.contrib import auth

参数:

  • request:用户对象

返回值:None

def logout(request):ppp = auth.logout(request)print(ppp) # Nonereturn redirect("/login/")

设置装饰器,给需要登录成功后才能访问的页面统一加装饰器。

使用前要导入:

from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import login_required @login_required
def index(request):return HttpResponse("index页面。。。")

设置从哪个页面访问,登录成功后就返回哪个页面。

解析:

django 在用户访问页面时,如果用户是未登录的状态,就给用户返回登录页面。

此时,该登录页面的 URL 后面有参数:next=用户访问的页面的 URL。

因此,设置在用户登录成功后重定向的 URL 为 next 参数的值。

但是,若用户一开始就输入登录页面 logi,request.GET.get("next") 就取不到值,所以在后面加 or,可以设置自定义返回的页面。

# 如果直接输入 login、get() 就取不到值,path 可以自定义设置返回的页面
path = request.GET.get("next") or "/index/"
return redirect(path)

Django cookie 与 session

Cookie 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。

识别返回用户包括三个步骤:

  • 服务器脚本向浏览器发送一组 Cookie。例如:姓名、年龄或识别号码等。

  • 浏览器将这些信息存储在本地计算机上,以备将来使用。

  • 当下一次浏览器向 Web 服务器发送任何请求时,浏览器会把这些 Cookie 信息发送到服务器,服务器将使用这些信息来识别用户。

HTTP 是一种"无状态"协议,这意味着每次客户端检索网页时,客户端打开一个单独的连接到 Web 服务器,服务器会自动不保留之前客户端请求的任何记录。

但是仍然有以下三种方式来维持 Web 客户端和 Web 服务器之间的 session 会话:

Cookies

一个 Web 服务器可以分配一个唯一的 session 会话 ID 作为每个 Web 客户端的 cookie,对于客户端的后续请求可以使用接收到的 cookie 来识别。

在Web开发中,使用 session 来完成会话跟踪,session 底层依赖 Cookie 技术。

Django 中 Cookie 的语法

设置 cookie:

rep.set_cookie(key,value,...) 
rep.set_signed_cookie(key,value,salt='加密盐',...)

获取 cookie:

request.COOKIES.get(key)

删除 cookie:

rep =HttpResponse || render || redirect 
rep.delete_cookie(key)

class UserInfo(models.Model):username = models.CharField(max_length=32)password = models.CharField(max_length=64)

from django.contrib import admin
from django.urls import path
from cookie import views
urlpatterns = [path('admin/', admin.site.urls),path('login/', views.login),path('index/', views.index),path('logout/', views.logout),path('order/', views.logout)

def login(request):if request.method == "GET":return render(request, "login.html")username = request.POST.get("username")password = request.POST.get("pwd")
​user_obj = models.UserInfo.objects.filter(username=username, password=password).first()print(user_obj.username)
​if not user_obj:return redirect("/login/")else:rep = redirect("/index/")rep.set_cookie("is_login", True)return repdef index(request):print(request.COOKIES.get('is_login'))status = request.COOKIES.get('is_login') # 收到浏览器的再次请求,判断浏览器携带的cookie是不是登录成功的时候响应的 cookieif not status:return redirect('/login/')return render(request, "index.html")
​
​
def logout(request):rep = redirect('/login/')rep.delete_cookie("is_login")return rep # 点击注销后执行,删除cookie,不再保存用户状态,并弹到登录页面def order(request):print(request.COOKIES.get('is_login'))status = request.COOKIES.get('is_login')if not status:return redirect('/login/')return render(request, "order.html")

以下创建三个模板文件:login.html、index.html、order.html。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h3>用户登录</h3>
<form action="" method="post">{% csrf_token %}<p>用户名: <input type="text" name="username"></p><p>密码: <input type="password" name="pwd"></p><input type="submit">
</form></body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h2>index 页面。。。</h2><a href="/logout/">注销</a></body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h2>order 页面。。。</h2><a href="/logout/">注销</a></body>
</html>

Session(保存在服务端的键值对)

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

工作原理

  • a. 浏览器第一次请求获取登录页面 login。

  • b. 浏览器输入账号密码第二次请求,若输入正确,服务器响应浏览器一个 index 页面和一个键为 sessionid,值为随机字符串的 cookie,即 set_cookie ("sessionid",随机字符串)。

  • c. 服务器内部在 django.session 表中记录一条数据。

    django.session 表中有三个字段。

    • session_key:存的是随机字符串,即响应给浏览器的 cookie 的 sessionid 键对应的值。

    • session_data:存的是用户的信息,即多个 request.session["key"]=value,且是密文。

    • expire_date:存的是该条记录的过期时间(默认14天)

  • d. 浏览器第三次请求其他资源时,携带 cookie :{sessionid:随机字符串},服务器从 django.session 表中根据该随机字符串取出该用户的数据,供其使用(即保存状态)。

注意: django.session 表中保存的是浏览器的信息,而不是每一个用户的信息。 因此, 同一浏览器多个用户请求只保存一条记录(后面覆盖前面),多个浏览器请求才保存多条记录。

cookie 弥补了 http 无状态的不足,让服务器知道来的人是"谁",但是 cookie 以文本的形式保存在浏览器端,安全性较差,且最大只支持 4096 字节,所以只通过 cookie 识别不同的用户,然后,在对应的 session 里保存私密的信息以及超过 4096 字节的文本。

session 设置:

equest.session["key"] = value

执行步骤:

  • a. 生成随机字符串

  • b. 把随机字符串和设置的键值对保存到 django_session 表的 session_key 和 session_data 里

  • c. 设置 cookie:set_cookie("sessionid",随机字符串) 响应给浏览器

session 获取:

request.session.get('key')

执行步骤:

  • a. 从 cookie 中获取 sessionid 键的值,即随机字符串。

  • b. 根据随机字符串从 django_session 表过滤出记录。

  • c. 取出 session_data 字段的数据。

session 删除,删除整条记录(包括 session_key、session_data、expire_date 三个字段):

request.session.flush()

删除 session_data 里的其中一组键值对:

del request.session["key"]

执行步骤:

  • a. 从 cookie 中获取 sessionid 键的值,即随机字符串

  • b. 根据随机字符串从 django_session 表过滤出记录

  • c. 删除过滤出来的记录

from session import views as session_views
​
urlpatterns = [path('session_login/', session_views.login),path('s_index/', session_views.s_index),path('s_logout/', session_views.s_logout),
]

def login(request):if request.method == "GET":return render(request, "login.html")username = request.POST.get("username")password = request.POST.get("pwd")
​user_obj = models.UserInfo.objects.filter(username=username, password=password).first()print(user_obj.username)
​if not user_obj:return redirect("/session_login/")else:request.session['is_login'] = Truerequest.session['user1'] = usernamereturn redirect("/s_index/")
​
​
def s_index(request):status = request.session.get('is_login')if not status:return redirect('/session_login/')return render(request, "s_index.html")
​
​
def s_logout(request):# del request.session["is_login"] # 删除session_data里的一组键值对request.session.flush() # 删除一条记录包括(session_key session_data expire_date)三个字段return redirect('/session_login/')
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h2>session_index 页面。。。{{ request.session.user1 }}</h2>
<a href="/s_logout/">注销</a>
</body>
</html>

Django 中间件

Django 中间件是修改 Django request 或者 response 对象的钩子,可以理解为是介于 HttpRequest 与 HttpResponse 处理之间的一道处理过程。

浏览器从请求到响应的过程中,Django 需要通过很多中间件来处理,可以看如下图所示:

Django 中间件作用:

  • 修改请求,即传送到 view 中的 HttpRequest 对象。

  • 修改响应,即 view 返回的 HttpResponse 对象。

中间件组件配置在 settings.py 文件的 MIDDLEWARE 选项列表中。

配置中的每个字符串选项都是一个类,也就是一个中间件。

Django 默认的中间件配置:

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

自定义中间件

中间件可以定义四个方法,分别是:

process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_exception(self, request, exception)
process_response(self, request, response)

自定义中间的步骤:

在 app 目录下新建一个 py 文件,名字自定义,并在该 py 文件中导入 MiddlewareMixin:

自定义的中间件类,要继承父类 MiddlewareMixin:

class MD1(MiddlewareMixin): pass

在 settings.py 中的 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','app01.middlewares.MD1',
]

自定义中间件类的方法

自定义中间件类的方法有:process_request 和 process_response。

process_request 方法

process_request 方法有一个参数 request,这个 request 和视图函数中的 request 是一样的。

process_request 方法的返回值可以是 None 也可以是 HttpResponse 对象。

  • 返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。

  • 返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。

process_request 方法是在视图函数之前执行的。

当配置多个中间件时,会按照 MIDDLEWARE中 的注册顺序,也就是列表的索引值,顺序执行。

不同中间件之间传递的 request 参数都是同一个请求对象。

from django.utils.deprecation import MiddlewareMixin
​
from django.shortcuts import render, HttpResponse
​
class MD1(MiddlewareMixin):def process_request(self, request):print("md1  process_request 方法。", id(request)) #在视图之前执行

process_response

process_response 方法有两个参数,一个是 request,一个是 response,request 是请求对象,response 是视图函数返回的 HttpResponse 对象,该方法必须要有返回值,且必须是response。

process_response 方法是在视图函数之后执行的。

当配置多个中间件时,会按照 MIDDLEWARE 中的注册顺序,也就是列表的索引值,倒序执行。

class MD1(MiddlewareMixin):def process_request(self, request):print("md1  process_request 方法。", id(request)) #在视图之前执行
​
​def process_response(self,request, response): :#基于请求响应print("md1  process_response 方法!", id(request)) #在视图之后return response

从下图看,正常的情况下按照绿色的路线进行执行,假设中间件1有返回值,则按照红色的路线走,直接执行该类下的 process_response 方法返回,后面的其他中间件就不会执行。

process_view

process_view 方法格式如下:

process_view(request, view_func, view_args, view_kwargs)

process_view 方法有四个参数:

  • request 是 HttpRequest 对象。

  • view_func 是 Django 即将使用的视图函数。

  • view_args 是将传递给视图的位置参数的列表。

  • view_kwargs 是将传递给视图的关键字参数的字典。

view_args 和 view_kwargs 都不包含第一个视图参数(request)。

process_view 方法是在视图函数之前,process_request 方法之后执行的。

返回值可以是 None、view_func(request) 或 HttpResponse 对象。

  • 返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。

  • 返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。

  • c.返回值是 view_func(request),Django 将不执行后续视图函数之前执行的方法,提前执行视图函数,然后再倒序执行视图函数之后执行的方法。

  • 当最后一个中间件的 process_request 到达路由关系映射之后,返回到第一个中间件 process_view,然后依次往下,到达视图函数。

class MD1(MiddlewareMixin):def process_request(self, request):print("md1  process_request 方法。", id(request)) #在视图之前执行
​
​def process_response(self,request, response): :#基于请求响应print("md1  process_response 方法!", id(request)) #在视图之后return response
​
​def process_view(self,request, view_func, view_args, view_kwargs):print("md1  process_view 方法!") #在视图之前执行 顺序执行#return view_func(request)

process_exception

process_exception 方法如下:

process_exception(request, exception)

参数说明:

  • request 是 HttpRequest 对象。

  • exception 是视图函数异常产生的 Exception 对象。

process_exception 方法只有在视图函数中出现异常了才执行,按照 settings 的注册倒序执行。

在视图函数之后,在 process_response 方法之前执行。

process_exception 方法的返回值可以是一个 None 也可以是一个 HttpResponse 对象。

返回值是 None,页面会报 500 状态码错误,视图函数不会执行。

process_exception 方法倒序执行,然后再倒序执行 process_response 方法。

返回值是 HttpResponse 对象,页面不会报错,返回状态码为 200。

视图函数不执行,该中间件后续的 process_exception 方法也不执行,直接从最后一个中间件的 process_response 方法倒序开始执行。

若是 process_view 方法返回视图函数,提前执行了视图函数,且视图函数报错,则无论 process_exception 方法的返回值是什么,页面都会报错, 且视图函数和 process_exception 方法都不执行。

直接从最后一个中间件的 process_response 方法开始倒序执行:

class MD1(MiddlewareMixin):def process_request(self, request):print("md1  process_request 方法。", id(request)) #在视图之前执行
​def process_response(self,request, response): :#基于请求响应print("md1  process_response 方法!", id(request)) #在视图之后return response
​def process_view(self,request, view_func, view_args, view_kwargs):print("md1  process_view 方法!") #在视图之前执行 顺序执行#return view_func(request)
​
​def process_exception(self, request, exception):#引发错误 才会触发这个方法print("md1  process_exception 方法!")# return HttpResponse(exception) #返回错误信息

Django 视图 - FBV 与 CBV

FBV(function base views) 基于函数的视图,就是在视图里使用函数处理请求。

CBV(class base views) 基于类的视图,就是在视图里使用类处理请求。

FBV

基于函数的视图其实我们前面章节一直在使用,就是使用了函数来处理用户的请求,查看以下实例:

路由配置:

urlpatterns = [path("login/", views.login),
]
from django.shortcuts import render,HttpResponse
​
def login(request):if request.method == "GET":return HttpResponse("GET 方法")if request.method == "POST":user = request.POST.get("user")pwd = request.POST.get("pwd")if user == "runoob" and pwd == "123456":return HttpResponse("POST 方法")else:return HttpResponse("POST 方法1")

如果我们在浏览器中直接访问 http://127.0.0.1:8000/login/ ,输出结果为:

GET 方法

CBV

基于类的视图,就是使用了类来处理用户的请求,不同的请求我们可以在类中使用不同方法来处理,这样大大的提高了代码的可读性。

定义的类要继承父类 View,所以需要先引入库:

from django.views import View

执行对应请求的方法前会优先执行 dispatch 方法(在get/post/put...方法前执行),dispatch() 方法会根据请求的不同调用相应的方法来处理。

其实,在我们前面学到的知识都知道 Django 的 url 是将一个请求分配给可调用的函数的,而不是一个类,那是如何实现基于类的视图的呢? 主要还是通过父类 View 提供的一个静态方法 as_view() ,as_view 方法是基于类的外部接口, 他返回一个视图函数,调用后请求会传递给 dispatch 方法,dispatch 方法再根据不同请求来处理不同的方法。

路由配置:

urlpatterns = [path("login/", views.Login.as_view()),
]
from django.shortcuts import render,HttpResponse
from django.views import View
​
class Login(View):def get(self,request):return HttpResponse("GET 方法")
​def post(self,request):user = request.POST.get("user")pwd = request.POST.get("pwd")if user == "runoob" and pwd == "123456":return HttpResponse("POST 方法")else:return HttpResponse("POST 方法 1")

如果我们在浏览器中直接访问 http://127.0.0.1:8000/login/ ,输出结果为:

GET 方法

Django Nginx+uwsgi 安装配置

在前面的章节中我们使用 python manage.py runserver 来运行服务器。这只适用测试环境中使用。

正式发布的服务,我们需要一个可以稳定而持续的服务器,比如apache, Nginx, lighttpd等,本文将以 Nginx 为例。

你也可以直接参考:Python uwsgi 安装配置

安装基础开发包

Centos 下安装步骤如下:

yum groupinstall "Development tools"
yum install zlib-devel bzip2-devel pcre-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel

CentOS 自带 Python 2.4.3,但我们可以再安装Python2.7.5:

cd ~
wget http://python.org/ftp/python/2.7.5/Python-2.7.5.tar.bz2
tar xvf Python-2.7.5.tar.bz2
cd Python-2.7.5
./configure --prefix=/usr/local
make && make altinstall

安装Python包管理

easy_install 包 https://pypi.python.org/pypi/distribute

安装步骤:

cd ~
wget https://pypi.python.org/packages/source/d/distribute/distribute-0.6.49.tar.gz
tar xf distribute-0.6.49.tar.gz
cd distribute-0.6.49
python2.7 setup.py install
easy_install --version

pip 包: https://pypi.python.org/pypi/pip

安装 pip 的好处是可以用 pip list、pip uninstall 管理 Python 包, easy_install 没有这个功能,只有 uninstall。

安装 uwsgi

uwsgi:https://pypi.python.org/pypi/uWSGI

uwsgi 参数详解:http://uwsgi-docs.readthedocs.org/en/latest/Options.html

pip install uwsgi
uwsgi --version    # 查看 uwsgi 版本

测试 uwsgi 是否正常:

新建 test.py 文件,内容如下:

def application(env, start_response):start_response('200 OK', [('Content-Type','text/html')])return "Hello World"

然后在终端运行:

uwsgi --http :8001 --wsgi-file test.py

在浏览器内输入:http://127.0.0.1:8001,查看是否有"Hello World"输出,若没有输出,请检查你的安装过程。


安装 Django

pip install django

测试 django 是否正常,运行:

django-admin.py startproject demosite
cd demosite
python2.7 manage.py runserver 0.0.0.0:8002

在浏览器内输入:http://127.0.0.1:8002,检查django是否运行正常。

安装 Nginx

安装命令如下:

cd ~
wget http://nginx.org/download/nginx-1.5.6.tar.gz
tar xf nginx-1.5.6.tar.gz
cd nginx-1.5.6
./configure --prefix=/usr/local/nginx-1.5.6 \
--with-http_stub_status_module \
--with-http_gzip_static_module
make && make install

uwsgi 配置

uwsgi支持ini、xml等多种配置方式,本文以 ini 为例, 在/etc/目录下新建uwsgi9090.ini,添加如下配置:

[uwsgi]
socket = 127.0.0.1:9090
master = true         //主进程
vhost = true          //多站模式
no-site = true        //多站模式时不设置入口模块和文件
workers = 2           //子进程数
reload-mercy = 10     
vacuum = true         //退出、重启时清理文件
max-requests = 1000   
limit-as = 512
buffer-size = 30000
pidfile = /var/run/uwsgi9090.pid    //pid文件,用于下面的脚本启动、停止该进程
daemonize = /website/uwsgi9090.log

Nginx 配置

找到nginx的安装目录(如:/usr/local/nginx/),打开conf/nginx.conf文件,修改server配置:

server {listen       80;server_name  localhost;location / {            include  uwsgi_params;uwsgi_pass  127.0.0.1:9090;              //必须和uwsgi中的设置一致uwsgi_param UWSGI_SCRIPT demosite.wsgi;  //入口文件,即wsgi.py相对于项目根目录的位置,“.”相当于一层目录uwsgi_param UWSGI_CHDIR /demosite;       //项目根目录index  index.html index.htm;client_max_body_size 35m;}}

你可以阅读 Nginx 安装配置 了解更多内容。

设置完成后,在终端运行:

uwsgi --ini /etc/uwsgi9090.ini &
/usr/local/nginx/sbin/nginx

在浏览器输入:http://127.0.0.1,你就可以看到 django 的 "It work" 了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.exyb.cn/news/show-35398.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

UDP和TCP的特点和区别

1、UDP 和 TCP 的特点与区别 用户数据报协议 UDP&#xff08;User Datagram Protocol&#xff09; 是无连接的&#xff0c;尽最大可能交付&#xff0c;没有拥塞控制&#xff0c;面向报文&#xff08;对于应用程序传下来的报文不合并也不拆分&#xff0c;只是添加 UDP 首部&…...

JS获取当前日期、当前月第一天、当前月最后一天

js获取当前时间YYYY-MM-DD HH:MM:SS getNowTime: function () {let yy new Date().getFullYear()let mm new Date().getMonth() 1let dd new Date().getDate()let hh new Date().getHours()let mf new Date().getMinutes() < 10 ? 0 new Date().getMinutes():new D…...

Chrom 插件下载网址

https://chrome.zzzmh.cn/#index...

CRM中销售周期的各个阶段以及销售管理

什么是销售周期? 销售周期是指销售人员为转化新客户而采取行动的所有时间阶段。销售周期经常与销售方法混淆。销售方法&#xff0c;通常为提高销售效率或成交率而设计&#xff0c;例如以客户为中心销售法。而销售周期则更具战略性&#xff0c;通常包括“挖掘”&#xff0c;“…...

MybatisPlus零基础到入门(一)

MybatisPlus概述 1、 学习基本说明 版本&#xff1a;SpringBoot2.5.1、Mybatis-Plus3.0.5 工具&#xff1a;IntelliJ IDEA 2018.1.4、Navicat Premium 12 数据库&#xff1a;mysql-8.0.23 2、 简介 MyBatisPlus是什么&#xff1f; 3、 特性 • 无侵入&#xff1a;只做增…...

.Net Core 5 查看当前CPU 占用比例

class Program{public static async Task Main(string[] args){var task Task.Run(() > ConsumeCPU(50));while (true){await Task.Delay(2000);var cpuUsage await GetCpuUsageForProcess();Console.WriteLine(cpuUsage);}}public static void ConsumeCPU(int percentag...

以太坊基础入门

以太坊特点 • 以太坊是“世界计算机”&#xff0c;这代表它是一个开源的、全球分布的计算 基础设施 • 执行称为智能合约&#xff08;smart contract&#xff09;的程序 • 使用区块链来同步和存储系统状态以及名为以太币&#xff08;ether&#xff09;的加密 货币&#xff0…...

宇言(三):我的处事原则

这里主要讲几点我的处事原则&#xff0c;希望自己在几乎任何时刻都能做到。 1、己所不欲&#xff0c;勿施于人。或者说换位思考。 2、三思而后行。&#xff08;1、说话做事&#xff0c;考虑清楚各种可能存在或发生的情况。 2、喷人前&#xff0c;先把对方当高手&#xff0c;确…...

【报告分享】2021中国住宿业市场网络口碑报告-中国饭店协会众荟(附下载)

摘要:通过语义分析进一步了解酒店住宿业在不同服务维度的表现与变化趋势。点评是消费者对酒店服务最真实的反馈&#xff0c;不同服务的观点数&#xff0c;一方面反映消费者对该项服务的关注度&#xff0c;另一方面也反映酒店在营销中的 “有形展示” 是否做得到位&#xff0c;酒…...

2.1常量、变量、整型、实型和字符型

C语言的数据类型 常见数据类型所占内存的大小 数据类型32位操作系统(字节)64位操作系统(字节)char11short(unsigned short)22int(unsigned int)44float44double88long4\color{red}{4}48\color{red}{8}8long long88 常见数据类型的取值范围 数据类型最小值最大值所占字节char-…...

概率论实验报告01- | 使用Matlab产生随机数

一、实验目的 1.了解随机数的产生方法&#xff1b; 2.了解常用随机数的概率分布函数、分布律和概率密度函数。 二、实验原理 随机数的产生有好多方法&#xff0c;可以利用乘积法和同余法产生[0,1]之间的均匀分布&#xff0c;然后利用函数变换法产生所需不同分布的随机数。可以…...

网络协议系列四 - 路由/名词解释

在不同网段之间转发数据&#xff0c;需要有路由器的支持。 一、路由 默认情况下&#xff0c;路由器只知道跟它直连的网段&#xff0c;非直连的网段需要通过静态路由、动态路由告诉它。 1.1. 静态路由 管理员手动添加路由信息适用于小规模网络 类型C代表直连&#xff08;conn…...

Jmeter工具使用-分布式架构和服务器性能监控解决方案

Jmeter工具使用-分布式架构和服务器性能监控解决方案参考文章&#xff1a; &#xff08;1&#xff09;Jmeter工具使用-分布式架构和服务器性能监控解决方案 &#xff08;2&#xff09;https://www.cnblogs.com/zhengshuheng/p/6600215.html 备忘一下。...

SpringBoot - MyBatis-Plus使用详解(一)

1&#xff0c;什么是 MyBatis-Plus&#xff1f; &#xff08;1&#xff09;MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。我们可以理解为它已经封装好了一些…...

Jasper(1)——入门

Jasper&#xff08;1&#xff09;——入门 第一次写自己的总结~写得烂大家别介意哈O(∩_∩)O 由于项目需要&#xff0c;导出功能必须要Jasper导出报表&#xff0c;所以不得不用&#xff0c;刚接触真的蛮多坑。现在我自己还没摸索明白&#xff0c;因此很多我都是不知道的&#…...

crackme杂记007

花指令的特征&#xff1a; 遇到这种指令&#xff0c;可以立即判断出这是一个花指令&#xff0c;所以花指令较多的话&#xff0c;我们也可以通过搜索代码的方式快速去除 如上图&#xff0c;我们已经知道 E8 01 00 00 00 ?? ?? ?? ?? ?? C3 是花指令&#xff0c;所以…...

c++实现树的dfs,bfs

void dfs(Node* head) {if (head nullptr) {return;}std::cout << head->value << ",";dfs(head->left);dfs(head->right); }void bfs(Node* head) {if (head nullptr) { // if head is nullptr, return directlyreturn;}std::queue<Nod...

第138章 触发器关键字 - Order

文章目录第138章 触发器关键字 - Order用法详情默认第138章 触发器关键字 - Order在同一个EVENT和TIME有多个触发器的情况下&#xff0c;指定触发器应该触发的顺序。 用法 要指定此触发器的触发顺序&#xff0c;相对于具有相同EVENT和TIME的其他触发器&#xff0c;请使用以下…...

DP求解 最大连续子数组和

DP求解 最大连续子数组和 题目描述&#xff1a;输入一个整型数组&#xff0c;数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。 1. 暴力求解 思路分析&#xff1a;计算数组中每一个连续子数组的和&#xff0c;找出其中最大值 /*** 暴力求解* param nums…...

再生龙clonezilla启动u盘制作,从vmware启动

制作u盘教程&#xff1a; http://www.360doc.com/content/20/0509/22/59153222_911267277.shtml 制作好u盘后&#xff0c;vmware里把u盘添加成硬盘&#xff0c;从u盘所在的硬盘启动即可。...

关于在vue2.0中使用wangeditor富文本的一些问题

1.先下载相关依赖 npm install wangeditor 2. 引入相关包 import E from "wangeditor"; 3.注册相关内容 name: "picLibraryDetail", 4.相关代码如下&#xff1a; import E from "wangeditor"; export default {name: "picLibraryDetai…...

关于新手指导IntroJS,vue项目(vue3)和react项目(ant-design)的引用

安装插件 npm i introJs 一、vue3项目中引用intro作新手引导 src/utils/util.js文件 import introJs from intro.js/*** name: 新手指导* param {String} pathname 当前页面的path* param {Array} stepsArr 步骤内容&#xff08;包括element、intro&#xff09;* return {*}…...

nuxt中使用svg 开发svg组件

为什么要使用SVG 虽然我们在日常开发的时候&#xff0c;在使用iview 或者element ui等组件时&#xff0c;通常会包含一些常用icon&#xff1b;但是在面对一些特定的需求时&#xff0c;或者自己想high一下&#xff0c;这些通用的icon并不能很好的满足我们。这个时候我们可能会拿…...

A - Til the Cows Come Home POJ - 2387

A - Til the Cows Come Home POJ - 2387 最短路 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int maxn 2010; typedef pair<int, int> PII; // first 存距离…...

C#不同状态的按钮 消失or显示

效果展示&#xff1a; 点击前 点击后 //而且 ”备注“ 变成可编辑状态 点击确定后 编辑成功 并变为初始 只有一个的” 编辑 “按钮 并在listView更改成功 列表样式 解决方案&#xff1a; 首先创建三个Button 都放在一个位置上 当然需要有一个按钮重合在编辑上面&#xff08;为…...

vue--CompositionAPI 中如何使用 VueX

a.同步方法改变数据&#xff1a; Home.vue <template><div class"home"><img alt"Vue logo" src"../assets/logo.png"><h1>{{name}}</h1></div> </template><script> import {toRefs} from v…...

5. 断路器hystrix dashboard turbine

hystrix使用 feign中使用 hystrix feign:hystrix:enabled: true #必须配置Component public class EurekaClientHystrix implements EurekaClientFeign{}使用feign配置了&#xff0c;这些&#xff0c;即可进行回退 Feign 的起步依赖中已经包含Hystrix依赖&#xff0c;所以只…...

springBoot 一键启动多个服务

我们启动springboot服务时经常一个一个去启动,这样操作显然是非常麻烦的,那么怎样让他一键启动多个服务呢? 第一步:打开设置 第二步:选择Compound 第三步:选择你要启动的服务 第四步:点击就可以一键启动了...

JavaScript 字面值对象的一些基本方法

展开运算符的使用 //构造字面量对象时使用展开语法 let person {name:tom,age:18} let person2 {...person} //展开运算符展开对象属性时&#xff0c;只能在对象中进行展开 //console.log(...person); //报错&#xff0c;展开运算符不能直接展开对象 console.log(我是perso…...

深入理解cloud 题外话1:httpPool连接池工具类配置,logback.xml,feign日志配置

httpPool Configuration public class HttpPool {Beanpublic HttpClient httpClient(){System.out.println("init feign httpclient configuration " );// 生成默认请求配置RequestConfig.Builder requestConfigBuilder RequestConfig.custom();// 超时时间request…...

dbc2000 注册机|dbc2000 注册码注册机下载

点击下载来源&#xff1a;dbc2000 注册机 dbc2000 注册机是同名源程序软件的注册机软件&#xff0c;该源程序软件是一款应用于数据库搭建以及数据写入的数据库架设工具&#xff0c;它拥有强大的数据写入功能&#xff0c;在作为应用程序使用时&#xff0c;它不仅可以充当数据属性…...

秋招面经第八弹:网易二面-数据开发工程师

秋招第八弹&#xff1a;网易二面-数据开发工程师 写在最前&#xff1a;秋招以来一直在冲&#xff0c;因为事情比较多&#xff0c;对于笔试面试一直没有复盘&#xff0c;现在靠仅存的记忆把面试的一些问题记录下来&#xff0c;尽可能记录出能回忆到的问题&#xff0c;但可能记的…...

安卓课程格子APP

https://download.csdn.net/download/weixin_57836618/73810452 功能演示&#xff1a; 查看所有课程 点击主页面空白处即可添加课程 添加课程之后查看课程 查看双周课程 查看单周课程 6.查看课程详情...

强化学习——格子世界

强化学习——格子世界 项目源码地址&#xff1a;https://gitee.com/infiniteStars/machine-learning-experiment 1. 实验内容 2. 实验代码 import numpy as np import matplotlib.pyplot as plt from matplotlib.table import Table from xml.dom.minidom import Document #手…...

华为机试 - 跳格子游戏

目录 题目描述 输入描述 输出描述 用例 题目解析 算法源码 题目描述 地上共有N个格子&#xff0c;你需要跳完地上所有的格子&#xff0c;但是格子间是有强依赖关系的&#xff0c;跳完前一个格子后&#xff0c;后续的格子才会被开启&#xff0c;格子间的依赖关系由多组st…...

php 爬课程表信息,Ruby爬取教务系统生成课程表

我为什么要虐自己最近觉得课程格子广告越来越多&#xff0c;乱七八糟的东西越来越多&#xff0c;完全失去了一开始的存在价值&#xff0c;并且没有电脑端app&#xff0c;想查看课程必须拿出手机&#xff0c;而我使用电脑频率要比手机高&#xff0c;所以才有了折腾的动力。于是我…...

android 课程表 ui,UICollectionViewLayout实现课程表布局

因为项目中有课程表的相关模块&#xff0c;第一时间想到用UICollectionView。然而后期的需求越来越复杂&#xff0c;每个格子需要展示的内容越来越多&#xff0c;所以不得不寻找合适的解决方案。最后发现自定义UICollectionViewLayout可以实现我的需求。先放效果图&#xff1a;…...

Android自定义View课程表,Android 自定义View课程表表格

自己闲下来时间写的一个课表控件使用的自定义LinearLayout 里面View都是用代码实现的 最终效果如下图 写的可能有问题希望多多指点创建一个自定义LinearLayout 控件用来装载课程的信息和课程的周数 和节数大概的布局三这样的根据上面的看来觉得总体布局我分了两个 上面的星期是…...

java课程设计设计_java课程设计

1. 团队课程设计博客链接https://www.cnblogs.com/choco1ate/p/12172223.html2.本组课题及本人任务本组课题&#xff1a;泡泡堂(炸弹人)游戏本人任务&#xff1a;Box类(游戏地图中的每个方格)Bomb类(游戏过程中的)游戏玩家输赢信息的文件储存3.需求分析Box类&#xff1a;该类为…...

《课程格子》的一个笔试题目

题目如下&#xff0c;感觉很适合喜欢琢磨的程序员&#xff0c;也是考验你编码风格的时候。 Lets make a tower defense game&#xff08;塔防游戏):1. You have 1 tower, with H health and D dps(damage per second).2. There are n attackers, each with h_i health and d_i …...

Android仿照超级课程表 or 课程格子 一键提取课表功能(方正系统)

参考文章http://blog.csdn.net/sbsujjbcy ,本文仿照‘ 安卓弟 提供的android 项目实战——打造超级课程表一键提取课表功能文章&#xff0c;对他的代码进行了修改和补充&#xff0c;为什么要修改呢&#xff1f;原因是安卓弟的那个源码版本过于老旧&#xff0c;很多方法已经过…...

计算机错误提示声音,win7系统安装声卡驱动出现错误提示error code 0x0000000解决方法...

win7系统声卡就是管理声音的驱动程序&#xff0c;如果声卡受到损坏或故障问题的话就会导致电脑无法发声&#xff0c;就连听音乐、观看视频都无法进行。如果发现声卡无法发声的时候&#xff0c;我们可以试着检查电脑的声卡是否出现故障&#xff0c;或者是重新安装声卡。最近有位…...

服务器声卡硬件安装,虚拟声卡,详细教您怎么安装虚拟声卡

虚拟声卡可以在某些服务器主板上面使用&#xff0c;能够让服务器发出声音&#xff0c;虚拟声卡来带领我们走进声音的世界。还可以支持内录等功能。那么对于这种声卡是如何安装的呢&#xff1f;下面&#xff0c;小编给大家介绍安装虚拟声卡的方法了。在电脑语音聊天的时候我们说…...

ECharts快速入门

ECharts一、ECharts简介二、ECharts的基本使用步骤1&#xff1a;下载并引入echarts.js文件步骤2&#xff1a;准备一个具备大小的DOM容器步骤3&#xff1a;初始化echarts实例对象步骤4&#xff1a;指定配置项和数据&#xff08;option&#xff09;步骤5&#xff1a;将配置项设置…...

导致美国大范围网络瘫痪的Mirai僵尸网络

2016年导致美国大范围网络瘫痪而名噪一时的Mirai僵尸网络 Mirai僵尸网络是第一个“僵尸”各种物联网设备的大型僵尸网络&#xff0c;它使用了自动伪随机扫描过程来发现和感染新设备...

【神操作】网络分线器短路导致公司网络瘫痪

昨天公司网络整整瘫痪了一整天&#xff0c;今天早上才恢复&#xff0c;原因是有个新同事&#xff0c;把一根网线两个水晶头同时插到一个分线器上面&#xff0c;导致公司网络时好时坏&#xff0c;网和人都崩溃了&#xff0c;简直是神操作&#xff01;&#xff01;&#xff01; …...

微信公众号服务器瘫痪的现象,微信出现大范围故障瘫痪30分钟 现已恢复正常

原标题&#xff1a;微信出现大范围故障瘫痪30分钟 现已恢复正常就在刚刚过去一个小时&#xff0c;作为绝大部分人都离不开的微信出现了持续30分钟的故障 &#xff0c;几乎微信所有功能都遭受到影响&#xff0c;出现发不出消息、文件&#xff0c;甚至账号“被锁定”、“被删除”…...

员工私接无线路由器,使一部分人无法上网如何解决

员工A把家里路由器拿来&#xff0c;直接把网线接到路由器的lan口了&#xff0c;路由器开启了dhcp服务&#xff0c;致使一部分员工电脑ip地址获取错误的ip而无法上网。首先在不能上网的电脑打开网络连接查看本机ip:192.168.31.101。网关为192.168.31.1。打开浏览器输入网关地址&…...

kali linux wifi 瘫痪,在Kali Linux的奇怪的wifi

我在戴尔Inspiron N5110笔记本电脑上安装了Kali Linux。 在安装过程中&#xff0c;我手动设置了网络设置(自动配置失败)。 我已将ip设置为192.168.1.2&#xff0c;网络掩码为255.255.255.0&#xff0c;其他两个设置为192.168.1.1(我的家庭路由器IP)。 安装后我连接到我的网络并…...

2021-10-06 BGP错误配置导致脸书网络瘫痪

2021-10-06 BGP错误配置导致脸书网络瘫痪 论消防斧和其它破门工具对数据中心机房的重要性 在举国欢庆的这几天里&#xff0c;米国的互联网界发生了一件大事&#xff0c;FB因为BGP配置错误导致整个网络瘫痪&#xff0c;所有的业务都不能访问&#xff0c;网络中断时间近6个小时…...