第四篇 web前端
第1章 、Web的基本概念
前端基础总共分为三部分:html
、css
和js
。
1.3、HTTP协议
1.3.1 、http协议简介
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web )服务器与本地浏览器之间传输超文本的传送协议。HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。
# 最简单的web应用程序
import socket
sock=socket.socket()
sock.bind(("127.0.0.1",8800))
sock.listen(5)
while 1:
print("server is working...")
conn,addr=sock.accept()
recv_data=conn.recv(1024)
conn.send(b"HTTP/1.1 200 OK\r\n\r\nwelcome to web world!")
conn.close()
sock.close()
1.3.2、 http协议特性
(1) 基于TCP/IP协议
http协议是基于TCP/IP协议之上的应用层协议。
(2) 基于请求-响应模式
HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并 返回。换句话说,肯定是先从客户端开始建立通信的,服务器端在没有 接收到请求之前不会发送响应
(3) 无状态保存
HTTP是一种不保存状态,即无状态(stateless)协议。HTTP协议 自身不对请求和响应之间的通信状态进行保存。也就是说在HTTP这个 级别,协议对于发送过的请求或响应都不做持久化处理。
使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产 生。协议本身并不保留之前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把HTTP协议设计成 如此简单的。
可是,随着Web的不断发展,因无状态而导致业务处理变得棘手 的情况增多了。比如,用户登录到一家购物网站,即使他跳转到该站的 其他页面后,也需要能继续保持登录状态。针对这个实例,网站为了能 够掌握是谁送出的请求,需要保存用户的状态。HTTP/1.1虽然是无状态协议,但为了实现期望的保持状态功能, 于是引入了Cookie技术。有了Cookie再用HTTP协议通信,就可以管 理状态了。有关Cookie的详细内容稍后讲解。
(4) 无连接
无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
1.3.3、http请求协议与响应协议
http协议包含由浏览器发送数据到服务器需要遵循的请求协议与服务器发送数据到浏览器需要遵循的请求协议。用于HTTP协议交互的信被为HTTP报文。请求端(客户端)的HTTP报文 做请求报文,响应端(服务器端)的 做响应报文。HTTP报文本身是由多行数据构成的字文本。
(1) 请求协议
请求方式: get与post请求
- GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的请求体中.
- GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制
(2) 响应协议
响应状态码:状态码指的是是当客户端向服务器端发送请求时, 返回的请求 结果。借助状态码,用户可以知道服务器端是正常受理了请求,还是出现了什么问题错误 。
第2章 、HTML
了解了web相关基本概念以后,我们开始正式接触网页开发,网页开发的基础是HTML,所以,本章内容主要分两部分,一是介绍HTML的相关概念、发展历史,二是 创建HTML网页文档和认识HTML的基本结构。我们学会如何新建一个 HTML 页面和熟记HTML文档的基本结构和主要标签。
2.1、 HTML概述
-
HTML,即超文本标记语言(HyperText Markup Language ]),由SGML (标准通用标记语言) 发展而来,也叫web页面。扩展名是 .html 或是 .htm 。
-
HTML,是一种用来制作网页的标准标记语言。超文本,指的就是超出普通文本范畴的文档,可以包含文本、图片、视频、音频、链接等元素。
-
HTML 不是一种编程语言,而是一种写给网页浏览器、具有描述性的标记语言。
自1990年以来HTML就一直被用作WWW(World Wide Web的缩写,也可简写WEB,中文叫做万维网)的信息表示语言,使用HTML语言描述的文件,需要通过网页浏览器显示出效果。用户在访问网页时,是把服务器的HTML文档下载 到本地客户设备中,然后通过本地客户设备的浏览器将文档按顺序解释渲染成对应的网页效果。
网页本身是一种文本文件,通过在文本文件中添加各种各样的标记标签,可以告诉浏览器如何显示标记中的代表的内容,如:HTML中有的标签可以告诉浏览器要把字体放大,就像word一样,也有的标签可以告诉浏览器显示指定的图片,还有的标签可以告诉浏览器把内容居中或者倾斜等等。
每一个HTML标签代表的意义都不一样。同样,他们在浏览器中表现出来的外观也是不一样的。
2.2、 HTML结构和标签格式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
1、
<!DOCTYPE html>
告诉浏览器使用什么样的html
或者xhtml
来解析html
文档2、
<html></html>
是文档的开始标记和结束标记。此元素告诉浏览器其自身是一个HTML
文档,在它们之间是文档的头部<head>
和主体<body>
。3、元素出现在文档的开头部分。与之间的内容不会在浏览器的文档窗口显示,但是其间的元素有特殊重要的意义。
4、
<title></title>
定义网页标题,在浏览器标题栏显示。5、
<body></body>
之间的文本是可见的网页主体内容6、
<meta charset="UTF-8">
声明编码方式用utf8
2.3、标签的语法
<标签名 属性1=“属性值1” 属性2=“属性值2”……>内容部分</标签名>
<标签名 属性1=“属性值1” 属性2=“属性值2”…… />
1、HTML标签是由尖括号包围的特定关键词
2、标签分为闭合和自闭合两种标签
3、HTML不区分大小写
4、标签可以有若干个属性,也可以不带属性,比如就不带任何属性
2.4、基本标签
- 标题标签
<h1>标题1</h1>
<h2>标题2</h2>
<h3>标题3</h3>
<h4>标题4</h4>
<h5>标题5</h5>
<h6>标题6</h6>
- 段落标签
<p>大家好,我是段落1。</p>
<p>大家好,我是段落2。</p>
<p>大家好,我是段落3。</p>
- 换行标签
<p>大家好,我是段落标签p。我按了enter一下
换行了</p>
<p>大家好,我是段落标签p。我按了enter一下<br/> 换行了</p>
- 文本格式化标签
HTML提供了一系列的用于格式化文本的标签,可以让我们输出不同外观的元素,比如粗体和斜体字。如果需要在网页中,需要让某些文本内容展示的效果丰富点,可以使用以下的标签来进行格式化。
<b>定义粗体文本</b><br />
<strong>定义粗体文本方式2</strong><br />
<em>定义斜体字</em><br />
<i>定义斜体字方式2</i><br />
<del>定义删除文本</del><br />
- 特殊符号
® ©
标签大致可分为两类
- 块级标签(block) – 独占一行
- 内联标签(inline) – 按文本内容占位
- div和span标签
<div>只是一个块级元素,并无实际的意义。主要通过CSS样式为其赋予不同的表现.
<span>表示了内联行(行内元素),并无实际的意义,主要通过CSS样式为其赋予不同的表现
块级元素与行内元素的区别:所谓块元素,是以另起一行开始渲染的元素,行内元素则不需另起一行。如果单独在网页中插入这两个元素,不会对页面产生任何的影响。这两个元素是专门为定义CSS样式而生的。
2.5、超链接标签
2.5.1、超链接基本使用
超链接是浏览者和服务器的交互的主要手段,也叫超级链接或a链接,是网页中指向一个目标的连接关系,这个目标可以是网页、网页中的具体位置、图片、邮件地址、文件、应用程序等。
超链接是网页中最重要的元素之一。一个网站的各个网页就是通过超链接关联起来的,用户通过点击超链接可以从一个网页跳转到另一个网页。
几乎可以在所有的网页中找到链接。点击链接可以从一张页面跳转到另一张页面。例如,在阅读某个网站时,遇到一个不认识的英文,你只要在这个单词上单击一下,即可跳转到它的翻译页面中,看完单词的解释后点一下返回按钮,又可继续阅读,这就是超链接的常见用途。还有经常到购物网站中去,我们都是在百度搜索,然后点击对应的搜索项进入到对应的购物网站的,这也是超链接的作用。超链接的属性:
属性 | 值 | 描述 |
---|---|---|
href | 网络链接 [ 例如: http://www.baidu.com ] 本地链接 [ 例如:F:\html\index.html ] | 规定链接的跳转目标 |
title | 百度 | 链接的提示信息 |
target | _blank [ 在新建窗口中打开网页 ] _self [ 默认值,覆盖自身窗口打开网页 ] _parent [ 在父级框架中打开网页 ] _top [ 在顶级框架中打开网页 ] framename [ 在指定的框架中打开网页] | 与前面四项固定值不同,framename是泛指,并不是这个值,这点将在后面框架部分内容中详细介绍,这里可以暂时先略过 |
1、href是超链接最重要的属性,规定了用户点击链接以后的跳转目标,这个目标可以是 网络连接,也可以是本地连接。
2、网络链接指的是依靠网络来进行关联的地址,一般在地址前面是以 http://或者https://这样开头的,如果没有网络,则用户点击了超链接也无法访问对应的目标。
3、本地链接指的是本地计算机的地址,一般在地址前面是以 file:///开头或直接以 C:/、D:/、E:/开头的,不需要经过网络。
4、如果href的值留空,则默认是跳转到当前页面,也就是刷新当前页面。
2.5.2、锚点应用
锚点( anchor )是超链接的一种应用,也叫命名锚记,锚点可以像一个定位器一样,可以实现页面内的链接跳转,运用相当普遍。例如,我们有一个网页,由于内容太多,导致页面很长,而且里面的内容,可以分为N个部分。这样的话,我们就可以在网页的顶部设置一些锚点,这样便可以方便浏览者点击相应的锚点,到达本页内相应的位置,而不必在一个很长的网页里自行寻找。又例如,我们页面中,有个链接需要跳转到另一个页面的中间或者脚部去,这时候也可以运用上锚点技术来解决这个问题。
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title>锚点的使用</title>
</head>
<body>
<a href="#i1">第一章</a>
<a href="#i2">第二章</a>
<a href="#i3">第三章</a>
<div id="i1">
<p>第一章内容</p>
</div>
<div id="i2">
<p>第二章内容</p>
</div>
<div id="i3">
<p> 第三章内容</p>
</div>
</body>
</html>
2.6、img标签
在HTML中,图像由标签定义的,它可以用来加载图片到html网页中显示。网页开发过程中,有三种图片格式被广泛应用到web里,分别是 jpg、png、gif。
img标签的属性:
/*
src属性:
指定图像的URL地址,是英文source的简写,表示引入资源。
src的值可以是本地计算机存储的图片的地址,也可以是网络上外部网站的图片的地址。
如果src的值不正确,那么浏览器就无法正确的图片,而是显示一张裂图。
alt属性:指定图像无法显示时的替换文本。当图像显示错误时,在图像位置上显示alt的值。如上所示,就是谷歌浏览器中,引入图像失败后,显示了替换文本。alt属性一般 作为SEO优化的手段之一,所以,使用了img标签就需要加上alt属性。
width属性: 指定引入图片的显示宽度。
height属性:指定引入图片的显示高度。
border属性:指定引入图片的边框宽度,默认为0。
title属性:悬浮图片上的提示文字
*/
点击图片跳转可以配合a标签使用
<a><img src="" alt=""></a>
2.7、列表标签
<ul type="square">
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ul>
<ol start="100">
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ol>
2.8、表格标签
在HTML中使用table来定义表格。网页的表格和办公软件里面的xls一样,都是有行有列的。HTML使用tr标签定义行,使用td标签定义列。
语法:
<table border="1">
<tr>
<td>单元格的内容</td>
……
</tr>
……
</table>
1、
<table>
和</table>
表示一个表格的开始和结束。一组<table>...</table>
表示一个表格。2、border用于设置整个表格的边框宽度,默认为0,表示不显示边框。
3、
<tr>
和</tr>
表示表格中的一行的开始和结束。一组<tr>...</tr>
,一个表格可以有多行。通过计算table标签中包含多少对tr子标签即可知道一个表格有多少行。4、
<td>
和</td>
表示表格中的一个单元格的开始和结束。通过计算一个tr里面包含了多少对td自标签即可知道一个表格有多少列,多少的单元格了。
table属性
属性 | 值 | 描述 |
---|---|---|
width | px、% | 规定表格的宽度。 |
height | px、% | 规定表格的高度。 |
align | left、center、right | 规定表格相对周围元素的对齐方式。 |
bgcolor | rgb(x,x,x)、#xxxxxx、colorname | 规定表格的背景颜色。 |
background | url | 规定表格的背景图片。 |
border | px | 规定表格边框的宽度。 |
cellpadding | px、% | 规定单元格边框与其内容之间的空白。 |
cellspacing | px、% | 规定单元格之间的空隙。 |
td属性
表格中除了行元素以外,还有单元格,单元格的属性和行的属性类似。td和th都是单元格。
属性 | 值 | 描述 |
---|---|---|
height | px、% | 规定单元格的高度。 |
width | px、% | 规定单元格的宽度。 |
align | left、center、right | 规定单元格内容的对齐方式。 |
valign | top、middle、bottom | 规定单元格内容的垂直对齐方式。 |
bgcolor | rgb(x,x,x)、#xxxxxx、colorname | 规定单元格的背景颜色。 |
background | url | 规定单元格的背景图片。 |
rowspan | number | 规定单元格合并的行数 |
colspan | number | 规定单元格合并的列数 |
2.9、表单标签
表单主要是用来收集客户端提供的相关信息,提供了用户数据录入的方式,有多选、单选、单行文本、下拉列表等输入框,便于网站管理员收集用户的数据,是Web浏览器和Web服务器之间实现信息交流和数据传递的桥梁.
表单被form标签包含,内部使用不同的表单元素来呈现不同的方式来供用户输入或选择。当用户输入好数据后,就可以把表单数据提交到服务器端。
一个表单元素有三个基本组成部分:
-
表单标签,包含了表单处理程序所在的URL以及数据提交到服务器的方法等表单信息。
-
表单域,包含了文本框、密码框、隐藏域、多行文本框、复选框、单选框、下拉选择框和文件上传框等表单控件。
-
表单按钮,包括提交按钮、复位按钮和一般按钮,用于将数据传送到服务器上的CGI脚本或者取消输入,还可以用表单按钮来控制其他定义了处理脚本的处理工作。
在HTML中创建表单用form标签。每个表单都可以包含一到多个表单域或按钮。form标签属性:
属性 | 值 | 描述 |
---|---|---|
action | 访问服务器地址 | 服务器端表单处理程序的URL地址 |
method | post、get[默认值] | 表单数据的提交方法 |
target | 参考超链接的target属性 | 表单数据提交时URL的打开方式 |
enctype | application/x-www-form-urlencoded[默认值] multipart/form-data [用于文件上传] text/plain [用于纯文本数据发送] | 表单提交数据时的编码方式 |
<h3>用户注册</h3>
<form action="http://127.0.0.1:8800" method="get">
<p><label for="user">姓名</label>: <input type="text" name="user" id="user"></p>
<p>密码: <input type="password" name="pwd"></p>
<p>爱好:
<input type="checkbox" name="hobby" value="basketball">篮球
<input type="checkbox" name="hobby" value="football">足球
<input type="checkbox" name="hobby" value="shuangseqiu" checked>双色球
</p>
<p>性别:
<input type="radio" name="gender" value="men">男
<input type="radio" name="gender" value="female">女
<input type="radio" name="gender" value="qita">其他
</p>
<p>生日:<input type="date" name="birth"></p>
<p>籍贯:
<select name="province" id="" multiple size="2">
<option value="">广东省</option>
<option value="" selected>山东省</option>
<option value="">河北省</option>
</select>
</p>
<p>
<textarea name="" id="" cols="30" rows="10" placeholder="个人简介"></textarea>
</p>
<div>
<p><input type="reset" value="重置"></p>
<p><input type="button" value="普通按钮"></p>
<p><button>普通按钮</button></p>
<p><input type="submit" value="提交"></p>
</div>
</form>
第3章 、CSS
CSS就是Cascading Style Sheet的缩写,中文译作“层叠样式表”或者是“级联样式表”,是用于控制网页外观处理并允许将网页的表现与内容分离的一种标记性语言,CSS不需要编译,可以直接由浏览器执行(属于浏览器解释型语言),是Web网页开发技术的重要组成部分。
那么接下来,继续看下,使用CSS有什么好处吧。
-
使用CSS样式可以有效地对页面进行布局,更加灵活多样。
-
使用CSS样式可以对页面字体、颜色、背景和其他效果实现精确控制,同时对它们的修改和控制变得更加快捷,更加强大。
-
站点中所有的网页风格都使用一个CSS文件进行统一控制,达到一改全改。还可以快速切换主题,我们可以把HTML比作是骨架,CSS是衣服。同一个HTML骨架结构,不同CSS样式,所得到的美化布局效果不同。
-
CSS可以支持多种设备,比如手机,PDA,打印机,电视机,游戏机等。
-
CSS可以将网页的表现与结构分离,使页面载入得更快,更利于维护,这也是我们的最终目的。
CSS基本语法:
CSS的基本语法由选择器、属性、属性的值组成,如果选择器有多个属性,由分号隔开。
注意,这里的代码都是英文格式,例如花括号、冒号和分号。
3.1、CSS的引入方式
CSS样式有三种不同的使用方式,分别是行内样式,嵌入样式以及链接式。我们需要根据不同的场合不同的需求来使用不同的样式。
- 行内样式
行内样式,就是写在元素的style属性中的样式,这种样式仅限于元素内部起作用。当个别元素需要应用特殊样式时就可以使用内联样式。但不推荐大量使用内联样式,因为那样不利于后期维护。
<div style="color: white;background-color: #369;text-align: center">行内设置</div>
- 嵌入式
嵌入式,是把CSS样式写在HTML文档内部head标签中的style标签里。浏览器加载HTML的同时就已经加载了CSS样式了。当单个文档需要特殊,单独的样式时,可以使用内部样式表。
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title>锚点的使用</title>
<meta charset="utf8">
<style>
div{
color: white;
background-color: #369;
text-align: center
}
</style>
</head>
<body>
<div> 嵌入式</div>
</body>
</html>
- 链接式
链接式,就是把CSS样式写在HTML文档的外部,一个后缀为 .css 的外部样式表中,然后使用时在head标签中,使用link标签的href属性引入文件即可。当CSS样式需要应用在很多页面时,外部样式表是最理想的选择。在使用外部样式表的情况下,我们可以通过改变一个文件来改变这所有页面的外观。
common.css
div{
color: white;
background-color: #369;
text-align: center
}
html文件
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title>锚点的使用</title>
<meta charset="utf8">
<link rel="stylesheet" href="common.css">
</head>
<body>
<div>链接式</div>
</body>
</html>
3.2、CSS的选择器
3.2.1、基本选择器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#i1{
color: red;
}
.c1{
color: red;
}
.c2{
font-size: 32px;
}
</style>
</head>
<body>
<div id="i1">item1</div>
<div id="i2">item2</div>
<div id="i3">item3</div>
<div class="c1 c2">item4</div>
<div class="c1">item5</div>
<div class="c1">item6</div>
</body>
</html>
3.2.2、组合选择器
- 与或选择器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
/*与选择器*/
p.c1{
color: red;
}
/*或选择器*/
p.c1,#i1{
color: red;
}
</style>
</head>
<body>
<!--与选择器-->
<div class="c1">item1</div>
<p class="c1">item2</p>
<div>item3</div>
<p id="i1">item4</p>
</body>
</html>
3.3、CSS的属性操作
3.3.1、文本属性
- font-style(字体样式风格)
/*
属性值:
normal:设置字体样式为正体。默认值。
italic:设置字体样式为斜体。这是选择字体库中的斜体字。
oblique:设置字体样式为斜体。人为的使文字倾斜,而不是去使用字体库的斜体字。
*/
- font-weight(字体粗细)
/*
属性值:
normal:设置字体为正常字体。相当于数字值400
bold:设置字体为粗体。相当于数字值700。
bolder:设置字体为比父级元素字体更粗的字体。
lighter:设置字体为比父级元素字体更细的字体。
number:用数字表示字体粗细。从小到大,越来约粗,取值范围:100、200、300、400、500、600、700、800、900。
注意:
font-weight的常用值有两个normal和bold,其他的值在浏览器中的支持并不好。
*/
- font-size(字体大小)
/*
font-size的值有很多,有xx-small、x-small、small、medium、large、x-large、xx-large、smaller和larger,也可以设置值为具体的数值加上对应的计算单位来表示字体的大小。字体单位有像素( px )、字符( em,默认1em等于16px,2em等于32px,根据不同浏览器的默认字体大小而决定 )、百分比( % ),磅[点]( pt )。
字体不指定大小时,主流浏览器默认是15像素到16像素。旧版本的谷歌浏览器,字体最小只能设置成12像素,新版已经修复。*/
- font-family(字体族)
/*
font-family可以指定元素使用的字体系列或字体族。当我们使用font-family指定字体族的时候,可以指定多种字体,作为候补。指定多个字体的时候,需要使用逗号隔开。
如果css中没有声明当前内容使用的字体族的时候,默认:
中文: 宋体 [ win7以后默认是 微软雅黑 ]
英文: Arial
*/
- color(字体颜色)
// 可以使用color来表示字体的颜色,颜色值最常用的有三种形式,英文单词,十六进制,RGB十进制。更高级的有 RGBA、HSL、HSLA,不过低版本的浏览器并不支持。
<style>
.c1{
color: red;
}
.c1{
color: #369;
}
.c1{
color: RGB(0,0,255);
}
</style>
另外要注意,使用十六进制表示颜色值的时候,如果字符的格式类似于“AAAAAA”的这种,六个字符一样的;又或者是“AABBCC”,这种,一二,三四,五六 位置上的数字一样的,我们可以使用简写来表达。
- text-align(文本对齐方式)
/*
text-align属性可以设置文本内容的水平对齐方式。属性值常用的有
左对齐left、居中对齐center、右对齐right。justify 实现两端对齐文本效果。
*/
- text-decoration
// 使用text-decoration可以设置文本内容的装饰线条,正常的文本是没有线条的,常用的值有none,underline,overline,line-through四种。
- line-height(字体行高)
// 字体行高即字体最底端与字体内部顶端之间的距离。值可以是normal、px、number、%。
行高 = 字体大小 + 上半行距 + 下半行距
- vertical-align
vertical-align 属性设置元素的垂直对齐方式。
<img src="" alt=""><span>yuan</span>
3.3.2、背景属性
- background-color(背景颜色)
页面的背景颜色有四种属性值表示,分别是transparent(透明),RGB十进制颜色表示,十六进制颜色表示和颜色单词表示。
属性使用:
/*
background-color: transparent; // 透明
background-color: rgb(255,0,0); // 红色背景
background-color: #ff0000; // 红色背景
background-color: red; // 红色背景
*/
- background-image(背景图片)
background-image可以引入一张图片作为元素的背景图像。默认情况下,background-image放置在元素的左上角,并在垂直和水平方向重复平铺。
语法:
// background-image: url('图片地址')
当同时定义了背景颜色和背景图像时,背景图像覆盖在背景颜色之上。 所以当背景图片没有被加载到,或者不能完全铺满元素时,就会显示背景颜色。
- background-repeat(背景平铺方式)
CSS中,当使用图像作为背景了以后,都是默认把整个页面平铺满的,但是有时候在很多场合下面,页面并不需要这种默认的效果,而可能需要背景图像只显示一次,或者只按照指定方式进行平铺的时候,可以使用background-repeat来进行设置。
background-repeat专门用于设置背景图像的平铺方式,一般有四个值,默认是repeat(平铺),no-repeat(不平铺),repeat-x(X轴平铺),repeat-y(Y轴平铺)。
- background-position(背景定位)
CSS中支持元素对背景图像的定位摆放功能,就是利用background-position属性来实现,以页面中元素的左上角为原点(0,0),把元素的内部区域当成一个坐标轴(上边框为X轴,越往左X的值越大,左边框为Y轴,越往下Y轴的值就越大,反之亦然),然后计算出背景图片的左上角与圆点的距离(x轴和y轴的距离),然后把背景图片放入到指定的位置上,对背景图片的位置进行精确的控制和摆放。
background-position的值分成两个,使用空格隔开,前面一个是背景图片左上角的x轴坐标,后面一个是背景图片左上角的y轴坐标。两个值都可以是正、负值。
语法:
// background-position: x轴坐标 y轴坐标
背景定位的值除了是具体的数值以外,还可以是左(left)、中(center)、右(right)
- background(背景样式缩写)
和字体属性一样,多个不同背景样式属性也是可以同时缩写的,不过不需要像字体那样按照一定的顺序,背景样式的缩写属性的顺序是不固定的,可以任意编排。
语法:
// background: 背景颜色 背景图片 背景平铺方式 背景定位;
3.3.3、边框属性
- border-style(边框风格)
定义边框的风格,值可以有
/*
none:没有边框,当border的值为none的时候,系统将会忽略[border-color]
hidden:隐藏边框,低版本浏览器不支持。
dotted:点状边框。
dashed:虚线边框。
solid:实线边框。
double:双实线边框,两条单线与其间隔的和等于border-width值。
*/
border-style的值可以缩写的:
/*
只有一个值的时候表示同时控制上下左右的边框风格。
只有两个值的时候表示分别控制上下、左右的边框风格。
有三个值的时候表示分别控制上、左右、下的边框风格。
有四个只的时候表示分别控制上、右、下、左的边框风格。
*/
border-style还可以单独指定不同方向:
/*
border-top-style 设置上边的边框风格
border-bottom-style 设置下边的边框风格
border-left-style 设置左边的边框风格
border-right-style 设置右边的边框风格
*/
- border-width(边框宽度)
使用border-width可以定义边框的厚度,值可以是medium,thin,thick和指定数值的宽度。 同时,border-width也可以进行缩写:
/*
只有一个值的时候表示同时控制上下左右的边框宽度。
只有两个值的时候表示分别控制上下、左右的边框宽度。
有三个值的时候表示分别控制上、左右、下的边框宽度。
有四个只的时候表示分别控制上、右、下、左的边框宽度。
*/
border-width也可以单独指定不同方向:
/*
border-top-width 设置上边的边框宽度
border-bottom-width 设置下边的边框宽度
border-left-width 设置左边的边框宽度
border-right-width 设置右边的边框宽度
*/
- border-color(边框颜色)
定义边框的颜色,值表示的方式可以是十六进制,RGB十进制和单词表示法。
同上,border-color的缩写:
/*
只有一个值的时候表示同时控制上下左右的边框颜色。
只有两个值的时候表示分别控制上下、左右的边框颜色。
有三个值的时候表示分别控制上、左右、下的边框颜色。
有四个只的时候表示分别控制上、右、下、左的边框颜色。
*/
border-color也可以单独指定不同方向:
/*
border-top-color 设置上边的边框颜色
border-bottom-color 设置下边的边框颜色
border-left-color 设置左边的边框颜色
border-right-color 设置右边的边框颜色
*/
- 边框样式缩写
还可以把边框风格,边框宽度,边框颜色进行组合在一起,进行缩写:语法:
// border: 边框宽度 边框样式 边框颜色;
注意,border的缩写值可以不按照顺序来进行书写。这样的缩写可以同时控制4个方向的边框样式。
3.3.4、列表属性
CSS中提供了一些列表属性可以用来:
(1)、设置不同的列表项标记为有序列表
(2)、设置不同的列表项标记为无序列表
(3)、设置列表项标记为图像
-
list-style-type(系统提供的列表项目符号)
-
list-style-image(自定义的列表项目符号)
li { list-style-image:url('qq.gif'); }
3.3.5、dispaly属性
display可以指定元素的显示模式,它可以把行内元素修改成块状元素,也可以把别的模式的元素改成行内元素。diisplay常用的值有四个。
语法:
/*
display: block; // 声明当前元素的显示模式为块状元素
display: inline; // 声明当前元素的显示模式为行内元素
display: inline-block; // 声明当前元素的显示模式为行内块状元素
display: none; // 声明当前元素的显示模式为隐藏
*/
3.3.6、盒子模型(重点)
盒模型是CSS的核心知识点之一,它指定元素如何显示以及如何相互交互。HTML页面上的每个元素都可以看成一个个方盒子,这些盒子由元素的content(内容)、padding(内边距)、border(边框)、margin(外边距)组成。
- padding(内边距及其缩写)
内边距,也叫“内补白”,表示页面中元素的边框与内容的距离。内边距的值不能是负值,相当于table标签的cellpadding属性。
内边距可以设置多个值:
/*
当padding只有一个值的时候表示同时控制上下左右的内边距。
当padding只有两个值的时候表示分别控制上下、左右的内边距。
当padding有三个值的时候表示分别控制上、左右、下的内边距。
当padding有四个只的时候表示分别控制上、右、下、左的内边距。
*/
内边距也可以进行单独设置:
/*
padding-top 设置上边的外边距
padding -bottom 设置下边的外边距
padding -left 设置左边的外边距
padding -right 设置右边的外边距
*/
- margin(外边距及其缩写)
外边距,也叫“外补白”,表示页面中元素与元素之间的距离。外边距越大,两者的距离就越远,反之,如果外边距越小,则元素之间的距离就越近,外边距的值可以是正数,也可以是负值。
margin也可以像padding一样设置多个值和单独方向设置,用法一样。
1、在网页的开发过程中,需要让一个元素相对于父级元素作水平居中时,可以借助margin的特性来实现。
使用margin让元素自身居中: margin: 0 auto;
2、浏览器的默认边距清零
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.c1{
width: 100%;
height: 600px;
border: 1px solid red;
}
.c2{
width: 50%;
height: 40px;
background-color: rebeccapurple;
margin: 10px auto;
}
</style>
</head>
<body>
<div class="c1">
<div class="c2"></div>
<div class="c2"></div>
</div>
</body>
</html>
边距案例:
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="utf8">
<style>
*{
margin: 0;
padding: 0;
}
.c1{
width: 80%;
margin: 100px auto;
}
.c1 .J_categoryList{
list-style: none;
}
.c1 .J_categoryList li{
display: inline-block;
margin: 10px;
}
.c1 .J_categoryList li a{
font-size: 16px;
color: #333;
padding: 20px;
border: 1px solid rebeccapurple;
text-decoration: none;
}
</style>
</head>
<body>
<div class="c1">
<ul class="J_categoryList">
<li><a href=""><span>红米</span></a></li>
<li><a href=""><span>电视</span></a></li>
<li><a href=""><span>笔记本</span></a></li>
<li><a href=""><span>家电</span></a></li>
<li><a href=""><span>小米手机</span></a></li>
</ul>
</div>
</body>
</html>
3.3.7、float属性(重点)
- 流动布局
流动模型(Flow),即文档流,浏览器打开HTML网页时,从上往下,从左往右,逐一加载。
在正常情况下,HTML元素都会根据文档流来分布网页内容的。
文档流有2大特征:
① 块状元素会随着浏览器读取文档的顺序,自上而下垂直分布,一行一个的形式占据页面位置。
② 行内元素会随着浏览器区队文档的顺序,从左往右水平分布,一行多个的形式占据页面位置。行内元素摆放满一行以后才会到下一行继续排列。
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title></title>
<style>
div{ border: 1px solid #f00; margin: 4px; }
.d3{ width: 100px; }
</style>
</head>
<body>
<div>d1</div>
<div>d2</div>
<div class="d3">
<span>span1</span>
<a>a1</a>
<a>a2</a>
<span>span2</span>
</div>
</body>
</html>
- 浮动模型
要学习浮动模型的布局模式,就要了解CSS提供的浮动属性(float)。浮动属性是网页布局中最常用的属性之一,通过浮动属性不但可以很好的实现页面布局,而且还可以依靠它来制作导航栏等页面功能。
简单浮动:
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title>简单浮动</title>
<style>
.c1{
width: 200px;
height: 200px;
background-color: indianred;
float: left;
}
.c2{
width: 300px;
height: 200px;
background-color: orange;
float: left;
}
.c3{
width: 400px;
height: 200px;
background-color: lightblue;
float: left;
}
</style>
</head>
<body>
<div class="c1"></div>
<div class="c2"></div>
<div class="c3"></div>
</body>
</html>
- 字围效果
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title>字围效果</title>
<style>
.c1{
width: 200px;
height: 200px;
background-color: indianred;
}
.c2{
width: 300px;
height: 200px;
background-color: orange;
float: left;
}
.c3{
width: 400px;
height: 400px;
background-color: lightblue;
}
</style>
</head>
<body>
<div class="c1">111</div>
<div class="c2">222</div>
<div class="c3">333</div>>
</body>
</html>
案例:
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title>字围案例</title>
<meta charset="utf8">
<style>
.c1{
width: 500px;
}
img{
float: left;
width: 300px;
height: 200px;
}
</style>
</head>
<body>
<div class="c1">
<img src="" alt="">
<span class="text">
</span>
</div>
</body>
</html>
当一个元素被设置浮动后,将具有以下特性:
- 任何申明为float 的元素都会自动被设置为一个行内块状元素,具有行内块状元素的特性。
- 假如某个元素A是浮动的,如果A元素上一个元素也是浮动的,那么A元素会跟随在上一个元素的后边(如果一行放不下这两个元素,那么A元素会被挤到下一行);如果A元素上一个元素是标准流中的元素,那么A的相对垂直位置不会改变,也就是说A的顶部总是和上一个元素的底部对齐。
- 在标准浏览器中如果浮动元素a脱离了文档流,那么排在浮动元素a后的元素将会往回排列占据浮动元素a本来所处的位置,使页面布局产生变化。
- 如果水平方向上没有足够的空间容纳浮动元素,则转向下一行。
- 字围效果:文字内容会围绕在浮动元素周围。
- 浮动元素只能浮动至左侧或者右侧。
- 浮动元素只能影响排在其后面元素的布局,却无法影响出现在浮动元素之前的元素。
- 清除浮动
网页布局中,最常用的布局便是浮动模型。但是浮动了以后就会破坏原有的文档流,使页面产生不必要的改动,所以我们一般在浮动了以后,达到目的了,就紧接着清除浮动。
在主流浏览器(如Firefox)下,如果没有设置height,元素的高度默认为auto,且其内容中有浮动元素时,在这种情况下元素的高度不能自动伸长以适应内容的高度,使得内容溢出到容器外面而影响(甚至破坏)布局的情况,叫“浮动溢出”,为了防止这个现象的出现而进行的CSS处理操作,CSS里面叫“清除浮动”。
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title></title>
<meta charset="utf8">
<style>
.box{
border: 1px solid red;
}
.c1{
width: 200px;
height: 200px;
background-color: #336699;
float: left;
}
.c2{
width: 200px;
height: 200px;
background-color: orange;
float: right;
}
.footer{
width: 100%;
height: 60px;
background-color: yellowgreen;
}
</style>
</head>
<body>
<div class="box">
<div class="c1"></div>
<div class="c2"></div>
</div>
<div class="footer"></div>
</body>
</html>
clear是css中专用于清除浮动的,常用的属性值有以下几个:
值 | 描述 |
---|---|
left | 在左侧不允许浮动元素。 |
right | 在右侧不允许浮动元素。 |
both | 在左右两侧均不允许浮动元素。 |
none | 默认值。允许浮动元素出现在两侧。 |
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title>简单浮动</title>
<style>
.c1{
width: 200px;
height: 200px;
background-color: indianred;
float: left;
/*float: right;*/
}
.c2{
width: 300px;
height: 200px;
background-color: orange;
float: left;
clear: left;
/*clear: both;*/
}
.c3{
width: 400px;
height: 200px;
background-color: lightblue;
float: left;
}
</style>
</head>
<body>
<div class="c1"></div>
<div class="c2"></div>
<div class="c3"></div>
</body>
</html>
清除浮动解决父级塌陷问题:
.clearfix:after { /*在类名为“clearfix”的元素内最后面加入内容*/
content: "."; /*内容为“.”就是一个英文的句号而已。也可以不写。*/
display: block; /*加入的这个元素转换为块级元素。*/
clear: both; /*清除左右两边浮动。*/
visibility: hidden; /*可见度设为隐藏。注意它和display:none;是有区别的。*/
/* visibility:hidden;仍然占据空间,只是看不到而已;*/
line-height: 0; /*行高为0;*/
height: 0; /*高度为0;*/
font-size:0; /*字体大小为0;*/
}
整段代码就相当于在浮动元素后面跟了个宽高为0的空div,然后设定它clear:both来达到清除浮动的效果。
之所以用它,是因为,你不必在html文件中写入大量无意义的空标签,又能清除浮动。
<div class="head clearfix"></div>
此外,还给父元素加上溢出隐藏属性(overflow: hidden;)来进行清除浮动。
3.3.8、position属性
就像photoshop中的图层功能会把一整张图片分层一个个图层一样,网页布局中的每一个元素也可以看成是一个个类似图层的层模型。层布局模型就是把网页中的每一个元素看成是一层一层的,然后通过定位属性position对元素进行定位摆放,最终实现网页的布局。
定位属性position有4个值,分别是静态定位(static)、相对定位(relative)、绝对定位(absolute)和固定定位(fixed)。默认就是static。所以我们略过。
元素设置了定位以后,还要依靠4个方位属性来进行定位摆放。
方位属性:
/*
top:让元素相对于指定目标的顶部偏移指定的距离。
例如: top:10px; 表示距离顶部10像素
right:让元素相对于指定目标的右边偏移指定的距离。
例如: right:10px; 表示距离顶部10像素
bottom:让元素相对于指定目标的底部偏移指定的距离。
例如: bottom:10px; 表示距离顶部10像素
left:让元素相对于指定目标的左边偏移指定的距离。
例如: left:10px; 表示距离顶部10像素
*/
- 相对定位(relative)
相对定位就是在正常文档流中,元素相对于自身位置使用left、right、top、bottom属性进行定位偏移。
.c1{
width: 200px;
height: 200px;
background-color: indianred;
}
.c2{
width: 200px;
height: 200px;
background-color: orange;
position: relative;
left: 200px;
top: 200px;
}
.c3{
width: 200px;
height: 200px;
background-color: lightblue;
}
- 绝对定位(absolute)
绝对定位就是将元素脱离文档流,然后使用left、right、top、bottom属性相对于其最接近的一个具有定位属性的父级元素进行绝对定位,如果不存在这样的父级元素,则默认是相对于body元素进行绝对定位。
轮播图案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
}
.outer .img img{
width: 590px;
height: 470px;
}
.outer{
width: 590px;
height: 470px;
margin: 100px auto;
position: relative;
border: 1px solid red;
}
.outer ul{
list-style: none;
}
.outer .img li{
position: absolute;
top: 0;
left: 0;
}
.outer .hide{
display: none;
}
.outer .num{
position: absolute;
z-index: 100;
bottom: 16px;
left: 16px;
}
.outer .num li{
display: inline-block;
width: 16px;
height: 16px;
background-color: lightgray;
text-align: center;
line-height: 16px;
border-radius: 50%;
margin-left: 5px;
}
.num li.current{
background-color: red;
}
.btn li{
position: absolute;
top:50%;
width: 30px;
height: 60px;
background-color: gray;
text-align: center;
line-height: 60px;
color: white;
margin-top: -30px;
}
.btn .left_btn{
left: 0;
}
.btn .right_btn{
right: 0;
}
</style>
</head>
<body>
<div class="outer">
<ul class="img">
<li><a href=""><img src="https://imgcps.jd.com/ling4/100009077475/5Lqs6YCJ5aW96LSn/5L2g5YC85b6X5oul5pyJ/p-5bd8253082acdd181d02fa71/c3196f74/cr/s/q.jpg" alt=""></a></li>
<li class="hide"><a href=""><img src="https://img12.360buyimg.com/pop/s590x470_jfs/t1/178599/8/1142/28979/6087858aE1679d862/173e0cfa2612b705.jpg.webp" alt=""></a></li>
<li class="hide"><a href=""><img src="https://imgcps.jd.com/ling4/6038430/5Lqs5Lic5aW954mp57K-6YCJ/MuS7tjjmipgz5Lu2N-aKmA/p-5bd8253082acdd181d02fa42/9ea6716c/cr/s/q.jpg" alt=""></a></li>
<li class="hide"><a href=""><img src="https://img12.360buyimg.com/pop/s1180x940_jfs/t1/174771/34/8431/98985/6095eaa2E8b8b4847/044f1b6318db4a9f.jpg.webp" alt=""></a></li>
<li class="hide"><a href=""><img src="https://img11.360buyimg.com/pop/s1180x940_jfs/t1/180648/29/4209/88436/609f7547Ec7b73259/74a4d25e8d614173.jpg.webp" alt=""></a></li>
</ul>
<ul class="num">
<li class="current"></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<ul class="btn">
<li class="left_btn"> < </li>
<li class="right_btn"> > </li>
</ul>
</div>
</body>
</html>
- 固定定位(fixed)
固定定位与绝对定位有点相似,但是固定定位是使用left、right、top、bottom属性相对于整个浏览器的窗口进行定位,而不再相对于某个HTML页面元素了,所以当元素使用了固定定位以后,就算页面的滚动条滚动了,固定定位的元素也不会变化位置。也就是说固定定位是相对于窗口的定位,不受文档流的影响了。
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="utf8">
<style>
body{
margin: 0;
}
.c1{
width: 100%;
height: 2000px;
background-color: lightgray;
}
.c2{
width: 200px;
height: 60px;
background-color: yellowgreen;
text-align: center;
line-height: 60px;
position: fixed;
right: 20px;
bottom: 20px;
}
</style>
</head>
<body>
<div class="c1"></div>
<div class="c2">返回顶部</div>
</body>
</html>
第4章 、JavaScript
4.1、JavaScript 的历史
4.1.1、JavaScript 的历史
JavaScript 因为互联网而生,紧随着浏览器的出现而问世。回顾它的历史,就要从浏览器的历史讲起。
1990年底,欧洲核能研究组织(CERN)科学家Tim Berners-Lee,在全世界最大的电脑网络——互联网的基础上,发明了万维网(World Wide Web),从此可以在网上浏览网页文件。最早的网页只能在操作系统的终端里浏览,也就是说只能使用命令行操作,网页都是在字符窗口中显示,这当然非常不方便。
1992年底,美国国家超级电脑应用中心(NCSA)开始开发一个独立的浏览器,叫做Mosaic。这是人类历史上第一个浏览器,从此网页可以在图形界面的窗口浏览。
1994年10月,NCSA的一个主要程序员Marc Andreessen联合风险投资家Jim Clark,成立了Mosaic通信公司(Mosaic Communications),不久后改名为Netscape。这家公司的方向,就是在Mosaic的基础上,开发面向普通用户的新一代的浏览器Netscape Navigator。
1994年12月,Navigator发布了1.0版,市场份额一举超过90%。
Netscape 公司很快发现,Navigator浏览器需要一种可以嵌入网页的脚本语言,用来控制浏览器行为。当时,网速很慢而且上网费很贵,有些操作不宜在服务器端完成。比如,如果用户忘记填写“用户名”,就点了“发送”按钮,到服务器再发现这一点就有点太晚了,最好能在用户发出数据之前,就告诉用户“请填写用户名”。这就需要在网页中嵌入小程序,让浏览器检查每一栏是否都填写了。
管理层对这种浏览器脚本语言的设想是:功能不需要太强,语法较为简单,容易学习和部署。那一年,正逢Sun公司的Java语言问世,市场推广活动非常成功。Netscape公司决定与Sun公司合作,浏览器支持嵌入Java小程序(后来称为Java applet)。但是,浏览器脚本语言是否就选用Java,则存在争论。后来,还是决定不使用Java,因为网页小程序不需要Java这么“重”的语法。但是,同时也决定脚本语言的语法要接近Java,并且可以支持Java程序。这些设想直接排除了使用现存语言,比如Perl、Python和TCL。
1995年,Netscape公司雇佣了程序员Brendan Eich开发这种网页脚本语言。Brendan Eich有很强的函数式编程背景,希望以Scheme语言(函数式语言鼻祖LISP语言的一种方言)为蓝本,实现这种新语言。
1995年5月,Brendan Eich只用了10天,就设计完成了这种语言的第一版。它是一个大杂烩,语法有多个来源:
- 基本语法:借鉴C语言和Java语言。
- 数据结构:借鉴Java语言,包括将值分成原始值和对象两大类。
- 函数的用法:借鉴Scheme语言和Awk语言,将函数当作第一等公民,并引入闭包。
- 原型继承模型:借鉴Self语言(Smalltalk的一种变种)。
- 正则表达式:借鉴Perl语言。
- 字符串和数组处理:借鉴Python语言。
为了保持简单,这种脚本语言缺少一些关键的功能,比如块级作用域、模块、子类型(subtyping)等等,但是可以利用现有功能找出解决办法。这种功能的不足,直接导致了后来JavaScript的一个显著特点:对于其他语言,你需要学习语言的各种功能,而对于JavaScript,你常常需要学习各种解决问题的模式。而且由于来源多样,从一开始就注定,JavaScript的编程风格是函数式编程和面向对象编程的一种混合体。
Netscape 公司的这种浏览器脚本语言,最初名字叫做 Mocha,1995年9月改为LiveScript。12月,Netscape公司与Sun公司(Java语言的发明者和所有者)达成协议,后者允许将这种语言叫做JavaScript。这样一来,Netscape公司可以借助Java语言的声势,而Sun公司则将自己的影响力扩展到了浏览器。
之所以起这个名字,并不是因为JavaScript本身与Java语言有多么深的关系(事实上,两者关系并不深),而是因为Netscape公司已经决定,使用Java语言开发网络应用程序,JavaScript可以像胶水一样,将各个部分连接起来。当然,后来的历史是Java语言的浏览器插件失败了,JavaScript反而发扬光大。
1995年12月4日,Netscape 公司与 Sun 公司联合发布了 JavaScript 语言。当时的意图是将 JavaScript 作为 Java 的补充,用来操作网页。
1996年3月,Navigator 2.0 浏览器正式内置了 JavaScript 脚本语言。
4.1.2、JavaScript与ECMAScript的关系
1996年8月,微软模仿JavaScript开发了一种相近的语言,取名为JScript(JavaScript是Netscape的注册商标,微软不能用),首先内置于IE 3.0。Netscape公司面临丧失浏览器脚本语言的主导权的局面。
1996年11月,Netscape公司决定将JavaScript提交给国际标准化组织ECMA(European Computer Manufacturers Association),希望JavaScript能够成为国际标准,以此抵抗微软。ECMA的39号技术委员会(Technical Committee 39)负责制定和审核这个标准,成员由业内的大公司派出的工程师组成,目前共25个人。该委员会定期开会,所有的邮件讨论和会议记录,都是公开的。
1997年7月,ECMA组织发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为ECMAScript。这个版本就是ECMAScript 1.0版。之所以不叫JavaScript,一方面是由于商标的关系,Java是Sun公司的商标,根据一份授权协议,只有Netscape公司可以合法地使用JavaScript这个名字,且JavaScript已经被Netscape公司注册为商标,另一方面也是想体现这门语言的制定者是ECMA,不是Netscape,这样有利于保证这门语言的开放性和中立性。因此,ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现。在日常场合,这两个词是可以互换的。
ECMAScript只用来标准化JavaScript这种语言的基本语法结构,与部署环境相关的标准都由其他标准规定,比如DOM的标准就是由W3C组织(World Wide Web Consortium)制定的。s
ECMA-262标准后来也被另一个国际标准化组织ISO(International Organization for Standardization)批准,标准号是ISO-16262。
4.1.3、JavaScript与Java的关系
JavaScript和Java是两种不一样的语言,但是它们之间存在联系。
JavaScript的基本语法和对象体系,是模仿Java而设计的。但是,JavaScript没有采用Java的静态类型。正是因为JavaScript与Java有很大的相似性,所以这门语言才从一开始的LiveScript改名为JavaScript。基本上,JavaScript这个名字的原意是“很像Java的脚本语言”。
在JavaScript语言中,函数是一种独立的数据类型,以及采用基于原型对象(prototype)的继承链。这是它与Java语法最大的两点区别。JavaScript语法要比Java自由得多。
另外,Java语言需要编译,而JavaScript语言则是运行时由解释器直接执行。
总之,JavaScript的原始设计目标是一种小型的、简单的动态语言,与Java有足够的相似性,使得使用者(尤其是Java程序员)可以快速上手。
4.1.4、JavaScript的版本
1997年7月,ECMAScript 1.0发布。
1998年6月,ECMAScript 2.0版发布。
1999年12月,ECMAScript 3.0版发布,成为JavaScript的通行标准,得到了广泛支持。
2007年10月,ECMAScript 4.0版草案发布,对3.0版做了大幅升级,预计次年8月发布正式版本。草案发布后,由于4.0版的目标过于激进,各方对于是否通过这个标准,发生了严重分歧。以Yahoo、Microsoft、Google为首的大公司,反对JavaScript的大幅升级,主张小幅改动;以JavaScript创造者Brendan Eich为首的Mozilla公司,则坚持当前的草案。
2008年7月,由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激进,ECMA开会决定,中止ECMAScript 4.0的开发(即废除了这个版本),将其中涉及现有功能改善的一小部分,发布为ECMAScript 3.1,而将其他激进的设想扩大范围,放入以后的版本,由于会议的气氛,该版本的项目代号起名为Harmony(和谐)。会后不久,ECMAScript 3.1就改名为ECMAScript 5。
2009年12月,ECMAScript 5.0版正式发布。Harmony项目则一分为二,一些较为可行的设想定名为JavaScript.next继续开发,后来演变成ECMAScript 6;一些不是很成熟的设想,则被视为JavaScript.next.next,在更远的将来再考虑推出。TC39的总体考虑是,ECMAScript 5与ECMAScript 3基本保持兼容,较大的语法修正和新功能加入,将由JavaScript.next完成。当时,JavaScript.next指的是ECMAScript 6。第六版发布以后,将指ECMAScript 7。TC39预计,ECMAScript 5会在2013年的年中成为JavaScript开发的主流标准,并在此后五年中一直保持这个位置。
2011年6月,ECMAscript 5.1版发布,并且成为ISO国际标准(ISO/IEC 16262:2011)。到了2012年底,所有主要浏览器都支持ECMAScript 5.1版的全部功能。
2013年3月,ECMAScript 6草案冻结,不再添加新功能。新的功能设想将被放到ECMAScript 7。
2013年12月,ECMAScript 6草案发布。然后是12个月的讨论期,听取各方反馈。
2015年6月,ECMAScript 6正式发布,并且更名为“ECMAScript 2015”。这是因为TC39委员会计划,以后每年发布一个ECMAScirpt的版本,下一个版本在2016年发布,称为“ECMAScript 2016”。
除了ECMAScript的版本,很长一段时间中,Netscape公司(以及继承它的Mozilla基金会)在内部依然使用自己的版本号。这导致了JavaScript有自己不同于ECMAScript的版本号。1996年3月,Navigator 2.0内置了JavaScript 1.0。JavaScript 1.1版对应ECMAScript 1.0,但是直到JavaScript 1.4版才完全兼容ECMAScript 1.0。JavaScript 1.5版完全兼容ECMAScript 3.0。目前的JavaScript 1.8版完全兼容ECMAScript 5。
https://javascript.ruanyifeng.com/introduction/history.html)
4.2、JS的引入方式
1 直接编写
<script>
console.log('hello yuan')
</script>
2 导入文件
<script src="hello.js"></script>
4.3、ECMAScript基本语法
js是一门弱类型的编程语言,属于基于对象和基于原型的脚本语言.
- 变量
格式:
// 方式1 先声明再赋值
var 变量名; // 声明的变量如果没有进行赋值,或者没有被定义的变量,值默认是undefined
变量名 = 变量值;
// 方式2 声明并赋值
var 变量名 = 变量值;
// 方式3 一行可以声明多个变量.并且可以是不同类型
var name="yuan", age=20, job="lecturer";
1、声明变量时 可以不用var. 如果不用var 那么它是全局变量
2、变量命名,首字符只能是字母,下划线,$美元符 三选一,余下的字符可以是下划线、美元符号或任何字母或数字字符且区分大小写
- 注释
// 单行注释
/*
多行注释
*/
- 语句分隔符
var a = 1 // 分号和换行符作为语句分隔符号
var b = 2;
console.log(a,b)
4.4、ECMAScript 基本数据类型
4.4.1、数字类型
JavaScript 没有整型和浮点型,只有一种数字类型,即number类型。
var x = 10;
var y = 3.14;
console.log(x,typeof x); // 10 "number"
console.log(y,typeof y); // 3.14 "number"
4.4.2、字符串
字符串创建(两种方式)
- 变量 = “字符串”
- 字串对象名称 = new String (字符串)
var str1="hello world";
var str1= new String("hello word");
// 字符串对象的操作
var str = "hello"; // 这就是字符串对象
console.log(str);
// 字符串对象内置属性
// length 计算字符串的长度
console.log( str.length );
// 字符串对象内置方法
// toUpperCase(); 字母大写转换
// toLowerCase(); 字母小写转换
console.log( str.toUpperCase() );
console.log( str.toLowerCase() );
// indexOf 获取指定字符在字符串中第一次出现的索引位置
// 字符串也有下标,也可以使用中括号来提取字符串的指定字符
console.log(str[1]); // e
console.log( str.indexOf("e") ); // 1
// match 正则匹配
// js中也存在正则,正则的使用符号和python里面是一样的
var str = "我的电话是: 13312345678,你的电话: 13512345678";
var ret = str.match(/\d{11}/g); // 匹配,提取数据
console.log(ret);
// replace 正则替换
var str = "我的电话是: 13512345678";
var ret = str.replace(/(\d{3})\d{4}(\d{4})/,"$1****$2"); // 正则 的 捕获模式 $1$2表示的正则中第一个和第二个小括号捕获的内容
console.log(ret);
// search 正则查找,如果查找不到,则返回-1
var str = "hello";
var ret = str.search(/l/);
console.log(ret);
// 切片,当前方法支持使用负数代表倒数下标
// slice(开始下标) 从开始位置切到最后
// slice(开始下标,结束下标) 从开始下标切到指定位置之前
var str = "helloworld";
var ret = str.slice(3,6); // 开区间,不包含结束下标的内容
console.log(ret); // low
var ret = str.slice(5);
console.log(ret); // world
var ret = str.slice(2,-1);
console.log(ret); // lloworl
var ret = str.slice(-4,-1);
console.log(ret); // orl
var ret = str.slice(-1,-4);
console.log(ret); // orl
// split 正则分割,经常用于把字符串转换成数组
var str = "广东-深圳-南山";
var ret = str.split("-");
console.log( ret );
// substr 截取
var str = "hello world";
var ret = str.substr(0,3);
console.log(ret); // hel
// trim 移除字符串首尾空白
var password = " ge llo ";
var ret = password.trim();
console.log(password.length); // 13
console.log(ret.length); // 6
4.4.3、布尔值
1、Boolean类型仅有两个值:true和false,也代表1和0,实际运算中true=1,false=0
2、布尔值也可以看作on/off、yes/no、1/0对应true/false
3、Boolean值主要用于JavaScript的控制语句
console.log(true);
console.log(false);
console.log(typeof true);
console.log(true === 1);
console.log(true == 1);
console.log(true + 1);
console.log(false + 1);
4.4.4、空值(Undefined和Null)
- undefined类型
undefined类型只有一个值,即 undefined。
(1) 当声明的变量未初始化时,该变量的默认值是 undefined。
(2)当函数无明确返回值时,返回的也是值 undefined;
- null类型
另一种只有一个值的类型是 null,它只有一个专用值 null,即它的字面量。值 undefined 实际上是从值 null 派生来的,因此 ECMAScript 把它们定义为相等的。
尽管这两个值相等,但它们的含义不同。undefined 是声明了变量但未对其初始化时赋予该变量的值,null 则用于表示尚未存在的对象。如果函数或方法要返回的是对象,那么找不到该对象时,返回的通常是 null。
4.4.5、类型转换
js中,类型转换有2种.一种就是强制转换,一种就是自动转换.
因为js是一门弱类型的脚本语言,所以变量会在运算符的运行要求,有时候根据运算符的要求,进行自动转换的.
- 强制转换
// 1. 转换数据为数值类型
// parseInt 把数据转换成整数
// parseFloat 把数据转换成小数
// Number 把数据转换成数值
var box1 = "一共100件"; // 转换会失败
var box1 = "100件"; // 转换会成功
var ret = parseInt(box1);
console.log(box1);
console.log(ret);
//
var box2 = "3.14";
console.log( parseFloat(box2) ); // 3.14
//
var box3 = "3.14"; // 使用Number转换的数据里面必须是纯数字!!!!否则都会转换失败
// var box3 = "3.1.4"; // 转换失败!
console.log( Number(box3) );
// 对于转换数值,如果转换失败的话,则结果为 NaN ,是 Not a Number ,但是NaN的类型也是number类型
// 2. 转换数据为字符串
// 变量.toString()
// String(数据)
var box4 = 3.14;
var ret = box4.toString();
console.log(ret);
//
ret = String(box4);
console.log(ret);
// 3. 转换数据成布尔类型
// Boolean()
var box5 = "";
console.log( Boolean(box5) ); // false
var box6 = -1;
console.log( Boolean(box6) ); // true
var box7 = 0;
console.log( Boolean(box7) ); // false;
var box8 = "false";
console.log( Boolean(box8) ); // true
var box9 = [];
console.log( Boolean(box9) ); // true
var box10 = {};
console.log( Boolean(box10) ); // true
var box11 = "0";
console.log( Boolean(box11) ); // true
var box12 = null;
console.log( Boolean(box12) ); // false
var box13 = undefined;
console.log( Boolean(box13) ); // false
- 自动转换
// 所谓的自动转换,其实弱类型中的变量会根据当前代码的需要,进行类型的自动隐式转化
var box1 = 1 + true;
// true 转换成数值,是1, false转换成数值,是0
console.log(box1); // 2
var box2 = 1 + "200";
console.log(box2); // 1200 原因是,程序中+的含义有2种,第一: 两边数值相加, 第二: 两边字符串拼接.但是在js中运算符的优先级中, 字符串拼接的优先级要高于数 // 值的加减乘除,所以解析器优先使用了+号作为了字符串的拼接符号了,因为程序就需要+号两边都是字符串才能完成运算操作,因此1变成字符串了
// 最终的结果就是 "1" +"200"
var box3 = 1 - "200";
console.log(box3); // -199;因为-号中表示的就是左边的数值减去右边的数值,因此程序就会要求"200"是数值,因此内部偷偷的转换了一下
4.4.6、原始值和引用值
// 初始值类型
var a = "yuan";
var b = a;
a = "alvin";
console.log(a);//alvin
console.log(b);//yuan
// 对象类型
var arr1=[1,2];
arr2 = arr1;
arr1.push(3);
console.log(arr1)// [1,2,3]
console.log(arr2);//[1,2,3]
arr1=[4,5];
console.log(arr1);//[4,5]
console.log(arr2);//[1,2,3]
4.5、运算符
- 运算符
/*
//算术运算符
+ 数值相加
- 数值相减
* 数值相乘
/ 数值相除
% 数值求余
** 数值求幂
a++ 数值后自增1 a=a+1
++a 数值前自增1 a=a+1
b-- 数值后自减1 b=b-1
--b 数值前自减1 b=b-1
//赋值运算符
=
+=
-=
*=
/=
%=
**=
//比较运算符,比较的结果要么是true, 要么是false
> 大于
< 小于
>= 大于或者等于
<= 小于或者等于
!= 不等于[计算数值]
== 等于[计算]
!== 不全等[不仅判断数值,还会判断类型是否一致]
=== 全等[不仅判断数值,还会判断类型是否一致]
//逻辑运算符
&& 并且 and 两边的运算结果为true,最终结果才是true
|| 或者 or 两边的运算结果为false,最终结果才是false
! 非 not 运算符的结果如果是true,则最终结果是false ,反之亦然.
//条件运算符[三目运算符]
条件?true:false
例如:
var age = 12;
var ret = age>=18?"成年":"未成年"; // 相当于 python中的"成年" if age >= 18 else "未成年"
console.log(ret);
*/
4.6、流程控制语句
编程语言的流程控制分为三种:
- 顺序结构(从上向下顺序执行)
- 分支结构
- 循环结构
之前我们学习的方式就是顺序执行,即代码的执行从上到下,一行行分别执行。
例如:
console.log("星期一");
console.log("星期二");
console.log("星期三");
4.6.1、分支结构
- if 分支语句
if(条件){
// 条件为true时,执行的代码
}
if(条件){
// 条件为true时,执行的代码
}else{
// 条件为false时,执行的代码
}
if(条件1){
// 条件1为true时,执行的代码
}else if(条件2){
// 条件2为true时,执行的代码
}....
}else{
// 上述条件都不成立的时候,执行的代码
}
- switch语句
switch(条件){
case 结果1:
满足条件执行的结果是结果1时,执行这里的代码..
break;
case 结果2:
满足条件执行的结果是结果2时,执行这里的代码..
break;
.....
default:
条件和上述所有结果都不相等时,则执行这里的代码
}
1、switch比if else更为简洁
2、执行效率更高。switch…case会生成一个跳转表来指示实际的case分支的地址,而这个跳转表的索引号与switch变量的值是相等的。从而,switch…case不用像if…else那样遍历条件分支直到命中条件,而只需访问对应索引号的表项从而到达定位分支的目的。
3、到底使用哪一个选择语句,代码环境有关,如果是范围取值,则使用if else语句更为快捷;如果是确定取值,则使用switch是更优方案。
4.6.2、循环语句
- while循环
while(循环的条件){
// 循环条件为true的时候,会执行这里的代码
}
循环案例:
var count = 0
while (count<10){
console.log(count);
count++;
}
- for循环
// 循环三要素
for(1.声明循环的开始; 2.条件; 4. 循环的计数){
// 3. 循环条件为true的时候,会执行这里的代码
}
for(循环的成员下标 in 被循环的数据){
// 当被循环的数据一直没有执行到最后下标,都会不断执行这里的代码
}
循环案例:
// 方式1
for (var i = 0;i<10;i++){
console.log(i)
}
// 方式2
var arr = [111,222,333]
for (var i in arr){
console.log(i,arr[i])
}
- 退出循环(break和continue)
for (var i = 0;i<100;i++){
if (i===88){
continue // 退出当次循环
// break // 退出当前整个循环
}
console.log(i)
}
作业:
(1)计算1+2+3+…+100=?
(2)求20的阶乘值
4.7、数组对象
- 创建数组
创建方式1:
var arrname = [元素0,元素1,….]; // var arr=[1,2,3];
创建方式2:
var arrname = new Array(元素0,元素1,….); // var test=new Array(100,"a",true);
- 数组方法
var arr = ["A","B","C","D"];
// 内置属性
console.log( arr.length );
// 获取指定下标的成员
// console.log( arr[3] ); // D
console.log( arr[arr.length-1] ); // 最后一个成员
// (1) pop() 出栈,删除最后一个成员作为返回值
var arr = [1,2,3,4,5];
var ret = arr.pop();
console.log(arr); // [1, 2, 3, 4]
console.log(ret); // 5
// (2) push() 入栈,给数组后面追加成员
var arr = [1,2,3,4,5];
arr.push("a");
console.log(arr); // [1, 2, 3, 4, 5, "a"]
// (3) shift是将数组的第一个元素删除
var arr = [1,2,3,4,5];
arr.shift()
console.log(arr); // [2, 3, 4, 5]
// (4) unshift是将value值插入到数组的开始
var arr = [1,2,3,4,5];
arr.unshift("yuan")
console.log(arr); // ["yuan",1,2, 3, 4, 5]
// (5) reverse() 反转排列
var arr = [1,2,3,4,5];
arr.reverse();
console.log(arr); // [5, 4, 3, 2, 1]
// (6) slice(开始下标,结束下标) 切片,开区间
// (7) sort() 排序
var arr = [3,4,1,2,5,10];
console.log( arr ); // [3, 4, 1, 2, 5, 10]
arr.sort();
//
// // 这是字符的排序,不是数值的排序
console.log(arr); // [1, 10, 2, 3, 4, 5]
// 数值升序
var arr = [3,4,1,2,5,10];
arr.sort(function(a,b){
return a-b;
});
console.log(arr); // [1, 2, 3, 4, 5, 10]
// 数值降序
var arr = [3,4,1,2,5,10];
arr.sort(function(a,b){
return b-a;
});
console.log(arr); // [10, 5, 4, 3, 2, 1]
// (8) splice(操作位置的下标,删除操作的成员长度,"替换或者添加的成员1","替换或者添加的成员2") 添加/删除指定的成员 "万能函数"
var arr1 = [1,2,3];
arr1.splice(1,1);
console.log(arr1); // 删除指定的1个成员 [1, 3]
var arr2 = ["a","b","c","d"];
arr2.splice(2,0,"w","x","w"); // 添加
console.log(arr2); // ["a", "b", "w", "x", "w", "c", "d"]
var arr3 = ["a","b","c"];
arr3.splice(1,1,"w");
console.log(arr3); // ["a", "w", "c"]
// (9) concat() 把2个或者多个数组合并
var arr1 = [1,2,3];
var arr2 = [4,5,7];
var ret = arr1.concat(arr2);
console.log( ret );
// (10) join() 把数组的每一个成员按照指定的符号进行拼接成字符串
var str = "广东-深圳-南山";
var arr = str.split("-");
console.log( arr ); // ["广东", "深圳", "南山"];
var arr1 = ["广东", "深圳", "南山"];
var str1 = arr1.join("-");
console.log( str1 ); // 广东-深圳-南山
// (11) find() 高阶函数, 返回符合条件的第一个成员
var arr = [4,6,5,7];
var func = (num)=>{
if(num%2===0){
return num;
}
};
var ret = arr.find(func);
console.log( ret ); // 4
// (12) filter() 高阶函数, 对数组的每一个成员进行过滤,返回符合条件的结果
var arr = [4,6,5,7];
function func(num){ // 也可以使用匿名函数或者箭头函数
if(num%2===0){
return num;
}
}
var ret = arr.filter(func); // 所有的函数名都可以作为参数传递到另一个函数中被执行
console.log( ret );
// (13) map() 对数组的每一个成员进行处理,返回处理后的每一个成员
var arr = [1,2,3,4,5];
var ret = arr.map((num)=>{
return num**3;
});
console.log( ret ); // [1, 8, 27, 64, 125]
// (14) 其它方法
// includes 查询指定数据是否在数组中存在!
// indexOf() 查询指定数据在数组中第一次出现的位置
// isArray() 判断变量的值是否是数组
- 遍历数组
var arr = [12,23,34]
for (var i in arr){
console.log(i,arr[i])
}
4.8、Object对象
8.1、object对象的基本操作
Object 的实例不具备多少功能,但对于在应用程序中存储和传输数据而言,它们确实是非常理想的选择。
创建 Object 实例的方式有两种。
var person = new Object();
person.name = "alvin";
person.age = 18;
另一种方式是使用对象字面量表示法。
对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。下面这个例子就使用了对象字面量语法定义了与前面那个例子中相同的person 对象:
var person = {
name : "alvin",
age : 18
};
- object可以通过. 和 []来访问。
console.log(person["age"]);
console.log(person.age)
- object可以通过for循环遍历
for (var attr in person){
console.log(attr,person[attr]);
}
8.2、json序列化和反序列化
JSON
:JavaScript 对象表示法(JavaScript Object Notation),是一种轻量级的数据交换格式。易于人阅读和编写。
// json是一种数据格式, 语法一般是{}或者[]包含起来
// 内部成员以英文逗号隔开,最后一个成员不能使用逗号!
// 可以是键值对,也可以是列表成员
// json中的成员如果是键值对,则键名必须是字符串.而json中的字符串必须使用双引号圈起来
// json数据也可以保存到文件中,一般以".json"结尾.
// 前端项目中,一般使用json作为配置文件.
{
"name": "xiaoming",
"age":12
}
[1,2,3,4]
{
"name": "xiaoming",
"age":22,
"sex": true,
"son": {
"name":"xiaohuihui",
"age": 2
},
"lve": ["篮球","唱","跳"]
}
js中也支持序列化和反序列化的方法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// js对象,因为这种声明的对象格式很像json,所以也叫json对象
var data = {
name: "xiaoming",
age: 22,
say: function(){
alert(123);
}
};
// 把json对象转换成json字符串
var ret = JSON.stringify(data);
console.log(ret ); // {"name":"xiaoming","age":22}
// 把json字符串转换成json对象
var str = `{"name":"xiaoming","age":22}`;
var ret = JSON.parse(str);
console.log(ret);
</script>
</body>
</html>
4.9、Date对象
- 创建Date对象
//方法1:不指定参数
var nowd1=new Date();
console.log(nowd1.toLocaleString( ));
//方法2:参数为日期字符串
var d2=new Date("2004/3/20 11:12");
console.log(d2.toLocaleString( ));
var d3=new Date("04/03/20 11:12");
console.log(d3.toLocaleString( ));
//方法3:参数为毫秒数
var d4=new Date(5000);
console.log(d4.toLocaleString( ));
console.log(d4.toUTCString());
//方法4:参数为年月日小时分钟秒毫秒
var d5=new Date(2004,2,20,11,12,0,300);
console.log(d5.toLocaleString( ));//毫秒并不直接显示
- 获取时间信息
获取日期和时间
getDate() 获取日
getDay () 获取星期
getMonth () 获取月(0-11)
getFullYear () 获取完整年份
getYear () 获取年
getHours () 获取小时
getMinutes () 获取分钟
getSeconds () 获取秒
getMilliseconds () 获取毫秒
getTime () 返回累计毫秒数(从1970/1/1午夜)
- 日期和时间的转换
日期和时间的转换:
// 返回国际标准时间字符串
toUTCString()
// 返回本地格式时间字符串
toLocalString()
// 返回累计毫秒数(从1970/1/1午夜到本地时间)
Date.parse(x)
// 返回累计毫秒数(从1970/1/1午夜到国际时间)
Date.UTC(x)
练习:以2021年03月2日 14:1:43 星期二
格式化输出当前时间
function getCurrentDate(){
//1. 创建Date对象
var date = new Date(); //没有填入任何参数那么就是当前时间
//2. 获得当前年份
var year = date.getFullYear();
//3. 获得当前月份 js中月份是从0到11.
var month = date.getMonth()+1;
//4. 获得当前日
var day = date.getDate();
//5. 获得当前小时
var hour = date.getHours();
//6. 获得当前分钟
var min = date.getMinutes();
//7. 获得当前秒
var sec = date.getSeconds();
//8. 获得当前星期
var week = date.getDay(); //没有getWeek
// 2014年06月18日 15:40:30 星期三
return year+"年"+changeNum(month)+"月"+day+"日 "+hour+":"+min+":"+sec+" "+parseWeek(week);
}
//解决 自动补齐成两位数字的方法
function changeNum(num){
if(num < 10){
return "0"+num;
}else{
return num;
}
}
//将数字 0~6 转换成 星期日到星期六
function parseWeek(week){
var arr = ["星期日","星期一","星期二","星期三","星期四","星期五","星期六"];
// 0 1 2 3 .............
return arr[week];
}
console.log(getCurrentDate());
4.10、Math对象
// Number对象的内置方法
// toFixed(x) 保留小数位
var num = 100.3;
var ret = num.toFixed(2);
console.log(num); // 100.3
console.log(ret); // 100.30
// Math对象的内置方法
// abs(x) 返回数值的绝对值
// var num = -10;
console.log( Math.abs(num) ); // 10
// ceil(x) 向上取整
var num = 10.3;
console.log( Math.ceil(num) ); // 11
// floor(x) 向下取整
var num = 10.3;
console.log( Math.floor(num) ); // 10
// max(x,y,z,...,n)
console.log( Math.max(3,56,3) ); // 56
// min(x,y,z,...,n)
// pow(x,y)
console.log(Math.pow(3, 2)); // 相等于 3**2
console.log( 3**2 ); // 使用这个,上面废弃
// random() 生成0-1随机数
console.log( Math.random() );
// 生成0-10之间的数值
console.log( Math.random() * 10 );
// round(x) 四舍五入
// 生成0-10之间的整数
console.log( Math.round( Math.random() * 10 ) );
- 练习:获取1-100的随机整数,包括1和100
var num=Math.random();
num=num*100;
num=Math.round(num);
console.log(num)
4.11、Function 对象
函数在程序中代表的就是一段具有功能性的代码,可以让我们的程序编程更加具有结构性和提升程序的复用性,也能让代码变得更加灵活强大
4.11.1、声明函数
/*
// 函数的定义方式1
function 函数名 (参数){
函数体;
return 返回值;
}
功能说明:
可以使用变量、常量或表达式作为函数调用的参数
函数由关键字function定义
函数名的定义规则与标识符一致,大小写是敏感的
返回值必须使用return
// 函数的定义方式2
用 Function 类直接创建函数的语法如下:
var 函数名 = new Function("参数1","参数n","function_body");
虽然由于字符串的关系,第二种形式写起来有些困难,但有助于理解函数只不过是一种引用类型,它们的行为与用 Function 类明确创建的函数行为是相同的。 */
4.11.2、函数调用
//f(); --->OK
function f(){
console.log("hello")
}
f() //----->OK
不同于python,js代码在运行时,会分为两大部分———检查装载 和 执行阶段。
- 检查装载阶段:会先检测代码的语法错误,进行变量、函数的声明
- 执行阶段:变量的赋值、函数的调用等,都属于执行阶段。
4.11.3、函数参数
(1) 参数基本使用
// 位置参数
function add(a,b){
console.log(a);
console.log(b);
}
add(1,2)
add(1,2,3)
add(1)
// 默认参数
function stu_info(name,gender="male"){
console.log("姓名:"+name+" 性别:"+gender)
}
stu_info("yuan")
(2)函数中的arguments对象
function add(a,b){
console.log(a+b);//3
console.log(arguments.length);//2
console.log(arguments);//[1,2]
}
add(1,2)
// arguments的应用1
function add2(){
var result=0;
for (var num in arguments){
result+=arguments[num]
}
console.log(result)
}
add2(1,2,3,4,5)
// arguments的应用2
function f(a,b,c){
if (arguments.length!=3){
throw new Error("function f called with "+arguments.length+" arguments,but it just need 3 arguments")
}
else {
alert("success!")
}
}
f(1,2,3,4,5)
4.11.4、函数返回值
在函数体内,使用 return 语句可以设置函数的返回值。一旦执行 return 语句,将停止函数的运行,并运算和返回 return 后面的表达式的值。如果函数不包含 return 语句,则执行完函数体内每条语句后,返回 undefined 值。
function add(x,y) {
return x+y
}
var ret = add(2,5);
console.log(ret)
1、在函数体内可以包含多条 return 语句,但是仅能执行一条 return 语句
2、函数的参数没有限制,但是返回值只能是一个;如果要输出多个值,可以通过数组或对象进行设计。
4.11.5、函数作用域
作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理。
任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。
// 局部变量,是在函数内部声明,它的生命周期在当前函数被调用的时候, 当函数调用完毕以后,则内存中自动销毁当前变量
// 全局变量,是在函数外部声明,它的生命周期在当前文件中被声明以后就保存在内存中,直到当前文件执行完毕以后,才会被内存销毁掉
首先熟悉下var
var name = "yuan"; // 声明一个全局变量 name并赋值”yuan“
name = "张三"; // 对已经存在的变量name重新赋值 ”张三“
console.log(name);
age = 18 // 之前不存在age变量,这里等同于var age = 19 即声明全局变量age并赋值为18
var gender = "male"
var gender = "female" // 原内存释放与新内存开辟,指针指向新开辟的内存
console.log(gender)
作用域案例:
var num = 10; // 在函数外部声明的变量, 全局变量
function func(){
// num = 20; // 函数内部直接使用变量,则默认调用了全局的变量,
//var num = 20; // 函数内部使用var 或者 let声明的变量则是局部变量
// 函数内部直接使用变量,则默认调用了全局的变量,
// 使用变量的时候,解释器会在当前花括号范围值搜索是否有关键字var 或者 let 声明了变量,如果没有,则一层一层往外查找最近的声明
// 如果最终查找不到,则直接报错! 变量名 is not define!
console.log("函数内部num:",num)
}
func();
console.log("全局num:",num);
4.11.6、匿名函数
匿名函数,即没有变量名的函数。在实际开发中使用的频率非常高!也是学好JS的重点。
// 匿名函数赋值变量
var foo = function () {
console.log("这是一个匿名函数!")
};
// 匿名函数的自执行
(function (x,y) {
console.log(x+y);
})(2,3)
// 匿名函数作为一个高阶函数使用
function bar() {
return function () {
console.log("inner函数!")
}
}
bar()()
使用匿名函数表达式时,函数的调用语句,必须放在函数声明语句之后!
4.12、BOM对象
BOM:Broswer object model,即浏览器提供我们开发者在javascript用于操作浏览器的对象。
4.12.1、window对象
- 窗口方法
// BOM Browser object model 浏览器对象模型
// js中最大的一个对象.整个浏览器窗口出现的所有东西都是window对象的内容.
console.log( window );
// alert() 弹出一个警告框
window.alert("hello");
//confirm 弹出一个确认框,点击确认,返回true, 点击取消,返回false
var ret = confirm("您确认要删除当前文件么?");
console.log( ret );
// 弹出一个消息输入框,当点击确认以后,则返回可以接收到用户在输入框填写的内容.如果点击取消,则返回null
var ret = prompt("请输入一个内容","默认值");
console.log( ret );
// close() 关闭当前浏览器窗口
window.close();
//打开一个新的浏览器窗口
window.open("http://www.baidu.com","_blank","width=800px,height=500px,left=200px,top=200px");
- 定时方法
setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。而setTimeout是在指定的毫秒数后调用code一次。
// 设置循环定时器
var ID = window.setInterval(code,millisec) // 每millisec毫秒执行一次code
// 取消循环定时器
window.clearInterval(ID);
// 设置单次定时器
var ID = window.setTimeout(code,millisec) // millisec毫秒后执行code一次
// 取消单次定时器
window.clearTimeout(ID);
其中,code为要调用的函数或要执行的代码串。millisec周期性执行或调用 code 之间的时间间隔,以毫秒计。
显示时间案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="ID1" type="text" >
<button onclick="begin()">开始</button>
<button onclick="end()">停止</button>
<script>
function showTime(){
var nowd2=new Date().toLocaleString();
var temp=document.getElementById("ID1");
temp.value=nowd2;
}
var ID;
function begin(){
if (ID==undefined){
showTime();
ID=setInterval(showTime,1000);
}
}
function end(){
clearInterval(ID);
ID=undefined;
}
</script>
</body>
</html>
4.12.2、Location ( 地址栏)对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button onclick="func1()">查看Location对象</button>
<button onclick="func2()">跳转到百度</button>
<button onclick="func3()">F5</button>
<script>
function func1(){
console.log( location );
}
// 地址栏对象控制和操作地址栏
// 所谓的地址就是当前页面所在地址
// 地址结构:
// 协议://域名:端口/路径/文件名?查询字符串#锚点
console.log( `协议=>${location.protocol}` );
console.log( `域名=>${location.port}` );
console.log( `域名=>${location.hostname}` );
console.log( `域名:端口=>${location.host}` );
console.log( `路径=>${location.pathname}` );
console.log( `查询字符串=>${location.search}` );
console.log( `锚点=>${location.hash}` );
console.log(`完整的地址信息=>${location.href}`);
function func2(){
// location.href="http://www.baidu.com"; // 页面跳转
location.assign("http://www.baidu.com"); // 页面跳转
}
function func3(){
location.reload(); // 刷新页面
}
</script>
</body>
</html>
4.13、DOM对象(JS核心)
DOM document Object Model 文档对象模型
// 整个html文档,会保存一个文档对象document
// console.log( document ); // 获取当前文档的对象
4.13.1、查找标签
- 直接查找标签
document.getElementsByTagName("标签名")
document.getElementById("id值")
document.getElementsByClassName("类名")
1、方法的返回值是dom对象还是数组
2、document对象可以是任意dom对象,将查询范围限制在当前dom对象
- 导航查找标签
elementNode.parentElement // 父节点标签元素
elementNode.children // 所有子标签
elementNode.firstElementChild // 第一个子标签元素
elementNode.lastElementChild // 最后一个子标签元素
elementNode.nextElementSibling // 下一个兄弟标签元素
elementNode.previousElementSibling // 上一个兄弟标签元素
- CSS选择器查找
document.querySelector("css选择器") //根据css选择符来获取查找到的第一个元素,返回标签对象(dom对象)
document.querySelectorAll("css选择器"); // 根据css选择符来获取查找到的所有元素,返回数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="i1">DIV1</div>
<div class="c1">DIV</div>
<div class="c1">DIV</div>
<div class="c1">DIV</div>
<div class="outer">
<div class="c1">item</div>
</div>
<div class="c2">
<div class="c3">
<ul class="c4">
<li class="c5" id="i2">111</li>
<li>222</li>
<li>333</li>
</ul>
</div>
</div>
<script>
// 直接查找
var ele = document.getElementById("i1"); // ele就是一个dom对象
console.log(ele);
var eles = document.getElementsByClassName("c1"); // eles是一个数组 [dom1,dom2,...]
console.log(eles);
var eles2 = document.getElementsByTagName("div"); // eles2是一个数组 [dom1,dom2,...]
console.log(eles2);
var outer = document.getElementsByClassName("outer")[0];
var te = outer.getElementsByClassName("c1");
console.log(te);
// 导航查找
var c5 = document.getElementsByClassName("c5")[0];
console.log(c5); // c5是一个DOM对象
console.log(c5.parentElement.lastElementChild); // 返回值是dom对象
console.log(c5.parentElement.children); // 返回值是dom对象数组
console.log(c5.nextElementSibling.nextElementSibling);
console.log(c5.parentElement.children);
// css选择器
var dom = document.querySelector(".c2 .c3 .c5");
console.log(":::",dom);
var doms = document.querySelectorAll("ul li");
console.log(":::",doms);
</script>
</body>
</html>
4.13.2、绑定事件
- 静态绑定 :直接把事件写在标签元素中
<div id="div" onclick="foo(this)">click</div>
<script>
function foo(self){ // 形参不能是this;
console.log("foo函数");
console.log(self);
}
</script>
- 动态绑定:在js中通过代码获取元素对象,然后给这个对象进行后续绑定
<p id="i1">试一试!</p>
<script>
var ele=document.getElementById("i1");
ele.onclick=function(){
console.log("ok");
console.log(this); // this直接用
};
</script>
一个元素本身可以绑定多个不同的事件, 但是如果多次绑定同一个事件,则后面的事件代码会覆盖前面的事件代码
多个标签绑定事件
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
<li>555</li>
</ul>
<script>
var eles = document.querySelectorAll("ul li");
for(var i=0;i<eles.length;i++){
eles[i].onclick = function (){
console.log(this.innerHTML)
// console.log(eles[i].innerHTML) // 结果?
}
}
</script>
4.13.3、操作标签
<标签名 属性1=“属性值1” 属性2=“属性值2”……>文本</标签名>
- 文本操作
<div class="c1"><span>click</span></div>
<script>
var ele =document.querySelector(".c1");
ele.onclick = function (){
// 查看标签文本
console.log(this.innerHTML)
console.log(this.innerText)
}
ele.ondblclick = function (){
// 设置标签文本
this.innerHTML = "<a href='#'>yuan</a>"
//this.innerText = "<a href='#'>yuan</a>"
}
</script>
- value操作
像input标签,select标签以及textarea标签是没有文本的,但是显示内容由value属性决定
<input type="text" id="i1" value="yuan">
<textarea name="" id="i2" cols="30" rows="10">123</textarea>
<select id="i3">
<option value="hebei">河北省</option>
<option value="hubei">湖北省</option>
<option value="guangdong">广东省</option>
</select>
<script>
// input标签
var ele1 =document.getElementById("i1");
console.log(ele1.value);
ele1.onmouseover = function (){
this.value = "alvin"
}
// textarea标签
var ele2 =document.getElementById("i2");
console.log(ele2.value);
ele2.onmouseover = function (){
this.innerText = "welcome to JS world!"
this.value = "welcome to JS world!"
}
// select标签
var ele3 =document.getElementById("i3");
console.log(ele3.value);
ele3.value= "hubei"
</script>
- css样式操作
<p id="i1">Hello world!</p>
<script>
var ele = document.getElementById("i1");
ele.onclick = function (){
this.style.color = "red"
}
</script>
- 属性操作
elementNode.setAttribute("属性名","属性值")
elementNode.getAttribute("属性名")
elementNode.removeAttribute("属性名");
并不是所有属性都可以像value那样操作。
- class属性操作
elementNode.className
elementNode.classList.add
elementNode.classList.remove
案例:tab切换
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
}
.tab{
width: 800px;
height: 300px;
/*border: 1px solid red;*/
margin: 200px auto;
}
.tab ul{
list-style: none;
}
.tab-title{
background-color: #f7f7f7;
border: 1px solid #eee;
border-bottom: 1px solid #e4393c;
}
.tab .tab-title li{
display: inline-block;
padding: 10px 25px;
font-size: 14px;
}
li.current {
background-color: #e4393c;
color: #fff;
cursor: default;
}
.hide{
display: none;
}
</style>
</head>
<body>
<div class="tab">
<ul class="tab-title">
<li class="current" index="0">商品介绍</li>
<li class="" index="1">规格与包装</li>
<li class="" index="2">售后保障</li>
<li class="" index="3">商品评价</li>
</ul>
<ul class="tab-content">
<li>商品介绍...</li>
<li class="hide">规格与包装...</li>
<li class="hide">售后保障...</li>
<li class="hide">商品评价...</li>
</ul>
</div>
<script>
var titles = document.querySelectorAll(".tab-title li");
var contents = document.querySelectorAll(".tab-content li");
for (var i = 0;i<titles.length;i++){
titles[i].onclick = function () {
// (1) 触发事件标签拥有current样式
for (var j = 0;j<titles.length;j++){
titles[j].classList.remove("current")
}
console.log(this);
this.classList.add("current");
// (2) 显示点击title对应的详情内容
var index = this.getAttribute("index");
// console.log(this.getAttribute("index"));
// console.log(contents[index]);
for (var z = 0;z<contents.length;z++){
contents[z].classList.add("hide");
}
contents[index].classList.remove("hide");
}
}
</script>
</body>
</html>
- 节点操作
// 创建节点:
document.createElement("标签名")
// 插入节点
somenode.appendChild(newnode) // 追加一个子节点(作为最后的子节点)
somenode.insertBefore(newnode,某个节点) // 把增加的节点放到某个节点的前边
// 删除节点
somenode.removeChild():获得要删除的元素,通过父元素调用删除
//替换节点
somenode.replaceChild(newnode, 某个节点);
案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button class="add_btn">添加节点</button>
<button class="del_btn">删除节点</button>
<button class="replace_btn">替换节点</button>
<div class="c1">
<h3>hello JS!</h3>
<h3 class="c2">hello world</h3>
</div>
<script>
var add_btn = document.querySelector(".add_btn");
var del_btn = document.querySelector(".del_btn");
var replace_btn = document.querySelector(".replace_btn");
var c1 = document.querySelector(".c1");
var c2 = document.querySelector(".c2");
add_btn.onclick = function () {
// 创建节点
var ele = document.createElement("img"); // <img>
ele.src = "https://img2.baidu.com/it/u=1613029778,1507777131&fm=26&fmt=auto&gp=0.jpg"
console.log(ele);
// 添加节点
// c1.appendChild(ele);
c1.insertBefore(ele, c2)
};
del_btn.onclick = function () {
// 删除子节点
c1.removeChild(c2);
};
replace_btn.onclick = function () {
// 创建替换节点
var ele = document.createElement("img"); // <img>
ele.src = "https://img2.baidu.com/it/u=1613029778,1507777131&fm=26&fmt=auto&gp=0.jpg"
console.log(ele);
// 替换节点
c1.replaceChild(ele, c2);
}
</script>
</body>
</html>
4.13.4、常用事件
- onload事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
window.onload = function (){
ele = document.getElementById("i1")
console.log(ele.innerHTML);
}
</script>
</head>
<body>
<div id="i1">yuan</div>
</body>
</html>
- onsubmit事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" id="i1">
用户名:<input type="text">
密码: <input type="password">
<input type="submit">
</form>
<script>
var ele = document.getElementById("i1");
var user = document.querySelector("#i1 [type=text]")
var pwd = document.querySelector("#i1 [type=password]")
ele.onsubmit = function (e){
console.log(user.value);
console.log(pwd.value);
return false; // 终止事件执行
// e.preventDefault() // 阻止元素默认行为
}
</script>
</body>
</html>
- onchange事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<select name="provonce" id="s1">
<option value="hebei">请选择省份</option>
<option value="hubei">湖北省</option>
<option value="hunan">湖南省</option>
<option value="hebei">河北省</option>
</select>
<select name="provonce" id="s2">
<option value="hebei">请选择城市</option>
</select>
<script>
var data={"hunan":["长沙","岳阳","张家界"],"hubei":["武汉","襄阳","荆州"],"hebei":["石家庄","保定","张家口"]};
console.log(data);
var ele = document.getElementById("s1");
var ele2 = document.getElementById("s2");
ele.onchange=function () {
console.log(this.value);
var citys = data[this.value];
console.log(citys);
// 清空操作
ele2.options.length=1;
// 创建标签
for (var i=0;i<citys.length;i++){
var option = document.createElement("option"); // </option></option>
option.innerHTML=citys[i];
ele2.appendChild(option)
}
}
</script>
</body>
</html>
- onmouse事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#container{
width: 300px;
}
#title{
cursor: pointer;
background: #ccc;
}
#list{
display: none;
background:#fff;
}
#list div{
line-height: 50px;
}
#list .item1{
background-color: green;
}
#list .item2{
background-color: rebeccapurple;
}
#list .item3{
background-color: lemonchiffon;
}
</style>
</head>
<body>
<div id="container">
<div id="title">使用了mouseout事件↓</div>
<div id="list">
<div class="item1">第一行</div>
<div class="item2">第二行</div>
<div class="item3">第三行</div>
</div>
</div>
<script>
// 1.不论鼠标指针离开被选元素还是任何子元素,都会触发 mouseout 事件。
// 2.只有在鼠标指针离开被选元素时,才会触发 mouseleave 事件。
var container=document.getElementById("container");
var title=document.getElementById("title");
var list=document.getElementById("list");
title.onmouseover=function(){
list.style.display="block";
};
container.onmouseleave=function(){ // 改为onmouseout试一下
list.style.display="none";
};
/*
因为mouseout事件是会冒泡的,也就是onmouseout事件可能被同时绑定到了container的子元素title和list
上,所以鼠标移出每个子元素时也都会触发我们的list.style.display="none";
*/
</script>
</body>
</html>
- onkey事件
<input type="text" id="t1"/>
<script type="text/javascript">
var ele=document.getElementById("t1");
ele.onkeydown=function(e){
console.log("onkeydown",e.key)
};
ele.onkeyup=function(e){
console.log("onkeyup",e.key)
};
</script>
- onblur和onfocus事件
<input type="text" class="c1">
<script>
var ele = document.querySelector(".c1");
// 获取焦点事件
ele.onfocus = function () {
console.log("in")
};
// 失去焦点事件
ele.onblur = function () {
console.log("out")
}
</script>
- 冒泡事件
<div class="c1">
<div class="c2"></div>
</div>
<script>
var ele1 = document.querySelector(".c1");
ele1.onclick = function () {
alert("c1区域")
};
var ele2 = document.querySelector(".c2");
ele2.onclick = function (event) {
alert("c2区域");
// 如何阻止事件冒泡
event.stopPropagation();
}
</script>
4.14、总结与作业
- 总结
- 作业
第5章 、jQuery
5.1、jQuery介绍
- jQuery是什么
jQuery是一个快速、简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(或JavaScript框架)。jQuery设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。
jQuery的核心特性可以总结为:具有独特的链式语法和短小清晰的多功能接口;具有高效灵活的css选择器,并且可对CSS选择器进行扩展;拥有便捷的插件扩展机制和丰富的插件。jQuery兼容各种主流浏览器,如IE 6.0+、FF 1.5+、Safari 2.0+、Opera 9.0+等
- jQuery的版本
目前在市场上, 1.x , 2.x, 3.x 功能的完善在1.x, 2.x的时候是属于删除旧代码,去除对于旧的浏览器兼容代码。3.x的时候增加es的新特性以及调整核心代码的结构
- jQuery的引入
根本上jquery就是一个写好的js文件,所以想要使用jQuery的语法必须先引入到本地
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
5.2、jQuery的选择器
5.2.1、直接查找
- 基本选择器
/*
#id # id选择符
element # 元素选择符
.class # class选择符
selector1, selector2, selectorN # 同时获取多个元素的选择符
$("#id")
$(".class")
$("element")
$(".class,p,div")
*/
- 组合选择器
/*
ancestor descendant // 包含选择符
parent > child // 父子选择符
prev + next // 下一个兄弟选择符
prev ~ siblings // 兄弟选择符
$(".outer div")
$(".outer>div")
$(".outer+div")
$(".outer~div")
*/
- 属性选择器
/*
[attribute=value] // 获取拥有指定数据attribute,并且置为value的元素
$('[type="checked"]')
$('[class*="xxx"]')
*/
- 表单选择器
/*
$("[type='text']")----->$(":text") 注意只适用于input标签 : $("input:checked")
同样适用表单的以下属性
:enabled
:disabled
:checked
:selected
*/
- 筛选器
/*
// 筛选器
:first // 从已经获取的元素集合中提取第一个元素
:even // 从已经获取的元素集合中提取下标为偶数的元素
:odd // 从已经获取的元素集合中提取下标为奇数的元素
:eq(index) // 从已经获取的元素集合中提取指定下标index对应的元素
:gt(index) // 从已经获取的元素集合中提取下标大于index对应的元素
:last // 从已经获取的元素集合中提取最后一个元素
:lt(index) // 从已经获取的元素集合中提取下标小于index对应的元素
:first-child // 从已经获取的所有元素中提取他们的第一个子元素
:last-child // 从已经获取的所有元素中提取他们的最后一个子元素
:nth-child // 从已经获取的所有元素中提取他们的指定下标的子元素
// 筛选器方法
$().first() // 从已经获取的元素集合中提取第一个元素
$().last() // 从已经获取的元素集合中提取最后一个元素
$().eq() // 从已经获取的元素集合中提取指定下标index对应的元素
*/
5.2.2、导航查找
/*
// 查找子代标签:
$("div").children(".test")
$("div").find(".test")
// 向下查找兄弟标签
$(".test").next()
$(".test").nextAll()
$(".test").nextUntil()
// 向上查找兄弟标签
$("div").prev()
$("div").prevAll()
$("div").prevUntil()
// 查找所有兄弟标签
$("div").siblings()
// 查找父标签:
$(".test").parent()
$(".test").parents()
$(".test").parentUntil()
*/
5.3、jQuery的事件绑定
/*
三种用法:
1. on 和 off
// 绑定事件
$().on("事件名",匿名函数)
// 解绑事件,给指定元素解除事件的绑定
$().off("事件名")
2. 直接通过事件名来进行调用
$().事件名(匿名函数)
3. 组合事件,模拟事件
$().ready(匿名函数) // 入口函数
$().hover(mouseover, mouseout) // 是onmouseover和 onmouseout的组合
$().trigger(事件名) // 用于让js自动触发指定元素身上已经绑定的事件
*/
1、绑定多个标签
2、$(this)
案例1:绑定取消事件
<p>限制每次一个按钮只能投票3次</p>
<button id="zan">点下赞(<span>10</span>)</button>
<script>
let zan = 0;
$("#zan").click(function(){
$("#zan span").text(function(){
zan++;
let ret = parseInt($(this).text())+1;
if(zan >= 3){
$("#zan").off("click"); // 事件解绑
}
return ret;
});
})
</script>
案例2:模拟事件触发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jquery-1.11.0.js"></script>
<style>
input[type=file]{
display: none;
}
.upload{
width: 180px;
height: 44px;
border-radius: 5px;
color: #fff;
text-align: center;
line-height: 44px;
background-color: #000000;
border: none;
outline: none;
cursor: pointer;
}
</style>
</head>
<body>
<input type="file" name="avatar">
<button class="upload">上传文件</button>
<script>
$(".upload").on("click", function(){
$("input[type=file]").trigger("click"); // 模拟事件的触发
});
</script>
</body>
</html>
5.4、jQuery的操作标签
- 文本操作
/*
$("选择符").html() // 读取指定元素的内容,如果$()函数获取了有多个元素,则提取第一个元素
$("选择符").html(内容) // 修改内容,如果$()函数获取了多个元素, 则批量修改内容
$("选择符").text() // 效果同上,但是获取的内容是纯文本,不包含html代码
$("选择符").text(内容) // 效果同上,但是修改的内容中如果有html文本,在直接转成实体字符,而不是html代码
*/
- value操作
$().val()
- 属性操作
/*
//读取属性值
$("选择符").attr("属性名"); // 获取非表单元素的属性值,只会提取第一个元素的属性值
$("选择符").prop("属性名"); // 表单元素的属性,只会提取第一个元素的属性值
//操作属性
$("选择符").attr("属性名","属性值"); // 修改非表单元素的属性值,如果元素有多个,则全部修改
$("选择符").prop("属性名","属性值"); // 修改表单元素的属性值,如果元素有多个,则全部修改
$("选择符").attr({'属性名1':'属性值1','属性名2':'属性值2',.....})
$("选择符").prop({'属性名1':'属性值1','属性名2':'属性值2',.....})
*/
全选反选案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="jquery3.6.js"></script>
</head>
<body>
<button class="select_all">全选</button>
<button class="cancel">取消</button>
<button class="reverse">反选</button>
<hr>
<table border="1">
<tr>
<td>选择</td>
<td>姓名</td>
<td>年龄</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>张三</td>
<td>23</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>李四</td>
<td>23</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>王五</td>
<td>23</td>
</tr>
</table>
<script>
$(".select_all").click(function () {
$("table input:checkbox").prop("checked",true);
});
$(".cancel").click(function () {
$("table input:checkbox").prop("checked",false);
});
$(".reverse").click(function () {
$("table input:checkbox").each(function () {
$(this).prop("checked",!$(this).prop("checked"))
})
});
</script>
</body>
</html>
- css样式操作
/*
获取样式
$().css("样式属性"); // 获取元素的指定样式属性的值,如果有多个元素,只得到第一个元素的值
操作样式
$().css("样式属性","样式值").css("样式属性","样式值");
$().css({"样式属性1":"样式值1","样式属性2":"样式值2",....})
$().css("样式属性":function(){
// 其他代码操作
return "样式值";
});
*/
- class 属性操作
$().addClass("class1 class2 ... ...") // 给获取到的所有元素添加指定class样式
$().removeClass() // 给获取到的所有元素删除指定class样式
$().toggleClass() // 给获取到的所有元素进行判断,如果拥有指定class样式的则删除,如果没有指定样式则添加
tab切换案例jQuery版本:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
}
.tab{
width: 800px;
height: 300px;
/*border: 1px solid rebeccapurple;*/
margin: 200px auto;
}
.tab ul{
list-style: none;
}
.tab ul li{
display: inline-block;
}
.tab_title {
background-color: #f7f7f7;
border: 1px solid #eee;
border-bottom: 1px solid #e4393c;
}
.tab .tab_title li{
padding: 10px 25px;
font-size: 14px;
}
.tab .tab_title li.current{
background-color: #e4393c;
color: #fff;
cursor: default;
}
.tab_con li.hide{
display: none;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
</head>
<body>
<div class="tab">
<ul class="tab_title">
<li class="current">商品介绍</li>
<li>规格与包装</li>
<li>售后保障</li>
<li>商品评论</li>
</ul>
<ul class="tab_con">
<li>商品介绍...</li>
<li class="hide">规格与包装...</li>
<li class="hide">售后保障...</li>
<li class="hide">商品评论...</li>
</ul>
</div>
<script>
// jQuery
$(".tab_title li").click(function (){
// current样式
$(this).addClass("current").siblings().removeClass('current');
// hide样式
$(".tab_con li").eq($(this).index()).removeClass("hide").siblings().addClass("hide")
})
</script>
</body>
</html>
- 节点操作
/*
//创建一个jquery标签对象
$("<p>")
//内部插入
$("").append(content|fn) // $("p").append("<b>Hello</b>");
$("").appendTo(content) // $("p").appendTo("div");
$("").prepend(content|fn) // $("p").prepend("<b>Hello</b>");
$("").prependTo(content) // $("p").prependTo("#foo");
//外部插入
$("").after(content|fn) // ("p").after("<b>Hello</b>");
$("").before(content|fn) // $("p").before("<b>Hello</b>");
$("").insertAfter(content) // $("p").insertAfter("#foo");
$("").insertBefore(content) // $("p").insertBefore("#foo");
//替换
$("").replaceWith(content|fn) // $("p").replaceWith("<b>Paragraph. </b>");
//删除
$("").empty()
$("").remove([expr])
//复制
$("").clone([Even[,deepEven]])
*/
案例1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="jquery3.6.js"></script>
</head>
<body>
<button class="add_btn">添加节点</button>
<button class="del_btn">删除节点</button>
<button class="replace_btn">替换节点</button>
<div class="c1">
<h3>hello JS!</h3>
<h3 class="c2">hello world</h3>
</div>
<script>
$(".add_btn").click(function () {
// 创建jquery对象
// var $img = $("<img>");
// $img.attr("src","https://img1.baidu.com/it/u=3210260546,3888404253&fm=26&fmt=auto&gp=0.jpg")
// 节点添加
// $(".c1").append($img);
// $img.appendTo(".c1")
// $(".c1").prepend($img);
// $(".c2").before($img);
// 支持字符串操作
$(".c1").append("<img src ='https://img1.baidu.com/it/u=3210260546,3888404253&fm=26&fmt=auto&gp=0.jpg'>")
});
$(".del_btn").click(function () {
$(".c2").remove();
// $(".c2").empty();
});
$(".replace_btn").click(function () {
// alert(123);
// var $img = $("<img>");
// $img.attr("src","https://img1.baidu.com/it/u=3210260546,3888404253&fm=26&fmt=auto&gp=0.jpg")
// $(".c2").replaceWith($img);
$(".c2").replaceWith("<img src ='https://img1.baidu.com/it/u=3210260546,3888404253&fm=26&fmt=auto&gp=0.jpg'>");
})
</script>
</body>
</html>
案例2:clone案例
<div class="outer">
<div class="item">
<input type="button" value="+" class="add">
<input type="text">
</div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script>
$(".add").click(function () {
var $clone=$(this).parent().clone()
$clone.children(".add").attr({"value":"-","class":"rem"})
$(".outer").append($clone);
});
$('.rem').click(function () {
$(this).parent().remove()
});
// 事件委派
$(".outer").on("click",".item .rem",function () {
$(this).parent().remove()
})
</script>
- css尺寸
/*
$("").height([val|fn])
$("").width([val|fn])
$("").innerHeight()
$("").innerWidth()
$("").outerHeight([soptions])
$("").outerWidth([options])
*/
- css位置
/*
$("").offset([coordinates]) // 获取匹配元素在当前视口的相对偏移。
$("").position() // 获取匹配元素相对父元素的偏移,position()函数无法用于设置操作。
$("").scrollTop([val]) // 获取匹配元素相对滚动条顶部的偏移。
*/
案例1:返回顶部
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
}
.content{
height: 2000px;
background-color: lightgray;
}
.return_top{
width: 120px;
height: 50px;
background-color: lightseagreen;
color: white;
text-align: center;
line-height: 50px;
position: fixed;
bottom: 20px;
right: 20px;
}
.hide{
display: none;
}
</style>
<script src="jquery3.6.js"></script>
</head>
<body>
<div class="content">
<h3>文章...</h3>
</div>
<div class="return_top hide">返回顶部</div>
<script>
console.log($(window).scrollTop());
$(".return_top").click(function () {
$(window).scrollTop(0)
});
$(window).scroll(function () {
console.log($(this).scrollTop());
var v =$(this).scrollTop();
if (v > 100){
$(".return_top").removeClass("hide");
}else {
$(".return_top").addClass("hide");
}
})
</script>
</body>
</html>
案例2:位置偏移
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
}
.box{
width: 200px;
height: 200px;
background-color: orange;
}
.parent_box{
width: 800px;
height: 500px;
margin: 200px auto;
border: 1px solid rebeccapurple;
}
</style>
</head>
<body>
<button class="btn1">offset</button>
<div class="parent_box">
<div class="box"></div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script>
var $offset=$(".box").offset();
var $left=$offset.left;
var $top=$offset.top;
console.log("$offset","top:"+$top+" left:"+$left)
var $position=$(".box").position();
var $left=$position.left;
var $top=$position.top;
console.log("$position","top:"+$top+" left:"+$left);
$(".btn1").click(function () {
$(".box").offset({left:50,top:50})
});
</script>
</body>
</html>
5.5、jQuery的动画
/*
//基本
show([s,[e],[fn]]) 显示元素
hide([s,[e],[fn]]) 隐藏元素
//滑动
slideDown([s],[e],[fn]) 向下滑动
slideUp([s,[e],[fn]]) 向上滑动
//淡入淡出
fadeIn([s],[e],[fn]) 淡入
fadeOut([s],[e],[fn]) 淡出
fadeTo([[s],opacity,[e],[fn]]) 让元素的透明度调整到指定数值
//自定义
animate(p,[s],[e],[fn]) 自定义动画
stop([c],[j]) 暂停上一个动画效果,开始当前触发的动画效果
*/
案例1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.c1{
width: 250px;
height: 250px;
background-color: black;
}
.hide{
display: none;
}
</style>
<script src="jquery3.6.js"></script>
</head>
<body>
<p>
<button class="show01">显示</button>
<button class="hide01">隐藏</button>
</p>
<p>
<button class="show02">显示</button>
<button class="hide02">隐藏</button>
</p>
<p>
<button class="show03">显示</button>
<button class="hide03">隐藏</button>
</p>
<p>
<button class="show04">显示</button>
<button class="hide04">隐藏</button>
</p>
<hr>
<div class="c1"></div>
<script>
// 自己实现的隐藏与显示
$(".show01").click(function () {
$(".c1").removeClass("hide")
});
$(".hide01").click(function () {
$(".c1").addClass("hide")
});
// (1) show与hide方法
$(".show02").click(function () {
$(".c1").show(1000,function () {
alert("显示成功")
});
});
$(".hide02").click(function () {
$(".c1").hide(1000,function () {
alert("隐藏成功")
})
});
// (2) slideDown与slideUp
$(".show03").click(function () {
$(".c1").slideDown(1000,function () {
alert("显示成功")
});
});
$(".hide03").click(function () {
$(".c1").slideUp(1000,function () {
alert("隐藏成功")
})
});
// (3) fadeIn与fadeOut
$(".show04").click(function () {
$(".c1").fadeIn(1000,function () {
alert("显示成功")
});
});
$(".hide04").click(function () {
$(".c1").fadeOut(1000,function () {
alert("隐藏成功")
})
});
</script>
</body>
</html>
案例2:
//$(".box").animate(动画最终效果,动画完成的时间,动画完成以后的回调函数)
$(".animate").click(function () {
$(".c1").animate({
"border-radius":"50%",
"top":340,
"left":200
},1000,function () {
$(".c1").animate({
"border-radius":"0",
"top":240,
"left":120
},1000,function () {
$(".animate").trigger("click")
})
})
})
5.6、扩展方法 (插件机制)
- jQuery.extend(object)
扩展jQuery对象本身。
用来在jQuery命名空间上增加新函数。
在jQuery命名空间上增加两个函数:
<script>
jQuery.extend({
min: function(a, b) { return a < b ? a : b; },
max: function(a, b) { return a > b ? a : b; }
});
jQuery.min(2,3); // => 2
jQuery.max(4,5); // => 5
</script>
- jQuery.fn.extend(object)
扩展 jQuery 元素集来提供新的方法(通常用来制作插件)
增加两个插件方法:
<body>
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
<script src="jquery.min.js"></script>
<script>
jQuery.fn.extend({
check: function() {
$(this).attr("checked",true);
},
uncheck: function() {
$(this).attr("checked",false);
}
});
$(":checkbox").check()
</script>
</body>
第五篇 web框架(django)
第1章、Django环境安装
1.1、Django下载
Python下有许多款不同的 Web 框架。Django是重量级选手中最有代表性的一位。许多成功的网站和APP都基于Django。Django 是一个开放源代码的 Web 应用框架,由 Python 写成。Django ,初次发布于 2005 年 7 月, 并于 2008 年 9 月发布了第一个正式版本 1.0 。Django 采用了 MVT 的软件设计模式,即模型(Model),视图(View)和模板(Template)。
目前我们学习和使用的版本是3.2LTS版本
官网: http://www.djangoproject.com
文档:https://docs.djangoproject.com/zh-hans/3.2/
在本地安装
pip install django
pip install django==3.2
pip源:
https://pypi.douban.com/simple/ 豆瓣源
https://pypi.tuna.tsinghua.edu.cn/simple 清华源
使用格式:
pip install django -i https://pypi.douban.com/simple/
1.2、MTV模型
这个MTV模式并非django首创,在其他的语言里面也有类似的设计模式MVC,甚至可以说django里面的MVT事实上是借鉴了MVC模式衍生出来的。
M,Model,模型,是一个类或者函数,里面的代码就是用于完成操作数据库的。
V,View,视图,里面的代码就是用于展示给客户端的页面效果。可以是一个具有特殊语法的HTML文件,也可以是一个数据结构。
C,Controller,控制器,是一个类或者函数,里面的代码就是用于项目功能逻辑的,一般用于调用模型来获取数据,获取到的数据通过调用视图文件返回给客户端。
第2章、创建django项目并运行
创建虚拟环境并在虚拟环境中下载安装django包
pip install django==3.2 -i https://pypi.douban.com/simple/
cd ~/Desktop
django-admin startproject demo
完成了以后,直接直接下pycharm下面的终端terminal中使用命令运行django
python manage.py runserver 8090
在浏览器中访问显示的地址http://127.0.0.1:8090
.效果如下则表示正确安装了.
2.1、目录结构
│─ manage.py # 终端脚本命令,提供了一系列用于生成文件或者目录的命令,也叫脚手架
└─ dome/ # 主应用开发目录,保存了项目中的所有开发人员编写的代码, 目录是生成项目时指定的
│- asgi.py # django3.0以后新增的,用于让django运行在异步编程模式的一个web应用对象
│- settings.py # 默认开发配置文件
│- urls.py # 路由列表目录,用于绑定视图和url的映射关系
│- wsgi.py # wsgi就是项目运行在wsgi服务器时的入口文件,相当于manage.py
└- __init__.py
2.2、快速使用django
在django中要提供数据展示给用户,我们需要完成3个步骤.
需求:利用Django实现一个查看当前时间的web页面。
基于MTV模型,设计步骤如下:
- step1:在urls.py中设计url与视图的映射关系。
- step2:创建子应用,在views.py中构建视图函数。
- step3:将变量嵌入到模板中返回客户端。
(1)创建子应用
python manage.py startapp 子应用名称
子应用的名称将来会作为目录名而存在,所以不能出现特殊符号,不能出现中文等多字节的字符.
(2) 绑定路由
demo/urls.py
代码:
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path("timer", views.timer),
]
(3)在子应用的视图文件中编写视图函数
home/view.py
,代码:
from django.shortcuts import render,HttpResponse
# Create your views here.
import datetime
def timer(request):
now=datetime.datetime.now().strftime("%Y-%m-%d %X")
#return HttpResponse(now)
return render(request,"timer.html",{"now":now})
(4)在templates中构建模板:timer.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>当前时间:{{ now }}</p>
</body>
</html>
因为上面我们绑定index视图函数的url地址是index,所以我们可以通过http://127.0.0.1:8000/
拼接url地址index
来访问视图函数
注意:如果出现问题,则考虑配置文件的配置
setting配置文件配置
在settings配置文件里面有关于templates(放html文件的配置):
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')] #有些版本的django没有写这个,自己写一下,就是配置一个django找html文件的路径,render方法就来这里找html文件
,
'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',
],
},
},
]
settings配置文件里面将这一行注释掉,这是django给你加的一个csrf的认证,现在不需要
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',
]
settings配置static静态文件路径:
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
第3章、路由控制器
Route路由, 是一种映射关系!路由是把客户端请求的url地址和用户请求的应用程序,这里意指django里面的视图进行一对一绑定映射的一种关系。
在django中所有的路由最终都被保存到一个变量 urlpatterns.
, urlpatterns必须声明在主应用下的urls.py总路由中。这是由配置文件settings设置的。
在django运行中,当客户端发送了一个http请求到服务端,服务端的web服务器则会从http协议中提取url地址, 从程序内部找到项目中添加到urlpatterns里面的所有路由信息的url进行遍历匹配。如果相等或者匹配成功,则调用当前url对象的视图方法。
在给urlpatterns路由列表添加路由的过程中,django一共提供了1个函数给开发者注册路由.
from django.urls import path # 字符串路由
基本使用
path('timer', views.timer),
第4章、视图
django的视图主要有2种,分别是函数视图和类视图.现在刚开始学习django,我们先学习函数视图(FBV),后面再学习类视图[CBV].
4.1、请求方式
web项目运行在http协议下,默认肯定也支持用户通过不同的http请求发送数据来.
常用的http请求:
POST
GET
django支持让客户端只能通过指定的Http请求来访问到项目的视图
xxx/views.py
,代码:
# 让用户发送POST才能访问的内容
from django.views.decorators.http import require_http_methods
@require_http_methods(["POST"])
def login(request):
return HttpResponse("登录成功!")
或者:
# 让用户发送POST才能访问的内容
def login(request):
if request.method == 'POST':
return HttpResponse("登录成功!")
路由绑定,demo/urls.py
,代码:
from django.contrib import admin
from django.urls import path
from home.views import index
urlpatterns = [
path('admin/', admin.site.urls),
path("index", index),
path("login", login),
]
通过浏览器,访问效果http://127.0.0.1:8090/login
:
4.2、请求对象
django将请求报文中的请求行、首部信息、内容主体封装成 HttpRequest 类中的属性。 除了特殊说明的之外,其他均为只读的。
(1)请求方式
print(request.method)
(2)请求数据
# 1.HttpRequest.GET:一个类似于字典的对象,包含 HTTP GET 的所有参数。详情请参考 QueryDict 对象。
#注意可以调用get函数获取GET返回对象中指定参数的值:例如get('username')
# 2.HttpRequest.POST:一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成 QueryDict 对象。
# 注意:键值对的值是多个的时候,比如checkbox类型的input标签,select标签,需要用: request.POST.getlist("hobby")
# 3.HttpRequest.body:一个字符串,代表请求报文的请求体的原数据。
#4.HttpRequest.COOKIES:一个标准的Python 字典,包含所有的cookie。键和值都为字符串。
(3)请求路径
# HttpRequest.path:表示请求的路径组件(不含get参数)
# HttpRequest.get_full_path():含参数路径
(4)请求头
- HttpRequest.META:一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器
具体的头部信息取决于客户端和服务器,下面是一些示例:
CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
CONTENT_TYPE —— 请求的正文的MIME 类型。
HTTP_ACCEPT —— 响应可接收的Content-Type。
HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
HTTP_HOST —— 客服端发送的HTTP Host 头部。
HTTP_REFERER —— Referring 页面。
HTTP_USER_AGENT —— 客户端的user-agent 字符串。
QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
REMOTE_ADDR —— 客户端的IP 地址。
REMOTE_HOST —— 客户端的主机名。
REMOTE_USER —— 服务器认证后的用户。
REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。
SERVER_NAME —— 服务器的主机名。
SERVER_PORT —— 服务器的端口(是一个字符串)。
4.3、响应对象
我们写的每个视图都需要实例化,填充和返回一个HttpResponse。HttpResponse类位于django.http模块中。
响应对象主要有三种形式:
- HttpResponse()
- render()
- redirect()
(1)HttpResponse()
(1)render()
render(request, template_name[, context])
#结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。
参数:
/*
request: 用于生成响应的请求对象。
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典,
默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
*/
render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体。
(2)redirect方法
当您使用Django框架构建Python Web应用程序时,您在某些时候必须将用户从一个URL重定向到另一个URL,
通过redirect方法实现重定向。
参数可以是:
- 一个绝对的或相对的URL, 将原封不动的作为重定向的位置.
- 一个url的别名: 可以使用reverse来反向解析url
传递要重定向到的一个具体的网址
def my_view(request):
...
return redirect("/some/url/")
当然也可以是一个完整的网址
def my_view(request):
...
return redirect("http://www.baidu.com")
传递一个视图的名称
def my_view(request):
...
return redirect(reverse("url的别名"))
第5章、模板语法
模板引擎是一种可以让开发者把服务端数据填充到html网页中完成渲染效果的技术。它实现了把前端代码和服务端代码分离的作用,让项目中的业务逻辑代码和数据表现代码分离,让前端开发者和服务端开发者可以更好的完成协同开发。
Django框架中内置了web开发领域非常出名的一个DjangoTemplate模板引擎(DTL)。DTL官方文档
要在django框架中使用模板引擎把视图中的数据更好的展示给客户端,需要完成3个步骤:
在项目配置文件中指定保存模板文件的模板目录。一般模板目录都是设置在项目根目录或者主应用目录下。
在视图中基于django提供的渲染函数绑定模板文件和需要展示的数据变量
在模板目录下创建对应的模板文件,并根据模板引擎内置的模板语法,填写输出视图传递过来的数据。
配置模板目录:在当前项目根目录下创建了模板目录templates. 然后在settings.py, 模板相关配置,找到TEMPLATES配置项,填写DIRS设置模板目录。
# 模板引擎配置
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',
],
},
},
]
5.1、简单案例
为了方便接下里的演示内容,我这里创建创建一个新的子应用tem
python manage.py startapp tem
settings.py,注册子应用,代码:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'tem', # 开发者创建的子应用,这填写就是子应用的导包路径
]
总路由加载子应用路由,urls.py
,代码:
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
# path("路由前缀/", include("子应用目录名.路由模块"))
path("book/", include("home.urls")),
path("student/", include("demo.students.urls",namespace="stu")),
path("users/", include("users.urls")),
path("tem/", include("tem.urls")),
]
在子应用目录下创建urls.py子路由文件,代码如下:
"""子应用路由"""
from django.urls import path, re_path
from . import views
urlpatterns = [
path("index", views.index),
]
tem.views.index,代码:
from django.shortcuts import render
def index(request):
# 要显示到客户端的数据
name = "hello DTL!"
# return render(request, "模板文件路径",context={字典格式:要在客户端中展示的数据})
return render(request, "index.html",context={"name":name})
templates.index.html,代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>来自模板的内容</h1>
<p>输出变量name的值:{{ name }}</p>
</body>
</html>
5.2、render函数内部本质
from django.shortcuts import render
from django.template.loader import get_template
from django.http.response import HttpResponse
def index(request):
name = "hello world!"
# 1. 初始化模板,读取模板内容,实例化模板对象
# get_template会从项目配置中找到模板目录,我们需要填写的参数就是补全模板文件的路径
template = get_template("index.html")
# 2. 识别context内容, 和模板内容里面的标记[标签]替换,针对复杂的内容,进行正则的替换
context = {"name": name}
content = template.render(context, request) # render中完成了变量替换成变量值的过程,这个过程使用了正则。
print(content)
# 3. 通过response响应对象,把替换了数据的模板内容返回给客户端
return HttpResponse(content)
# 上面代码的简写,直接使用 django.shortcuts.render
# return render(request, "index.html",context={"name":name})
# return render(request,"index3.html", locals())
# data = {}
# data["name"] = "xiaoming"
# data["message"] = "你好!"
# return render(request,"index3.html", data)
- DTL模板文件与普通html文件的区别在哪里?
- DTL模板文件是一种带有特殊语法的HTML文件,这个HTML文件可以被Django编译,可以传递参数进去,实现数据动态化。在编译完成后,生成一个普通的HTML文件,然后发送给客户端。
- 开发中,我们一般把开发中的文件分2种,分别是静态文件和动态文件。
* 静态文件,数据保存在当前文件,不需要经过任何处理就可以展示出去。普通html文件,图片,视频,音频等这一类文件叫静态文件。 * 动态文件,数据并不在当前文件,而是要经过服务端或其他程序进行编译转换才可以展示出去。 编译转换的过程往往就是使用正则或其他技术把文件内部具有特殊格式的变量转换成真实数据。 动态文件,一般数据会保存在第三方存储设备,如数据库中。django的视图,模板文件,就属于动态文件。
5.3、模板语法
5.3.1、变量渲染之深度查询
def index(request):
name = "root"
age = 13
sex = True
lve = ["swimming", "shopping", "coding", "game"]
bookinfo = {"id": 1, "price": 9.90, "name": "python3天入门到挣扎", }
book_list = [
{"id": 10, "price": 9.90, "name": "python3天入门到挣扎", },
{"id": 11, "price": 19.90, "name": "python7天入门到垂死挣扎", },
]
return render(request, 'index.html', locals())
模板代码,templates/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>name={{ name }}</p>
<p>{{ age }}</p>
<p>{{ sex }}</p>
<p>列表成员</p>
<p>{{ lve }}</p>
<p>{{ lve.0 }}</p>
<p>{{ lve | last }}</p>
<p>字典成员</p>
<p>id={{ bookinfo.id }}</p>
<p>price={{ bookinfo.price }}</p>
<p>name={{ bookinfo.name }}</p>
<p>复杂列表</p>
<p>{{ book_list.0.name }}</p>
<p>{{ book_list.1.name }}</p>
</body>
</html>
通过句点符号深度查询
tem.urls
,代码:
"""子应用路由"""
from django.urls import path, re_path
from . import views
urlpatterns = [
# ....
path("index", views.index),
]
5.3.2、变量渲染之内置过滤器
语法:
{{obj|过滤器名称:过滤器参数}}
内置过滤器
过滤器 | 用法 | 代码 |
---|---|---|
last | 获取列表/元组的最后一个成员 | {{liast | last}} |
first | 获取列表/元组的第一个成员 | {{list|first}} |
length | 获取数据的长度 | {{list | length}} |
defualt | 当变量没有值的情况下, 系统输出默认值, | {{str|default=“默认值”}} |
safe | 让系统不要对内容中的html代码进行实体转义 | {{htmlcontent| safe}} |
upper | 字母转换成大写 | {{str | upper}} |
lower | 字母转换成小写 | {{str | lower}} |
title | 每个单词首字母转换成大写 | {{str | title}} |
date | 日期时间格式转换 | `{{ value |
cut | 从内容中截取掉同样字符的内容 | {{content | cut:“hello”}} |
list | 把内容转换成列表格式 | {{content | list}} |
escape | 把内容中的HTML特殊符号转换成实体字符 | {{content | escape }} |
filesizeformat | 把文件大小的数值转换成单位表示 | {{filesize | filesizeformat}} |
join | 按指定字符拼接内容 | {{list| join(“-”)}} |
random | 随机提取某个成员 | {list | random}} |
slice | 按切片提取成员 | {{list | slice:“:-2”}} |
truncatechars | 按字符长度截取内容 | {{content | truncatechars:30}} |
truncatewords | 按单词长度截取内容 | 同上 |
过滤器的使用
视图代码 home.views.py;
def index(request):
"""过滤器 filters"""
content = "<a href='http://www.luffycity.com'>路飞学城</a>"
# content1 = '<script>alert(1);</script>'
from datetime import datetime
now = datetime.now()
content2= "hello wrold!"
return render(request,"index.html",locals())
# 模板代码,templates/index.html:
{{ content | safe }}
{{ content1 | safe }}
{# 过滤器本质就是函数,但是模板语法不支持小括号调用,所以需要使用:号分割参数 #}
<p>{{ now | date:"Y-m-d H:i:s" }}</p>
<p>{{ conten1 | default:"默认值" }}</p>
{# 一个数据可以连续调用多个过滤器 #}
<p>{{ content2 | truncatechars:6 | upper }}</p>
5.3.3、自定义过滤器
虽然官方已经提供了许多内置的过滤器给开发者,但是很明显,还是会有存在不足的时候。例如:希望输出用户的手机号码时,
13912345678 ---->> 139*****678
,这时我们就需要自定义过滤器。
要声明自定义过滤器并且能在模板中正常使用,需要完成2个前置的工作:
# 1. 当前使用和声明过滤器的子应用必须在setting.py配置文件中的INSTALLED_APPS中注册了!!!
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'home',
]
# 2. 自定义过滤器函数必须被 template.register进行装饰使用.
# 而且过滤器函数所在的模块必须在templatetags包里面保存
# 在home子应用下创建templatetags包[必须包含__init__.py], 在包目录下创建任意py文件
# home.templatetags.my_filters.py代码:
from django import template
register = template.Library()
# 自定义过滤器
@register.filter("mobile")
def mobile(content):
return content[:3]+"*****"+content[-3:]
# 3. 在需要使用的模板文件中顶部使用load标签加载过滤器文件my_filters.py并调用自定义过滤器
# home.views.py,代码:
def index(request):
"""自定义过滤器 filters"""
moblie_number = "13312345678"
return render(request,"index2.html",locals())
# templates/index2.html,代码:
{% load my_filters %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ moblie_number| mobile }}
</body>
</html>
5.3.4、标签
(1)if 标签
视图代码,tem.views.py:
def index(request):
name = "xiaoming"
age = 19
sex = True
lve = ["swimming", "shopping", "coding", "game"]
user_lve = "sleep"
bookinfo = {"id": 1, "price": 9.90, "name": "python3天入门到挣扎", }
book_list = [
{"id": 10, "price": 9.90, "name": "python3天入门到挣扎", },
{"id": 11, "price": 19.90, "name": "python7天入门到垂死挣扎", },
]
return render(request, 'index.html', locals())
模板代码,templates/index.html,代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{# 来自django模板引擎的注释~~~~ #}
{% comment %}
多行注释,comment中的所有内容全部都不会被显示出去
{% endcomment %}
{# {% if age < 18 %}#}
{# <p>你还没成年,不能访问我的网站!</p>#}
{# {% endif %}#}
{##}
{# {% if name == "root" %}#}
{# <p>超级用户,欢迎回家!</p>#}
{# {% else %}#}
{# <p>{{ name }},你好,欢迎来到xx网站!</p>#}
{# {% endif %}#}
{% if user_lve == lve.0 %}
<p>那么巧,你喜欢游泳,海里也能见到你~</p>
{% elif user_lve == lve.1 %}
<p>那么巧,你也来收快递呀?~</p>
{% elif user_lve == lve.2 %}
<p>那么巧,你也在老男孩?</p>
{% else %}
<p>看来我们没有缘分~</p>
{% endif %}
</body>
</html>
路由代码:
"""子应用路由"""
from django.urls import path, re_path
from . import views
urlpatterns = [
# ....
path("index", views.index),
]
(2)for标签
视图代码, home.views.py:
def index7(request):
book_list1 = [
{"id": 11, "name": "python基础入门", "price": 130.00},
{"id": 17, "name": "Go基础入门", "price": 230.00},
{"id": 23, "name": "PHP基础入门", "price": 330.00},
{"id": 44, "name": "Java基础入门", "price": 730.00},
{"id": 51, "name": "C++基础入门", "price": 300.00},
{"id": 56, "name": "C#基础入门", "price": 100.00},
{"id": 57, "name": "前段基础入门", "price": 380.00},
]
return render(request, 'index.html', locals())
template/index.html,代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table width="800" align="center" border="1">
<tr>
<td>序号</td>
<td>id</td>
<td>标题</td>
<td>价格</td>
</tr>
{# 多行编辑,alt+鼠标键,alt不要松开,左键点击要编辑的每一行 #}
{# {% for book in book_list1 %}#}
{# <tr>#}
{# <td>{{ book.id }}</td>#}
{# <td>{{ book.name }}</td>#}
{# <td>{{ book.price }}</td>#}
{# </tr>#}
{# {% endfor %}#}
{# 建议不要直接使用for循环一维字典,此处使用仅仅展示for嵌套for而已 #}
{# {% for book in book_list1 %}#}
{# <tr>#}
{# {% for field,value in book.items %}#}
{# <td>{{ field }} == {{ value }}</td>#}
{# {% endfor %}#}
{# </tr>#}
{# {% endfor %}#}
{# {% for book in book_list1 %}#}
{# <tr>#}
{# <td>{{ book.id }}</td>#}
{# <td>{{ book.name }}</td>#}
{# {% if book.price > 200 %}#}
{# <td bgcolor="#ff7f50">{{ book.price }}</td>#}
{# {% else %}#}
{# <td>{{ book.price }}</td>#}
{# {% endif %}#}
{# </tr>#}
{# {% endfor %}#}
{# 逆向循环数据 #}
{# {% for book in book_list1 reversed %}#}
{# <tr>#}
{# <td>{{ book.id }}</td>#}
{# <td>{{ book.name }}</td>#}
{# {% if book.price > 200 %}#}
{# <td bgcolor="#ff7f50">{{ book.price }}</td>#}
{# {% else %}#}
{# <td>{{ book.price }}</td>#}
{# {% endif %}#}
{# </tr>#}
{# {% endfor %}#}
{% for book in book_list1 %}
<tr>
{# <td>{{ forloop.counter }}</td>#}
{# <td>{{ forloop.counter0 }}</td>#}
{# <td>{{ forloop.revcounter }}</td>#}
{# <td>{{ forloop.revcounter0 }}</td>#}
{# <td>{{ forloop.first }}</td>#}
<td>{{ forloop.last }}</td>
<td>{{ book.id }}</td>
<td>{{ book.name }}</td>
{% if book.price > 200 %}
<td bgcolor="#ff7f50">{{ book.price }}</td>
{% else %}
<td>{{ book.price }}</td>
{% endif %}
</tr>
{% endfor %}
</table>
</body>
</html>
路由代码:
"""子应用路由"""
from django.urls import path, re_path
from . import views
urlpatterns = [
# ....
path("index", views.index),
]
循环中, 模板引擎提供的forloop对象,用于给开发者获取循环次数或者判断循环过程的.
属性 | 描述 |
---|---|
forloop.counter | 显示循环的次数,从1开始 |
forloop.counter0 | 显示循环的次数,从0开始 |
forloop.revcounter0 | 倒数显示循环的次数,从0开始 |
forloop.revcounter | 倒数显示循环的次数,从1开始 |
forloop.first | 判断如果本次是循环的第一次,则结果为True |
forloop.last | 判断如果本次是循环的最后一次,则结果为True |
forloop.parentloop | 在嵌套循环中,指向当前循环的上级循环 |
5.3.5、模板嵌套和继承
传统的模板分离技术,依靠{%include “模板文件名”%}实现,这种方式,虽然达到了页面代码复用的效果,但是由此也会带来大量的碎片化模板,导致维护模板的成本上升.因此, 大部分框架中除了提供这种模板分离技术以外,还并行的提供了 模板继承给开发者.
模板继承基本使用入门
(1) 继承父模板的公共内容
{% extends “base.html” %}
视图, home.views.py代码:
def index(request):
"""模板继承"""
return render(request,"index.html",locals())
子模板, templates/index.html
{% extends "base.html" %}
父模板, templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>base.html的头部</h1>
<h1>base.html的内容</h1>
<h1>base.html的脚部</h1>
</body>
</html>
(2) 个性展示不同于父模板的内容
{%block %} 独立内容 {%endblock%}
{{block.super}}
视图home.views.py, 代码:
def index(request):
"""模板继承"""
return render(request,"index.html",locals())
def home(request):
"""模板继承"""
return render(request,"home.html",locals())
路由 home.urls.py,代码:
from django.urls import path
from . import views
urlpatterns = [
path("", views.index),
path("home/", views.home),
]
子模板index.html,代码:
{% extends "base.html" %}
{% block title %}index3的标题{% endblock %}
{% block content %}
{{ block.super }} {# 父级模板同名block标签的内容 #}
<h1>index3.html的独立内容</h1>
{{ block.super }}
{% endblock %}
子模板home.html,代码:
{% extends "base.html" %}
{% block title %}home的标题{% endblock %}
父模板base.html,代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<h1>base.html的头部</h1>
{% block content %}
<h1>base.html的内容</h1>
{% endblock %}
<h1>base.html的脚部</h1>
</body>
</html>
- 如果你在模版中使用
{% extends %}
标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。- 在base模版中设置越多的
{% block %}
标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。- 为了更好的可读性,你也可以给你的
{% endblock %}
标签一个 名字 。例如:{``%
block content``%``}``...``{``%
endblock content``%``},
在大型模版中,这个方法帮你清楚的看到哪一个{% block %}
标签被关闭了。- 不能在一个模版中定义多个相同名字的
block
标签。
5.4、静态文件
开发中在开启了debug模式时,django可以通过配置,允许用户通过对应的url地址访问django的静态文件。
setting.py,代码:
STATIC_ROOT = BASE_DIR / 'static'
STATIC_URL = '/static/' # django模板中,可以引用{{STATIC_URL}}变量避免把路径写死。
总路由,urls.py,代码:
from django.views.static import serve as serve_static
urlpatterns = [
path('admin/', admin.site.urls),
# 对外提供访问静态文件的路由,serve_static 是django提供静态访问支持的映射类。依靠它,客户端才能访问到django的静态文件。
path(r'static/<path:path>', serve_static, {'document_root': settings.STATIC_ROOT},),
]
注意:项目上线以后,关闭debug模式时,django默认是不提供静态文件的访问支持,项目部署的时候,我们会通过收集静态文件使用nginx这种web服务器来提供静态文件的访问支持。
第6章、模型层(ORM)
O是object,也就类对象的意思。
R是relation,翻译成中文是关系,也就是关系数据库中数据表的意思。
M是mapping,是映射的意思。
ORM框架会帮我们把类对象和数据表进行了一对一的映射,让我们可以通过类对象来操作对应的数据表。
ORM框架还可以根据我们设计的类自动帮我们生成数据库中的表格,省去了我们自己建表的过程。
django中内嵌了ORM框架,不需要直接编写SQL语句进行数据库操作,而是通过定义模型类,操作模型类来完成对数据库中表的增删改查和创建等操作。
ORM的优点
数据模型类都在一个地方定义,更容易更新和维护,也利于重用代码。
ORM 有现成的工具,很多功能都可以自动完成,比如数据消除、预处理、事务等等。
它迫使你使用 MVC 架构,ORM 就是天然的 Model,最终使代码更清晰。
基于 ORM 的业务代码比较简单,代码量少,语义性好,容易理解。
新手对于复杂业务容易写出性能不佳的 SQL,有了ORM不必编写复杂的SQL语句, 只需要通过操作模型对象即可同步修改数据表中的数据.
开发中应用ORM将来如果要切换数据库.只需要切换ORM底层对接数据库的驱动【修改配置文件的连接地址即可】
ORM 也有缺点
- ORM 库不是轻量级工具,需要花很多精力学习和设置,甚至不同的框架,会存在不同操作的ORM。
- 对于复杂的业务查询,ORM表达起来比原生的SQL要更加困难和复杂。
- ORM操作数据库的性能要比使用原生的SQL差。
- ORM 抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的 SQL。【自己使用pymysql另外操作即可,用了ORM并不表示当前项目不能使用别的数据库操作工具了。】
我们可以通过以下步骤来使用django的数据库操作
1. 配置数据库连接信息
2. 在models.py中定义模型类
3. 生成数据库迁移文件并执行迁文件[注意:数据迁移是一个独立的功能,这个功能在其他web框架未必和ORM一块的]
4. 通过模型类对象提供的方法或属性完成数据表的增删改查操作
6.1、配置数据库连接
在settings.py中保存了数据库的连接配置信息,Django默认初始配置使用sqlite数据库。
-
使用MySQL数据库首先需要安装驱动程序
pip install PyMySQL
-
在Django的工程同名子目录的
__init__
.py文件中添加如下语句from pymysql import install_as_MySQLdb install_as_MySQLdb() # 让pymysql以MySQLDB的运行模式和Django的ORM对接运行
作用是让Django的ORM能以mysqldb的方式来调用PyMySQL。
-
修改DATABASES配置信息
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1', # 数据库主机 'PORT': 3306, # 数据库端口 'USER': 'root', # 数据库用户名 'PASSWORD': '123', # 数据库用户密码 'NAME': 'student' # 数据库名字 } }
-
在MySQL中创建数据库
create database student; # mysql8.0默认就是utf8mb4; create database student default charset=utf8mb4; # mysql8.0之前的版本
6.2、定义模型类
定义模型类
- 模型类被定义在"子应用/models.py"文件中。
- 模型类必须直接或者间接继承自django.db.models.Model类。
接下来以学生管理为例进行演示。[系统大概3-4表,学生信息,课程信息,老师信息],创建子应用student,注册子应用并引入子应用路由.
settings.py,代码:
INSTALLED_APPS = [
# ...
'student',
]
urls.py,总路由代码:
urlpatterns = [
# 省略,如果前面有重复的路由,改动以下。
path("student/", include("student.urls")),
]
在models.py 文件中定义模型类。
from django.db import models
from datetime import datetime
# 模型类必须要直接或者间接继承于 models.Model
class BaseModel(models.Model):
"""公共模型[公共方法和公共字段]"""
# created_time = models.IntegerField(default=0, verbose_name="创建时间")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
# auto_now_add 当数据添加时设置当前时间为默认值
# auto_now= 当数据添加/更新时, 设置当前时间为默认值
updated_time = models.DateTimeField(auto_now=True)
class Meta(object):
abstract = True # 设置当前模型为抽象模型, 当系统运行时, 不会认为这是一个数据表对应的模型.
class Student(BaseModel):
"""Student模型类"""
#1. 字段[数据库表字段对应]
SEX_CHOICES = (
(0,"女"),
(1,"男"),
(2,"保密"),
)
# 字段名 = models.数据类型(约束选项1,约束选项2, verbose_name="注释")
# SQL: id bigint primary_key auto_increment not null comment="主键",
# id = models.AutoField(primary_key=True, null=False, verbose_name="主键") # django会自动在创建数据表的时候生成id主键/还设置了一个调用别名 pk
# SQL: name varchar(20) not null comment="姓名"
# SQL: key(name),
name = models.CharField(max_length=20, db_index=True, verbose_name="姓名" )
# SQL: age smallint not null comment="年龄"
age = models.SmallIntegerField(verbose_name="年龄")
# SQL: sex tinyint not null comment="性别"
# sex = models.BooleanField(verbose_name="性别")
sex = models.SmallIntegerField(choices=SEX_CHOICES, default=2)
# SQL: class varchar(5) not null comment="班级"
# SQL: key(class)
classmate = models.CharField(db_column="class", max_length=5, db_index=True, verbose_name="班级")
# SQL: description longtext default "" not null comment="个性签名"
description = models.TextField(default="", verbose_name="个性签名")
#2. 数据表结构信息
class Meta:
db_table = 'tb_student' # 指明数据库表名,如果没有指定表明,则默认为子应用目录名_模型名称,例如: users_student
verbose_name = '学生信息表' # 在admin站点中显示的名称
verbose_name_plural = verbose_name # 显示的复数名称
#3. 自定义数据库操作方法
def __str__(self):
"""定义每个数据对象的显示信息"""
return "<User %s>" % self.name
(1) 数据库表名
模型类如果未指明表名db_table,Django默认以 小写app应用名_小写模型类名 为数据库表名。
可通过db_table 指明数据库表名。
(2) 关于主键
django会为表创建自动增长的主键列,每个模型只能有一个主键列。
如果使用选项设置某个字段的约束属性为主键列(primary_key)后,django不会再创建自动增长的主键列。
class Student(models.Model):
# django会自动在创建数据表的时候生成id主键/还设置了一个调用别名 pk
id = models.AutoField(primary_key=True, null=False, verbose_name="主键") # 设置主键
默认创建的主键列属性为id,可以使用pk代替,pk全拼为primary key。
(3) 属性命名限制
-
不能是python的保留关键字。
-
不允许使用连续的2个下划线,这是由django的查询方式决定的。__ 是关键字来的,不能使用!!!
-
定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下:
属性名 = models.字段类型(约束选项, verbose_name="注释")
(4)字段类型
类型 | 说明 |
---|---|
AutoField | 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性 |
BooleanField | 布尔字段,值为True或False |
NullBooleanField | 支持Null、True、False三种值 |
CharField | 字符串,参数max_length表示最大字符个数,对应mysql中的varchar |
TextField | 大文本字段,一般大段文本(超过4000个字符)才使用。 |
IntegerField | 整数 |
DecimalField | 十进制浮点数, 参数max_digits表示总位数, 参数decimal_places表示小数位数,常用于表示分数和价格 Decimal(max_digits=7, decimal_places=2) ==> 99999.99~ 0.00 |
FloatField | 浮点数 |
DateField | 日期 参数auto_now表示每次保存对象时,自动设置该字段为当前时间。 参数auto_now_add表示当对象第一次被创建时自动设置当前。 参数auto_now_add和auto_now是相互排斥的,一起使用会发生错误。 |
TimeField | 时间,参数同DateField |
DateTimeField | 日期时间,参数同DateField |
FileField | 上传文件字段,django在文件字段中内置了文件上传保存类, django可以通过模型的字段存储自动保存上传文件, 但是, 在数据库中本质上保存的仅仅是文件在项目中的存储路径!! |
ImageField | 继承于FileField,对上传的内容进行校验,确保是有效的图片 |
(5)约束选项
选项 | 说明 |
---|---|
null | 如果为True,表示允许为空,默认值是False。相当于python的None |
blank | 如果为True,则该字段允许为空白,默认值是False。 相当于python的空字符串,“” |
db_column | 字段的名称,如果未指定,则使用属性的名称。 |
db_index | 若值为True, 则在表中会为此字段创建索引,默认值是False。 相当于SQL语句中的key |
default | 默认值,当不填写数据时,使用该选项的值作为数据的默认值。 |
primary_key | 如果为True,则该字段会成为模型的主键,默认值是False,一般不用设置,系统默认设置。 |
unique | 如果为True,则该字段在表中必须有唯一值,默认值是False。相当于SQL语句中的unique |
注意:null是数据库范畴的概念,blank是表单验证范畴的
(6) 外键
在设置外键时,需要通过on_delete选项指明主表删除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量:
-
CASCADE 级联,删除主表数据时连通一起删除外键表中数据
-
PROTECT 保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
-
SET_NULL 设置为NULL,仅在该字段null=True允许为null时可用
-
SET_DEFAULT 设置为默认值,仅在该字段设置了默认值时可用
-
SET() 设置为特定值或者调用特定方法,例如:
from django.conf import settings from django.contrib.auth import get_user_model from django.db import models def get_sentinel_user(): return get_user_model().objects.get_or_create(username='deleted')[0] class UserModel(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET(get_sentinel_user), )
-
DO_NOTHING 不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常
商品分类表
id | category | |
---|---|---|
1 | 蔬菜 | |
2 | 电脑 |
商品信息表
id | goods_name | cid |
---|---|---|
1 | 冬瓜 | 1 |
2 | 华为笔记本A1 | 2 |
3 | 茄子 | 1 |
当模型字段的on_delete=CASCADE, 删除蔬菜(id=1),则在外键cid=1的商品id1和3就被删除。
当模型字段的on_delete=PROTECT,删除蔬菜,mysql自动检查商品信息表,有没有cid=1的记录,有则提示必须先移除掉商品信息表中,id=1的所有记录以后才能删除蔬菜。
当模型字段的on_delete=SET_NULL,删除蔬菜以后,对应商品信息表,cid=1的数据的cid全部被改成cid=null
当模型字段的on_delete=SET_DEFAULT,删除蔬菜以后,对应商品信息表,cid=1的数据记录的cid被被设置默认值。
6.3、数据迁移
将模型类定义表架构的代码转换成SQL同步到数据库中,这个过程就是数据迁移。django中的数据迁移,就是一个类,这个类提供了一系列的终端命令,帮我们完成数据迁移的工作。
(1)生成迁移文件
所谓的迁移文件, 是类似模型类的迁移类,主要是描述了数据表结构的类文件.
python manage.py makemigrations
(2)同步到数据库中
python manage.py migrate
补充:在django内部提供了一系列的功能,这些功能也会使用到数据库,所以在项目搭建以后第一次数据迁移的时候,会看到django项目中其他的数据表被创建了。其中就有一个django内置的admin站点管理。
# admin站点默认是开启状态的,我们可以通过http://127.0.0.1:8000/admin
# 这个站点必须有个管理员账号登录,所以我们可以在第一次数据迁移,有了数据表以后,就可以通过以下终端命令来创建一个超级管理员账号。
python manage.py createsuperuser
(3)添加测试数据
INSERT INTO `db_student`
(`id`,`name`,`sex`,`class`,`age`,`description`,`created_time`,`updated_time`)
VALUES
(1,'赵华',1,307,22,'对于勤奋的人来说,成功不是偶然;对于懒惰的人来说,失败却是必然。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(2,'程星云',1,301,20,'人生应该如蜡烛一样,从顶燃到底,一直都是光明的。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(3,'陈峰',1,504,21,'在不疯狂,我们就老了,没有记忆怎么祭奠呢?','2020-11-20 10:00:00','2020-11-20 10:00:00'),(4,'苏礼就',1,502,20,'不要为旧的悲伤,浪费新的眼泪。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(5,'张小玉',2,306,18,'没有血和汗水就没有成功的泪水。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(6,'吴杰',1,307,19,'以大多数人的努力程度之低,根本轮不到去拼天赋','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(7,'张小辰',2,405,19,'人生的道路有成千上万条, 每一条路上都有它独自的风景。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(8,'王丹丹',2,502,22,'平凡的人听从命运,坚强的人主宰命运。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(9,'苗俊伟',1,503,22,'外事找谷歌,内事找百度。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(10,'娄镇明',1,301,22,'不经三思不求教,不动笔墨不读书。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(11,'周梦琪',2,306,19,'学习与坐禅相似,须有一颗恒心。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(12,'欧阳博',1,503,23,'春去秋来,又一年。What did you get ?','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(13,'颜敏莉',2,306,20,'Knowledge makes humble, ignorance makes proud.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(14,'柳宗仁',1,301,20,'有志者事竟成。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(15,'谢海龙',1,402,22,'这世界谁也不欠谁,且行且珍惜。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(16,'邓士鹏',1,508,22,'青,取之于蓝而青于蓝;冰,水为之而寒于水。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(17,'宁静',2,502,23,'一息若存 希望不灭','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(18,'上官屏儿',2,502,21,'美不自美,因人而彰。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(19,'孙晓静',2,503,20,'人生本过客,何必千千结;无所谓得失,淡看风和雨。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(20,'刘承志',1,306,20,'good good study,day day up! ^-^','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(21,'王浩',1,503,21,'积土而为山,积水而为海。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(22,'钟无艳',2,303,19,'真者,精诚之至也,不精不诚,不能动人。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(23,'莫荣轩',1,409,22,'不管发生什么事,都请安静且愉快地接受人生,勇敢地、大胆地,而且永远地微笑着。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(24,'张裕民',1,303,21,'伟大的目标形成伟大的人物。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(25,'江宸轩',1,407,22,'用最少的悔恨面对过去。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(26,'谭季同',1,305,21,'人总是珍惜未得到的,而遗忘了所拥有的。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(27,'李松风',1,504,19,'明天的希望,让我们忘了今天的痛苦。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(28,'叶宗政',1,407,20,'因害怕失败而不敢放手一搏,永远不会成功。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(29,'魏雪宁',2,306,20,'成功与失败只有一纸之隔','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(30,'徐秋菱',2,404,19,'年轻是我们唯一拥有权利去编织梦想的时光。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(31,'曾嘉慧',2,301,19,'有一分热,发一分光。就令萤火一般,也可以在黑暗里发一点光,不必等候炬火。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(32,'欧阳镇安',1,408,23,'青春虚度无所成,白首衔悲补何及!','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(33,'周子涵',2,309,19,'青春是一个普通的名称,它是幸福美好的,但它也是充满着艰苦的磨炼。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(34,'宋应诺',2,501,23,'涓滴之水终可以磨损大石,不是由于它力量强大,而是由于昼夜不舍的滴坠。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(35,'白瀚文',1,305,19,'一个人假如不脚踏实地去做,那么所希望的一切就会落空。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(36,'陈匡怡',2,505,19,'一份耕耘,一份收获。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(37,'邵星芸',2,503,22,'冰冻三尺非一日之寒。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(38,'王天歌',2,302,21,'任何的限制,都是从自己的内心开始的。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(39,'王天龙',1,302,22,'再长的路,一步步也能走完,再短的路,不迈开双脚也无法到达。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(40,'方怡',2,509,23,'智者不做不可能的事情。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(41,'李伟',1,505,19,'人之所以能,是相信能。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(42,'李思玥',2,503,22,'人的一生可能燃烧也可能腐朽,我不能腐朽,我愿意燃烧起来。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(43,'赵思成',1,401,18,'合抱之木,生于毫末;九层之台,起于累土。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(44,'蒋小媛',2,308,22,'不积跬步无以至千里,不积细流无以成江河。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(45,'龙华',1,510,19,'只要持续地努力,不懈地奋斗,就没有征服不了的东西。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(46,'牧婧白夜',2,501,21,'读不在三更五鼓,功只怕一曝十寒。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(47,'江俊文',1,304,19,'立志不坚,终不济事。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(48,'李亚容',2,304,18,'Keep on going never give up.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(49,'王紫伊',2,301,22,'最可怕的敌人,就是没有坚强的信念。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(50,'毛小宁',1,501,19,'要从容地着手去做一件事,但一旦开始,就要坚持到底。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(51,'董 晴',2,507,19,'常常是最后一把钥匙打开了门。贵在坚持','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(52,'严语',2,405,18,'逆水行舟,不进则退。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(53,'陈都灵',2,503,19,'无论什么时候,不管遇到什么情况,我绝不允许自己有一点点灰心丧气。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(54,'黄威',1,301,23,'我的字典里面没有“放弃”两个字','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(55,'林佳欣',2,308,23,'梦想就是一种让你感到坚持,就是幸福的东西。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(56,'翁心颖',2,303,19,'有目标的人才能成功,因为他们知道自己的目标在哪里。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(57,'蒙毅',1,502,22,'所谓天才,就是努力的力量。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(58,'李小琳',2,509,22,'每天早上对自己微笑一下。这就是我的生活态度。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(59,'伍小龙',1,406,19,'一路上的点点滴滴才是我们的财富。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(60,'晁然',2,305,23,'人的价值是由自己决定的。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(61,'端木浩然',1,507,18,'摔倒了爬起来再哭。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(62,'姜沛佩',2,309,21,'Believe in yourself.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(63,'李栋明',1,306,19,'虽然过去不能改变,但是未来可以。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(64,'柴柳依',2,508,23,'没有实践就没有发言权。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(65,'吴杰',1,401,22,'人生有两出悲剧。一是万念俱灰;另一是踌躇满志','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(66,'杜文华',1,507,19,'有智者立长志,无志者长立志。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(67,'邓珊珊',2,510,18,'Action is the proper fruit of knowledge.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(68,'杜俊峰',1,507,23,'世上无难事,只要肯登攀。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(69,'庄信杰',1,301,22,'知识就是力量。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(70,'宇文轩',1,402,23,'如果你想要某样东西,别等着有人某天会送给你。生命太短,等不得。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(71,'黄佳怿',2,510,19,'Learn and live.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(72,'卫然',1,510,18,'神于天,圣于地。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(73,'耶律齐',1,307,23,'如果不是在海市蜃楼中求胜,那就必须脚踏实地去跋涉。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(74,'白素欣',2,305,18,'欲望以提升热忱,毅力以磨平高山。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(75,'徐鸿',1,403,23,'最美的不是生如夏花,而是在时间的长河里,波澜不惊。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(76,'上官杰',1,409,19,'生活之所以耀眼,是因为磨难与辉煌会同时出现。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(77,'吴兴国',1,406,18,'生活的道路一旦选定,就要勇敢地走到底,决不回头。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(78,'庄晓敏',2,305,18,'Never say die.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(79,'吴镇升',1,509,18,'Judge not from appearances.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(80,'朱文丰',1,304,19,'每个人都比自己想象的要强大,但同时也比自己想象的要普通。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(81,'苟兴妍',2,508,18,'Experience is the best teacher.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(82,'祝华生',1,302,21,'浅学误人。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(83,'张美琪',2,404,23,'最淡的墨水,也胜过最强的记性。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(84,'周永麟',1,308,21,'All work and no play makes Jack a dull boy.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(85,'郑心',2,404,21,'人生就像一杯茶,不会苦一辈子,但总会苦一阵子。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(86,'公孙龙馨',1,510,21,'Experience is the father of wisdom and memory the mother.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(87,'叶灵珑',2,401,19,'读一书,增一智。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(88,'上官龙',1,501,21,'别人能做到的事,自己也可以做到。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(89,'颜振超',1,303,19,'如果要飞得高,就该把地平线忘掉。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(90,'玛诗琪',2,409,22,'每天进步一点点,成功不会远。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(91,'李哲生',1,309,22,'这不是偶然的失误,是必然的结果。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(92,'罗文华',2,408,22,'好走的都是下坡路。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(93,'李康',1,509,19,'Deliberate slowly, promptly.','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(94,'钟华强',1,405,19,'混日子很简单,讨生活比较难。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(95,'张今菁',2,403,23,'不经一翻彻骨寒,怎得梅花扑鼻香。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(96,'黄伟麟',1,407,19,'与其诅咒黑暗,不如燃起蜡烛。没有人能给你光明,除了你自己。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(97,'程荣泰',1,406,22,'明天不一定更好,。但更好的明天一定会来。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(98,'范伟杰',1,508,19,'水至清则无鱼,人至察则无徒。凡事不能太执着。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(99,'王俊凯',1,407,21,'我欲将心向明月,奈何明月照沟渠。','2020-11-20 10:00:00','2020-11-20 10:00:00'),
(100,'白杨 ',1,406,19,'闪电从不打在相同的地方.人不该被相同的方式伤害两次。','2020-11-20 10:00:00','2020-11-20 10:00:00');
-- 作业:
-- 1. 完成学生的创建,导入上面的数据。
-- 2. 使用原来学过的SQL语句,然后对上面导入的学生信息,完成增删查改的操作。
-- 2.0 查询所有的学生信息(name,age)
SELECT name,age FROM db_student
-- 2.1 查询年龄在18-20之间的学生信息[name,age,sex]
select name,age,sex from db_student where age >=18 and age <=20;
-- 2.2 查询年龄在18-20之间的男生信息[name,age,sex]
select name,age,if(sex=1,'男','女') as sex from db_student where age >=18 and age <=20 and sex=1;
-- 2.3 查询401-406之间所有班级的学生信息[name,age,sex,class]
select name,age,sex,class from db_student where class between 401 and 406;
-- 2.4 查询401-406之间所有班级的总人数
select count(1) as c from db_student where class between 401 and 406;
-- 2.5 添加一个学生,男,刘德华,302班,17岁,"给我一杯水就行了。",'2020-11-20 10:00:00','2020-11-20 10:00:00'
insert into db_student (name,sex,class, age, description, created_time,updated_time) values ('刘德华',1,'302', 17, "给我一杯水就行了。",'2020-11-20 10:00:00','2020-11-20 10:00:00');
-- 2.6 修改刘德华的年龄,为19岁。
update db_student set age=19 where name='刘德华';
-- 2.7 刘德华毕业了,把他从学生表中删除
delete from db_student where name='刘德华';
-- 2.8 找到所有学生中,年龄最小的5位同学和年龄最大的5位同学
select * from db_student order by age asc limit 5;
select * from db_student order by age desc limit 5;
-- 2.9 【进阶】找到所有班级中人数超过4个人班级出来
select class,count(id) as total from db_student group by class having total >= 4;
-- 2.10【进阶】把上面2.8的要求重新做一遍,改成一条数据实现
(select * from db_student order by age asc limit 5) union all (select * from db_student order by age desc limit 5);
6.4、数据库基本操作
6.4.1、添加记录
(1)save方法
通过创建模型类对象,执行对象的save()方法保存到数据库中。
student = Student(
name="刘德华",
age=17,
sex=True,
clasmate=301,
description="一手忘情水"
)
student.save()
print(student.id) # 判断是否新增有ID
(2)create方法
通过模型类.objects.create()保存。
student = Student.objects.create(
name="赵本山",
age=50,
sex=True,
class_number=301,
description="一段小品"
)
print(student.id)
6.4.2、基础查询
ORM中针对查询结果的数量限制,提供了一个查询集[QuerySet].这个QuerySet,是ORM中针对查询结果进行保存数据的一个类型,我们可以通过了解这个QuerySet进行使用,达到查询优化,或者限制查询结果数量的作用。
查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。当调用如下过滤器方法时,Django会返回查询集(而不是简单的列表):
- all():返回所有数据。
- filter():返回满足条件的数据。filter会默认调用all方法
- exclude():返回满足条件之外的数据。exclude会默认调用all方法
- order_by():对结果进行排序。order_by会默认调用all方法
(1)all()
查询所有对象
(2)filter()
筛选条件相匹配的对象
(3)get()
返回与所给筛选条件相匹配的对象,返回结果有且只有一个, 如果符合筛选条件的对象超过一个或者没有都会抛出错误。
student = Student.objects.get(pk=104)
print(student)
print(student.description)
get使用过程中的注意点:get是根据条件返回多个结果或者没有结果,都会报错
try:
student = Student.objects.get(name="刘德华")
print(student)
print(student.description)
except Student.MultipleObjectsReturned:
print("查询得到多个结果!")
except Student.DoesNotExist:
print("查询结果不存在!")
(4)first()、last()
分别为查询集的第一条记录和最后一条记录
# 没有结果返回none,如果有多个结果,则返回模型对象
student = Student.objects.filter(name="刘德华").first()
print(student.id)
(5)exclude()
筛选条件不匹配的对象
(6)order_by()
对查询结果排序
# order_by("字段") # 按指定字段正序显示,相当于 asc 从小到大
# order_by("-字段") # 按字段倒序排列,相当于 desc 从大到小
# order_by("第一排序","第二排序",...)
# student_list = Student.objects.filter(classmate="301").order_by("-age").all()
# student_list = Student.objects.filter(classmate="301").order_by("age").all() #正序
student_list = Student.objects.filter(classmate="301").order_by("-age","id").all() # 年龄倒序,年龄一致按id进行正序
for student in student_list:
print(f"id={student.id}, name={student.name}, age={student.age}")
(7)count()
查询集中对象的个数
# count统计结果数量
ret = Student.objects.filter(classmate="301").count()
print(ret)
(8)exists()
判断查询集中是否有数据,如果有则返回True,没有则返回False
(9)values()、values_list()
-
value()
把结果集中的模型对象转换成字典,并可以设置转换的字段列表,达到减少内存损耗,提高性能 -
values_list()
: 把结果集中的模型对象转换成列表,并可以设置转换的字段列表(元祖),达到减少内存损耗,提高性能
# values 把查询结果中模型对象转换成字典
student_list = Student.objects.filter(classmate="301")
student_list = student_list.order_by("-age")
student_list = student_list.filter(sex=1)
ret1 = student_list.values() # 默认把所有字段全部转换并返回
ret2 = student_list.values("id","name","age") # 可以通过参数设置要转换的字段并返回
ret3 = student_list.values_list() # 默认把所有字段全部转换并返回
ret4 = student_list.values_list("id","name","age") # 可以通过参数设置要转换的字段并返回
print(ret4)
return JsonResponse({},safe=False)
(11)distinct()
从返回结果中剔除重复纪录
6.4.3、模糊查询
(1)模糊查询之contains
说明:如果要包含%无需转义,直接写即可。
例:查询姓名包含’华’的学生。
Student.objects.filter(name__contains='华')
(2)模糊查询之startswith、endswith
例:查询姓名以’文’结尾的学生
Student.objects.filter(name__endswith='文')
以上运算符都区分大小写,在这些运算符前加上i表示不区分大小写,如iexact、icontains、istartswith、iendswith.
(3)模糊查询之isnull
例:查询个性签名不为空的学生。
# 修改Student模型description属性允许设置为null,然后数据迁移
description = models.TextField(default=None, null=True, verbose_name="个性签名")
# 添加测试数据
NSERT INTO student.db_student (name, age, sex, class, description, created_time, updated_time) VALUES ('刘德华', 17, 1, '407', null, '2020-11-20 10:00:00.000000', '2020-11-20 10:00:00.000000');
# 代码操作
tudent_list = Student.objects.filter(description__isnull=True)
(4)模糊查询之in
例:查询编号为1或3或5的学生
Student.objects.filter(id__in=[1, 3, 5])
(5)模糊查询之比较查询
- gt 大于 (greater then)
- gte 大于等于 (greater then equal)
- lt 小于 (less then)
- lte 小于等于 (less then equal)
例:查询编号大于3的学生
Student.objects.filter(id__gt=3)
(6)模糊查询之日期查询
year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算。
例:查询2010年被添加到数据中的学生。
Student.objects.filter(born_date__year=1980)
例:查询2016年6月20日后添加的学生信息。
from django.utils import timezone as datetime
student_list = Student.objects.filter(created_time__gte=datetime.datetime(2016,6,20),created_time__lt=datetime.datetime(2016,6,21)).all()
print(student_list)
6.4.4、进阶查询
(1) F查询
之前的查询都是对象的属性与常量值比较,两个属性怎么比较呢? 答:使用F对象,被定义在django.db.models中。
语法如下:
"""F对象:2个字段的值比较"""
# 获取从添加数据以后被改动过数据的学生
from django.db.models import F
# SQL: select * from db_student where created_time=updated_time;
student_list = Student.objects.exclude(created_time=F("updated_time"))
print(student_list)
(2) Q查询
多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字。
例:查询年龄大于20,并且编号小于30的学生。
Student.objects.filter(age__gt=20,id__lt=30)
或
Student.filter(age__gt=20).filter(id__lt=30)
如果需要实现逻辑或or的查询,需要使用Q()对象结合|运算符,Q对象被义在django.db.models中。
语法如下:
Q(属性名__运算符=值)
Q(属性名__运算符=值) | Q(属性名__运算符=值)
例:查询年龄小于19或者大于20的学生,使用Q对象如下。
from django.db.models import Q
student_list = Student.objects.filter( Q(age__lt=19) | Q(age__gt=20) ).all()
Q对象可以使用&、|连接,&表示逻辑与,|表示逻辑或**
例:查询年龄大于20,或编号小于30的学生,只能使用Q对象实现
Student.objects.filter(Q(age__gt=20) | Q(pk__lt=30))
Q对象左边可以使用~操作符,表示非not。但是工作中,我们只会使用Q对象进行或者的操作,只有多种嵌套复杂的查询条件才会使用&和~进行与和非得操作
。
例:查询编号不等于30的学生。
Student.objects.filter(~Q(pk=30))
(3)聚合查询
使用aggregate()过滤器调用聚合函数。聚合函数包括:Avg 平均,Count 数量,Max 最大,Min 最小,Sum 求和,被定义在django.db.models中。
例:查询学生的平均年龄。
from django.db.models import Sum,Count,Avg,Max,Min
Student.objects.aggregate(Avg('age'))
注意:aggregate的返回值是一个字典类型,格式如下:
{'属性名__聚合类小写':值}
使用count时一般不使用aggregate()过滤器。
例:查询学生总数。
Student.objects.count() # count函数的返回值是一个数字。
(4)分组查询
QuerySet对象.annotate()
# annotate() 进行分组统计,按前面select 的字段进行 group by
# annotate() 返回值依然是 queryset对象,增加了分组统计后的键值对
模型对象.objects.values("id").annotate(course=Count('course__sid')).values('id','course')
# 查询指定模型, 按id分组 , 将course下的sid字段计数,返回结果是 name字段 和 course计数结果
# SQL原生语句中分组之后可以使用having过滤,在django中并没有提供having对应的方法,但是可以使用filter对分组结果进行过滤
# 所以filter在annotate之前,表示where,在annotate之后代表having
# 同理,values在annotate之前,代表分组的字段,在annotate之后代表数据查询结果返回的字段
(5)原生查询
执行原生SQL语句,也可以直接跳过模型,才通用原生pymysql.
ret = Student.objects.raw("SELECT id,name,age FROM tb_student") # student 可以是任意一个模型
# 这样执行获取的结果无法通过QuerySet进行操作读取,只能循环提取
for item in ret:
print(item)
6.4.5、修改记录
(1) 使用save更新数据
student = Student.objects.filter(name='刘德华').first()
print(student)
student.age = 19
student.classmate = "303"
# save之所以能提供给我们添加数据的同时,还可以更新数据的原因?
# save会找到模型的字段的主键id的值,
# 主键id的值如果是none,则表示当前数据没有被数据库,所以save会自动变成添加操作
# 主键id有值,则表示当前数据在数据库中已经存在,所以save会自动变成更新数据操作
student.save()
(2)update更新(推荐)
使用模型类.objects.filter().update(),会返回受影响的行数
# update是全局更新,只要符合更新的条件,则全部更新,因此强烈建议加上条件!!!
student = Student.objects.filter(name="赵华",age=22).update(name="刘芙蓉",sex=True)
print(student)
6.4.6、删除记录
删除有两种方法
(1)模型类对象.delete
student = Student.objects.get(id=13)
student.delete()
(2)模型类.objects.filter().delete()
Student.objects.filter(id=14).delete()
代码:
# 1. 先查询到数据模型对象。通过模型对象进行删除
# student = Student.objects.filter(pk=13).first()
# student.delete()
# 2. 直接删除
ret = Student.objects.filter(pk=100).delete()
print(ret)
# 务必写上条件,否则变成了清空表了。ret = Student.objects.filter().delete()
6.5、创建关联模型
实例:我们来假定下面这些概念,字段和关系
- 作者模型:一个作者有姓名和年龄。
- 作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
- 出版商模型:出版商有名称,所在城市以及email。
- 书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
模型建立如下:
from django.db import models
# Create your models here.
class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
publishDate = models.DateField()
price = models.DecimalField(max_digits=5, decimal_places=2)
# 与Publish建立一对多的关系,外键字段建立在多的一方
# on_delete= 关联关系的设置
# models.CASCADE 删除主键以后, 对应的外键所在数据也被删除
# models.DO_NOTHING 删除主键以后, 对应的外键不做任何修改
# 反向查找字段 related_name
publish = models.ForeignKey(to="Publish", to_field="nid",related_name="booklist",on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors = models.ManyToManyField(to='Author',related_name='booklist',db_table="tb_book_author")
class Meta:
db_table = 'tb_book'
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
class Meta:
db_table = 'tb_publish'
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
# 与AuthorDetail建立一对一的关系
authorDetail = models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
class Meta:
db_table = 'tb_author'
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
birthday = models.DateField()
telephone = models.BigIntegerField()
addr = models.CharField(max_length=64)
class Meta:
db_table = 'tb_authorDetail'
6.6、关联添加
(1)一对多
# 方式1:
pub_obj= Publish.objects.get(pk=2)
Book.objects.create(title="西游记",price = 122,publishDate="2012-12-12",publish=pub_obj)
# 方式2:
Book.objects.create(title="三国演义", price=345, publishDate="2012-12-12", publish_id=1)
一对一添加方式和一对多相同
(2)多对多
# 多对多添加记录
# 给西游记添加yuan和alivn两个作者
author1 = Author.objects.get(name="yuan")
author2 = Author.objects.get(name="alvin")
book = Book.objects.get(title="西游记")
# book.authors.add(author1,author2)
# book.authors.add(*[author1,author2])
book.authors.add(*[2,3])
book.authors.remove(2)
book.authors.remove(*[2,3])
book.authors.clear()
book.authors.set(2)
# authors = book.authors.all()
# print(authors.values("name","age"))
# authors.delete()
6.7、关联查询
6.7.1、基于对象查询(子查询)
# (1)一对多
# 查询西游记的出版社的名字
book = Book.objects.get(title='西游记')
print(book.publish.name)
# 查询西瓜出版社出版的书籍名称
publish = Publish.objects.get(name="西瓜出版社")
books = publish.booklist.all()
print(books)
# (2)多对多
# 查询西游记的所有作者的名字
books = book.authors.all()
print(books)
# 查询yuan出版过的所有书籍
author = Author.objects.get(name="yuan")
books =author.booklist.all()
print(books)
# (3)一对一
# 查询yuan的手机号
print(author.ad.telephone)
# 查询手机号为110的作者名字
ad = AuthorDetail.objects.get(telephone=110)
print(ad.author.name)
(1)正向查询按字段
(2)反向查询按表名小写或表名小写_set或者related_name
6.7.2、基于双下划线查询(join查询)
# (1)一对多
# 查询西游记的出版社的名字
ret = Book.objects.filter(price__gt=100).values("publish__name")
print(ret) # <QuerySet [{'publish__name': '西瓜出版社'}]>
ret = Publish.objects.filter(booklist__title="西游记").values("name")
print(ret) # <QuerySet [{'name': '西瓜出版社'}]>
# 查询id>1的出版社出版的书籍名称
ret = Publish.objects.filter(pk__gte=1).values("booklist__title")
print(ret)
ret = Book.objects.filter(publish__pk__gte=1).values("title")
print(ret)
# (2)多对多
# 查询西游记的所有作者的名字
ret = Book.objects.filter(title="西游记").values("authors__name")
print(ret) # <QuerySet [{'authors__name': 'yuan'}, {'authors__name': 'alvin'}]>
ret = Author.objects.filter(booklist__title="西游记").values("name")
print(ret)
# 查询yuan出版过的所有书籍
Book.objects.filter(authors__name="yuan").values("title")
Author.objects.filter(name='yuan').values("booklist__title")
# 查询yuan的手机号
Author.objects.filter(name="yuan").values("ad__telephone")
AuthorDetail.objects.filter(author__name="yuan").values("telephone")
# (3)一对一
# 查询手机号为110的作者名字
AuthorDetail.objects.filter(telephone=110).values("author__name")
Author.objects.filter(authorDetail__telephone=110).values("name")
# 手机号为110的作者出版过的所有书籍名称以及出版社名称
Book.objects.filter(authors__authorDetail__telephone=110).values("title","publish__name")
# (4) 进阶练习
# 手机号以151开头的作者出版过的所有书籍名称以及出版社名称
queryResult=Book.objects.filter(authors__authorDetail__telephone__regex="151").values_list("title","publish__name")
(1)正向关联按关联字段
(2)反向按表名小写或related_name
6.7.3、关联分组查询
from django.db.models import Avg,Max,Min,Count,Sum
ret = Book.objects.all().aggregate(m_price=Max("price"))
print(ret) # {'m_price': Decimal('345')}
# 查询每一个出版社出版过得书籍的平均价格
ret = Book.objects.values("publish_id").annotate(avg_pricve=Avg("price"))
print(ret)
# 查询每一个出版社的名称以及出版过得书籍的平均价格
# 方式1
ret = Book.objects.values("publish__name").annotate(avg_pricve=Avg("price"))
print(ret)
# <QuerySet [{'publish__name': '芒果出版社', 'avg_pricve': Decimal('233.5')}, {'publish__name': '西瓜出版社', 'avg_pricve': Decimal('222')}]>
# 方式2
ret = Publish.objects.all().annotate(max_price =Max("booklist__price")).values("name","email","max_price")
print(ret)
# 每一个作者出版过得书籍的个数
ret = Author.objects.all().annotate(c = Count("booklist")).values("name","c")
print(ret)
# 查询出版书籍个数大于1的作者的名字以及出版书籍的个数
ret = Author.objects.all().annotate(c=Count("booklist")).filter(c__gt=1).values("name","c")
print(ret)
# 统计每一个出版社的最便宜的书
ret = Publish.objects.all().annotate(c =Min("booklist__price")).values("name","c")
print(ret)
# 统计每一本书籍的作者个数:
ret = Book.objects.all().annotate(c = Count("authors")).values("title","c")
print(ret)
# 统计不止一个作者的图书:
ret = Book.objects.all().annotate(c=Count("authors")).filter(c__gt=1).values("title","c")
print(ret)
# 根据一本图书作者数量的多少对查询集 QuerySet进行排序
ret = Book.objects.all().annotate(c=Count("authors")).order_by("c")
print(ret)
# 查询各个作者出的书的总价格
ret = Author.objects.all().annotate(sum_price=Sum("booklist__price")).values("name","sum_price")
print(ret)
第7章、Ajax请求
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。
AJAX的优点:
- AJAX使用Javascript技术向服务器发送异步请求;
- AJAX无须刷新整个页面(局部刷新);
- 因为服务器响应内容不再是整个页面,而是页面中的局部,所以AJAX性能高;
应用:
7.1、json数据
'''
Supports the following objects and types by default:
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
'''
python支持的序列化和反序列化方法
import json
dic = {"name": "yuan"}
dic_json = json.dumps(dic)
dic = json.dumps(dic_json)
Django支持的序列化方法
关于Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,特别的Ajax请求一般返回的为Json格式。
from django.core import serializers
ret = models.BookType.objects.all()
data = serializers.serialize("json", ret)
7.2、Ajax请求案例
(1) 视图
# Create your views here.
def reg(request):
return render(request, "reg.html")
def reg_user(request):
data = {"msg": "", "state": "success"}
user = request.POST.get("user")
if user == "yuan":
data["state"] = "error"
data["msg"] = "该用户已存在!"
return JsonResponse(data)
(2) 模板:reg.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>用户名:<input type="text"><span class="error" style="color: red"></span></p>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script>
$(":text").blur(function () {
// 发送Ajax请求
$.ajax({
url: "http://127.0.0.1:8008/reg_user/",
type: "post",
data: {
user: $(":text").val(),
},
success: function (response) {
console.log(response);
$(".error").html(response.msg);
}
})
})
</script>
</body>
</html>
(3) 流程图
7.3、同源策略
现在我们将reg.html单独放在客户端,用浏览器打开,再触发事件,会发现报错:
这是因为浏览器的同源策略导致的。
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
那么如何解决这种跨域问题呢,我们主要由三个思路:
- jsonp
- cors
- 前端代理
这里主要给大家介绍第二个:cors
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
所以,服务器方面只要添加一个响应头,同意跨域请求,浏览器也就不再拦截:
response = JsonResponse(data)
response["Access-Control-Allow-Origin"] = "*"
7.4、练习作业
- 计算器
- 登录案例
第8章、Django的组件
8.1、中间件
中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。
Django的中间件的定义:
Middleware is a framework of hooks into Django’s request/response processing.
It’s a light, low-level “plugin” system for globally altering Django’s input or output.
MiddleWare,是 Django 请求/响应处理的钩子框架。
它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。【输入指代的就是客户端像服务端django发送数据,输出指代django根据客户端要求处理数据的结果返回给客户端】
如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。
django框架内部声明了很多的中间件,这些中间件有着各种各种的用途,有些没有被使用,有些被默认开启使用了。而被开启使用的中间件,都是在settngs.py的MIDDLEWARE中注册使用的。
Django默认的Middleware
:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
8.1.1.自定义中间件
(1)定义中间件
创建存放自定义中间件的文件这里选择在app01里创建mdws.py文件:
from django.utils.deprecation import MiddlewareMixin
class Md1(MiddlewareMixin):
def process_request(self, request):
print("Md1请求")
# return HttpResponse("Md1中断") # 拦截
def process_response(self, request, response):
print("Md1返回")
return response
class Md2(MiddlewareMixin):
def process_request(self, request):
print("Md2请求")
# return HttpResponse("Md2中断")
def process_response(self, request, response):
print("Md2返回")
return response
- process_request默认返回None,返回None,则继续执行下一个中间件的process_request;一旦返回响应体对象,则会拦截返回。
- process_response必须有一个形参response,并return response;这是view函数返回的响应体,像接力棒一样传承给最后的客户端。
(2) 注册中间件
MIDDLEWARE = [
...
'app01.mdws.Md1',
'app01.mdws.Md2'
]
(3)构建index路由
# path('index/', views.index),
def index(request):
print("index 视图函数执行...")
return HttpResponse("hello yuan")
启动项目,访问index路径:
后台打印结果:
Md1请求
Md2请求
index 视图函数执行...
Md2返回
Md1返回
所以,通过结果我们看出中间件的执行顺序:
8.1.3.中间件应用
1. 做IP访问频率限制
某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。
2. 权限访问限制
8.2、Cookie与Session
我们知道HTTP协议是无状态协议,也就是说每个请求都是独立的!无法记录前一次请求的状态。但HTTP协议中可以使用Cookie来完成会话跟踪!在Web开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。
8.2.1、cookie
Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是key-value结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端了!
cookie可以理解为每一个浏览器针对每一个服务器创建的key-value结构的本地存储文件
(1)cookie流程图
(2)cookie语法
# (1) 设置cookie:
res = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect()
res.set_cookie(key,value,max_age...)
res.set_signed_cookie(key,value,salt='加密盐',...)
# (2) 获取cookie:
request.COOKIES
# (3) 删除cookie
response.delete_cookie("cookie_key",path="/",domain=name)
8.2.2、session
(1)session流程图
(2)session语法与案例
# 1、设置Sessions值 request.session['session_name'] ="admin" # 2、获取Sessions值 session_name = request.session["session_name"] # 3、删除Sessions值 del request.session["session_name"] # 4、flush() # 删除当前的会话数据并删除会话的Cookie。这用于确保前面的会话数据不可以再次被用户的浏览器访问
def s_login(request):
if request.method == "GET":
return render(request, "login.html")
else:
user = request.POST.get("user")
pwd = request.POST.get("pwd")
try:
# user_obj = User.objects.get(user=user,pwd=pwd)
# 写session
# request.session["is_login"] = True
# request.session["username"] = user_obj.user
return redirect("/s_index/")
except:
return redirect("/s_login/")
def s_index(request):
# 读session
is_login = request.session.get("is_login")
if is_login:
username = request.session.get("username")
return render(request, "index.html", {"user": username})
else:
return redirect("/s_login/")
'''
shop.html:
<p>
客户端最后一次访问时间:{{ last_time|default:"第一次访问" }}
</p>
<h3>商品页面</h3>
'''
def shop(request):
last_time = request.session.get("last_time")
now = datetime.datetime.now().strftime("%Y-%m-%d %X")
request.session["last_time"] = now
return render(request, "shop.html", {"last_time": last_time})
def s_logout(request):
# request.session.flush()
del request.session["username"]
del request.session["is_login"]
return redirect("/s_login/")
(3)session配置
# Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
# 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
(4) 练习:基于session实现验证码案例
8.2.3、用户认证组件
Django默认已经提供了认证系统Auth模块,我们认证的时候,会使用auth模块里面给我们提供的表。认证系统包含:
- 用户管理
- 权限
- 用户组
- 密码哈希系统
- 用户登录或内容显示的表单和视图
- 一个可插拔的后台系统 admin
(1)Django用户模型类
Django认证系统中提供了用户模型类User保存用户的数据,默认的User包含以下常见的基本字段:
字段名 | 字段描述 |
---|---|
username | 必选。150个字符以内。 用户名可能包含字母数字,_ ,@ ,+ . 和- 个字符。 |
first_name | 可选(blank=True )。 少于等于30个字符。 |
last_name | 可选(blank=True )。 少于等于30个字符。 |
email | 可选(blank=True )。 邮箱地址。 |
password | 必选。 密码的哈希加密串。 (Django 不保存原始密码)。 原始密码可以无限长而且可以包含任意字符。 |
groups | 与Group 之间的多对多关系。 |
user_permissions | 与Permission 之间的多对多关系。 |
is_staff | 布尔值。 设置用户是否可以访问Admin 站点。 |
is_active | 布尔值。 指示用户的账号是否激活。 它不是用来控制用户是否能够登录,而是描述一种帐号的使用状态。 |
is_superuser | 是否是超级用户。超级用户具有所有权限。 |
last_login | 用户最后一次登录的时间。 |
date_joined | 账户创建的时间。 当账号创建时,默认设置为当前的date/time。 |
上面缺少一些字段,所以后面我们会对当前内置的用户模型进行改造,比如说它里面没有手机号字段,后面我们需要加上。
(2)重要方法
Django 用户认证(Auth)组件需要导入 auth 模块
# 认证模块
from django.contrib import auth
# 对应数据库用户表,可以继承扩展
from django.contrib.auth.models import User
(1)用户对象
create() # 创建一个普通用户,密码是明文的。
create_user() # 创建一个普通用户,密码是密文的。
create_superuser() # 与create_user() 相同,但是设置is_staff 和is_superuser 为True。
set_password(*raw_password*)
# 设置用户的密码为给定的原始字符串,并负责密码的。 不会保存User对象。当None为raw_password时,密码将设置为一个不可用的密码。
check_password(*raw_password*)
# 如果给定的raw_password是用户的真实密码,则返回True,可以在校验用户密码时使用。
(2)认证方法
auth.authenticate(username,password)
# 将输入的密码转为密文去认证,认证成功返回用户对象,失败则返回None
(3)登录和注销方法
from django.contrib import auth
# 该函数接受一个HttpRequest对象,以及一个认证了的User对象。此函数使用django的session框架给某个已认证的用户附加上session id等信息。
auth.login()
# 该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。
auth.logout()
(4)request.user
Django有一个默认中间件,叫做AuthenticationMiddleware,每次请求进来都会去session中去一个userid,取不到的话,赋值request.user = AnonymousUser() , 一个匿名用户对象。
当用户组件auth.login一旦执行,将userid到session中后,再有请求进入Django,将注册的userid对应的user对象赋值给request.user,即再后面的任何视图函数中都可以从request.user中取到该客户端的登录对象。
8.3、Django的分页器
(1) index视图
def index(request):
'''
批量导入数据:
Booklist=[]
for i in range(100):
Booklist.append(Book(title="book"+str(i),price=30+i*i))
Book.objects.bulk_create(Booklist)
分页器的使用:
book_list=Book.objects.all()
paginator = Paginator(book_list, 10)
print("count:",paginator.count) #数据总数
print("num_pages",paginator.num_pages) #总页数
print("page_range",paginator.page_range) #页码的列表
page1=paginator.page(1) # 第1页的page对象
for i in page1: # 遍历第1页的所有数据对象
print(i)
print(page1.object_list) #第1页的所有数据
page2=paginator.page(2)
print(page2.has_next()) #是否有下一页
print(page2.next_page_number()) #下一页的页码
print(page2.has_previous()) #是否有上一页
print(page2.previous_page_number()) #上一页的页码
# 抛错
#page=paginator.page(12) # error:EmptyPage
#page=paginator.page("z") # error:PageNotAnInteger
'''
book_list = Book.objects.all()
paginator = Paginator(book_list, 10)
page = request.GET.get('page', 1)
current_page = int(page)
try:
print(page)
book_list = paginator.page(page)
except PageNotAnInteger:
book_list = paginator.page(1)
except EmptyPage:
book_list = paginator.page(paginator.num_pages)
return render(request, "index.html", {"book_list": book_list, "paginator": paginator, "currentPage": current_page})
(2) index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h4>分页器</h4>
<ul>
{% for book in book_list %}
<li>{{ book.title }} -----{{ book.price }}</li>
{% endfor %}
</ul>
<ul class="pagination" id="pager">
{% if book_list.has_previous %}
<li class="previous"><a href="/index/?page={{ book_list.previous_page_number }}">上一页</a></li>
{% else %}
<li class="previous disabled"><a href="#">上一页</a></li>
{% endif %}
{% for num in paginator.page_range %}
{% if num == currentPage %}
<li class="item active"><a href="/index/?page={{ num }}">{{ num }}</a></li>
{% else %}
<li class="item"><a href="/index/?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %}
{% if book_list.has_next %}
<li class="next"><a href="/index/?page={{ book_list.next_page_number }}">下一页</a></li>
{% else %}
<li class="next disabled"><a href="#">下一页</a></li>
{% endif %}
</ul>
</div>
</body>
</html>
{“user”: username})
else:
return redirect(“/s_login/”)
‘’’
shop.html:
客户端最后一次访问时间:{{ last_time|default:"第一次访问" }}
商品页面
'''def shop(request):
last_time = request.session.get(“last_time”)
now = datetime.datetime.now().strftime(“%Y-%m-%d %X”)
request.session[“last_time”] = now
return render(request, "shop.html", {"last_time": last_time})
def s_logout(request):
# request.session.flush()
del request.session[“username”]
del request.session[“is_login”]
return redirect(“/s_login/”)
#### (3)session配置
````python
# Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
# 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
(4) 练习:基于session实现验证码案例
8.2.3、用户认证组件
Django默认已经提供了认证系统Auth模块,我们认证的时候,会使用auth模块里面给我们提供的表。认证系统包含:
- 用户管理
- 权限
- 用户组
- 密码哈希系统
- 用户登录或内容显示的表单和视图
- 一个可插拔的后台系统 admin
(1)Django用户模型类
Django认证系统中提供了用户模型类User保存用户的数据,默认的User包含以下常见的基本字段:
字段名 | 字段描述 |
---|---|
username | 必选。150个字符以内。 用户名可能包含字母数字,_ ,@ ,+ . 和- 个字符。 |
first_name | 可选(blank=True )。 少于等于30个字符。 |
last_name | 可选(blank=True )。 少于等于30个字符。 |
email | 可选(blank=True )。 邮箱地址。 |
password | 必选。 密码的哈希加密串。 (Django 不保存原始密码)。 原始密码可以无限长而且可以包含任意字符。 |
groups | 与Group 之间的多对多关系。 |
user_permissions | 与Permission 之间的多对多关系。 |
is_staff | 布尔值。 设置用户是否可以访问Admin 站点。 |
is_active | 布尔值。 指示用户的账号是否激活。 它不是用来控制用户是否能够登录,而是描述一种帐号的使用状态。 |
is_superuser | 是否是超级用户。超级用户具有所有权限。 |
last_login | 用户最后一次登录的时间。 |
date_joined | 账户创建的时间。 当账号创建时,默认设置为当前的date/time。 |
上面缺少一些字段,所以后面我们会对当前内置的用户模型进行改造,比如说它里面没有手机号字段,后面我们需要加上。
(2)重要方法
Django 用户认证(Auth)组件需要导入 auth 模块
# 认证模块
from django.contrib import auth
# 对应数据库用户表,可以继承扩展
from django.contrib.auth.models import User
(1)用户对象
create() # 创建一个普通用户,密码是明文的。
create_user() # 创建一个普通用户,密码是密文的。
create_superuser() # 与create_user() 相同,但是设置is_staff 和is_superuser 为True。
set_password(*raw_password*)
# 设置用户的密码为给定的原始字符串,并负责密码的。 不会保存User对象。当None为raw_password时,密码将设置为一个不可用的密码。
check_password(*raw_password*)
# 如果给定的raw_password是用户的真实密码,则返回True,可以在校验用户密码时使用。
(2)认证方法
auth.authenticate(username,password)
# 将输入的密码转为密文去认证,认证成功返回用户对象,失败则返回None
(3)登录和注销方法
from django.contrib import auth
# 该函数接受一个HttpRequest对象,以及一个认证了的User对象。此函数使用django的session框架给某个已认证的用户附加上session id等信息。
auth.login()
# 该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。
auth.logout()
(4)request.user
Django有一个默认中间件,叫做AuthenticationMiddleware,每次请求进来都会去session中去一个userid,取不到的话,赋值request.user = AnonymousUser() , 一个匿名用户对象。
当用户组件auth.login一旦执行,将userid到session中后,再有请求进入Django,将注册的userid对应的user对象赋值给request.user,即再后面的任何视图函数中都可以从request.user中取到该客户端的登录对象。
8.3、Django的分页器
[外链图片转存中…(img-l5Zjk7Kh-1713238246529)]
(1) index视图
def index(request):
'''
批量导入数据:
Booklist=[]
for i in range(100):
Booklist.append(Book(title="book"+str(i),price=30+i*i))
Book.objects.bulk_create(Booklist)
分页器的使用:
book_list=Book.objects.all()
paginator = Paginator(book_list, 10)
print("count:",paginator.count) #数据总数
print("num_pages",paginator.num_pages) #总页数
print("page_range",paginator.page_range) #页码的列表
page1=paginator.page(1) # 第1页的page对象
for i in page1: # 遍历第1页的所有数据对象
print(i)
print(page1.object_list) #第1页的所有数据
page2=paginator.page(2)
print(page2.has_next()) #是否有下一页
print(page2.next_page_number()) #下一页的页码
print(page2.has_previous()) #是否有上一页
print(page2.previous_page_number()) #上一页的页码
# 抛错
#page=paginator.page(12) # error:EmptyPage
#page=paginator.page("z") # error:PageNotAnInteger
'''
book_list = Book.objects.all()
paginator = Paginator(book_list, 10)
page = request.GET.get('page', 1)
current_page = int(page)
try:
print(page)
book_list = paginator.page(page)
except PageNotAnInteger:
book_list = paginator.page(1)
except EmptyPage:
book_list = paginator.page(paginator.num_pages)
return render(request, "index.html", {"book_list": book_list, "paginator": paginator, "currentPage": current_page})
(2) index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h4>分页器</h4>
<ul>
{% for book in book_list %}
<li>{{ book.title }} -----{{ book.price }}</li>
{% endfor %}
</ul>
<ul class="pagination" id="pager">
{% if book_list.has_previous %}
<li class="previous"><a href="/index/?page={{ book_list.previous_page_number }}">上一页</a></li>
{% else %}
<li class="previous disabled"><a href="#">上一页</a></li>
{% endif %}
{% for num in paginator.page_range %}
{% if num == currentPage %}
<li class="item active"><a href="/index/?page={{ num }}">{{ num }}</a></li>
{% else %}
<li class="item"><a href="/index/?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %}
{% if book_list.has_next %}
<li class="next"><a href="/index/?page={{ book_list.next_page_number }}">下一页</a></li>
{% else %}
<li class="next disabled"><a href="#">下一页</a></li>
{% endif %}
</ul>
</div>
</body>
</html>