MVC框架简介
软件框架是针对某类设计而需要搭建的,软件框架是很多个模块组成,我们学习框架就是学习各个模块的功能。
M模型(用于和数据库交互),V视图(产生html页面),C控制器(接收请求,做出相应,与MV交互)
MVC是让专门的人做专门的事,不同人有不同的分工
MVC核心思想是解耦,即各设计的模块是可以更换的,类似电脑硬盘坏了,我们换一个硬盘就行,而不是整个电脑
MVC被各种语言应用到不同的开发中去,我们Django就是MVC思想的web开发应用
我们举个例子介绍MVC框架的功能,以用户注册账号为例,见下图
我们分析流程:
1在浏览器端用户填写信息,点击提交
2控制器C收到后交给模型M
3模型M将信息和尝试发给数据库
4通过校验返回是否保存成功给模型M
5将这个保存结果给控制器
6通过控制器给视图V生成对应页面
7视图V将页面交给控制器C
8通过控制器C将页面交给浏览器端,让用户看到是否注册成功
Django简介
Django对于MVC思想又有了自己的结构,MVT
M模型,V视图,T模板
类比之前MVC注册实现,这里V实现原MVC控制器功能,T模板实现MVC的V视图的功能
M和数据库交互
V进行请求处理,和MT交互,应答处理
T生成html页面
Django开发2个原则,1快速开发,2DRY(DoNotRrepeatYourself)不要重复原则
Django我们希望的是,1多看源代码并理解,2多看文档,有中文文档可以查阅
Django虚拟环境搭建
我们unix环境下使用sudo pip3 install django但是这里需要注意一个问题,我们安装软件时,比如我们之前的是django1.9但是我们老板后面要求使用django2.0.1这样我们安装就会把之前的版本覆盖掉,就会导致我们之前使用的项目不可用
我们要安装virtualenv和virtualenvwrapper两个包,然后编辑家目录的.bashrc文件,添加
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh
修改并保存
使用source .bashrc使其生效
这里需要注意的是我按照如上视频讲解出现了小坑,在此记录下,
第一个export是建立个文件夹指定输出位置,这里.virtualenvs是隐藏文件,这条倒是没什么
第二行,我的ubuntu并没有这个路径,which一下,看看virtualenvwrapper.sh的位置,将其改好并保存,这才算.bashrc文件处理完,
然而当我source .bashrc的时候又出错了,经过查阅发现virtualenvwrapper.sh默认是python操作,实际我是使用python3,这里需要gedit下virtualenvwrapper.sh
查找到
if [ "$VIRTUALENVWRAPPER_PYTHON" = "" ] then
VIRTUALENVWRAPPER_PYTHON="$(command \which python)"
fi
将which python替换为which python3,保存后,
执行source .bashrc才OK
创建虚拟环境
视频中由于有python的2个版本,需要额外指定,我们使用mkvirtualenv 虚拟环境名 即可实现创建虚拟环境(必须有网,会自动下载东西)
我们创建完虚拟环境就可以看到账户开始是有虚拟环境名被括号包起来
我们在虚拟环境里pip3安装就会只安装在这个虚拟环境里,这样我们创建不同的虚拟环境就可以搭建不同版本的django
虚拟环境创建文件的话,也会真是存在到linux文件夹中,退出也不会消失
退出虚拟环境
deactivate
选择虚拟环境工作
workon 虚拟环境名 直接选择虚拟环境工作
workon输入完直接按2个tab键,就有可选择的各种虚拟环境
删除虚拟环境
rmvirtualenv 虚拟环境名
在虚拟环境下,我们可以使用pip list查看已经安装的包
我们可以安装指定版本的django pip install django==1.8.2
使用虚拟环境不能使用sudo,否则会安装到无虚拟环境的python里
Django项目创建
进入虚拟环境
创建项目目录并切换到目录
终端输入django-admin startproject 项目名
我们可以创建完毕后,可以看到一个项目名的文件夹,我们tree一下,可以看到如上图的结构
__init__.py说明是一个包
settings.py是各种配置相关
urls.py进行url路由配置
wsgi.py是web服务器和django交互的入口
manage.py管理整个项目
我们比如说要设计天天生鲜功能,需要设计几个模块,用户模块,视频模块,购物车模块,订单模块
在Django里每个模块用一个app应用实现
我们使用python manage.py startapp 应用名 创建应用
比如我们创建booktest应用,ls可以查询到多了一个booktest文件夹
我们切换到booktest然后tree一下,可以看到有上图结构
2个__init__分别说明目录其是一个模块
models.py写和数据库相关的内容(M模块)
views.py接收请求,与MT交互,完成应答(V模块)
在views.py我们定义处理函数,视图函数
tests.py是我们写测试代码的文件(这部分一般是测试人员来写)
admin.py是和网站后台管理相关的
migrations文件夹我们之后讲
注册应用
我们应用创建完毕,就需要将其注册,具体方法时,编辑器(pycharm)打开项目
我们点选项目test1的settings.py进行配置,找到INSTALLED_APPS项目,在最后添加字符串,内容为我们应用名booktest,保存后可以体验运行,命令python manage.py runserver
提示我们服务器已经运行在本机的8000端口上,我们可以用浏览器打开,
ORM框架
Django框架功能齐全自带数据库操作功能
当我们的程序涉及到数据库相关操作时,我们一般都会这么搞:
创建数据库,设计表结构和字段使用 MySQLdb 来连接数据库,并编写数据访问层代码业务逻辑层去调用数据访问层执行数据库操作
ORM是什么?:(在django中,根据代码中的类自动生成数据库的表也叫--code first)
ORM:Object Relational Mapping(关系对象映射)
类名对应------》数据库中的表名
类属性对应---------》数据库里的字段
类实例对应---------》数据库表里的一行数据
obj.id obj.name.....类实例对象的属性
Django orm的优势:Django的orm操作本质上会根据对接的数据库引擎,翻译成对应的sql语句;所有使用Django开发的项目无需关心程序底层使用的是MySQL、Oracle、sqlite....,如果数据库迁移,只需要更换Django的数据库引擎即可;
Django连接MySQL
1 创建数据库
由于Django自带的orm是data_first类型的ORM,使用前必须先终端创建数据库
create database booktest default character set utf8 collate utf8_general_ci;
2 修改project中的settings.py文件中设置 连接 MySQL数据库(Django默认使用的是sqllite数据库)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'booktest', #上一步创建的数据库
'USER': 'root',
'PASSWORD': 'mysql',
'HOST': 'localhost',
'PORT': 3306,
}
}
3 修改project中的settings.py文件中时区设置
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Shanghai' (只是修改这行,让时区为东八区)
4 修改project 中的__init__py 文件设置 Django默认连接MySQL的方式
import pymysql
pymysql.install_as_MySQLdb()
5 编写models.py文件(模型类**)
models文件开头Django已经为我们引入了models模块
我们在里面需要写自己的类(即数据库的表),这个类要继承自models.Model
我们给表写2个字段(类属性),btitle,bpub_date
其中btitle使用models.CharField(max_length=20)定义了最大长度为20的字符串字段
bpub_date使用DateField()创建日期字段
这样,我们的表结构就创建好了
6 迁移文件
我们写了models.py还需要迁移,才能将我们创建的表结构落实到数据库里(这个数据库我们之前在settings.py已经配置好数据库名和端口,我们这里使用mysql)
6.1生成迁移文件命令
终端输入python manage.py makemigrations
我们可以看见创建了模型BookInfo模型
运行完命令可以看到我们的migrations,文件夹多了个initial文件,我们点击可以看到里面的迁移类,
同时我们还可以看到我们定义的表格在里面,由于我们使用Mysql, django给我们自动创建了表格的主键字段id作为第一个字段(所以我们一般models不写),当然还能看见我们的btitle,bpub_date字段
我们运行了就会生成迁移文件,但是表还没有创建出来,需要下一步
6.2迁移文件生成表
终端执行命令python manage.py migrate
执行命令,会出现上图结果,我们可以看到倒数第二行,执行了我们的迁移文件
我们就可以在mysql数据库里查询到数据库booktest下的数据
我们可以看到booktest有很多表,很多是django自动帮我们建的,我们需要留意的是booktest_bookinfo表,这个表就是我们创建的表,表名是由应用名下划线和我们的models使用的类名(的小写)共同拼接成
我们可以查看表结构,可以看到我们之前创建btitle实例,则这个实例就是字段名,还有id,bpub_date
模型操作数据表
我们之前介绍了ORM框架可以自动实现各数据库的功能,并进行了封装统一
插入数据
我们先进入虚拟环境,终端输入python manage.py shell 进入终端的python shell界面,
我们使用数据结构需要先导入我们定义的表格类from booktest.models import BookInfo
我们需要创建一个类实例对象b,我们插入信息就是给b的字段名赋值,像字符串我们赋给字符串就行,对于日期类型,我们导入datetime的date函数,赋值
然而这些还不够,我们需要执行b.save()才能将这条数据保存到数据库里
可以通过终端查询或者客户端查询到我们插入了这条记录
查询数据
我们可以查询数据,是通过BookInfo.objects的get方法,里面设置条件id=1,
这个查询结果可以返回一个BookInfo对象,我们也可以访问到他的属性
修改数据
我们可以修改这个返回的属性,比如修改了日期,我们修改后,需要保存,就能实现修改数据
所以save对于刚创建的是插入,对于已有的是修改
删除数据
直接使用对象的delete()方法,数据直接删掉,而且不用save,数据库已删掉数据
模型类关系和关系查询
我们之前的model里创建了BookInfo类,这次我们要创建英雄人物类,因为小说名和英雄是一对多的关系,我们可以在英雄人物类加上前一类的外键约束
于是我们就在models.py继续写一个英雄人物类,HeroInfo类,当然还是要继承models.Model,
我们定义hname姓名,hgender性别(我们使用布尔型,因为男的多,默认设置false,即男性),hcomment英雄简介(字符多些)
hbook这里就是作为外键使用的,models.ForeignKey('BookInfo'),内部参数是我们之前创建的BookInfo类名
创建好新类,我们就可以生成迁移文件,python manage.py makemigrations
可以看到上图信息,创建了新的迁移文件,创建了类HeroInfo
我们可以在Pycharm中直观的看到我们新生成了迁移文件(也帮我们自动添加了id主键),我们生成迁移文件,不能删之前的迁移文件,因为我们可以看到dependencies里面写了是关于上一个迁移文件的,即经过多次迁移文件实现最终改动,每次迁移是针对上次的相对改动
然后我们就可以迁移表python manage.py migrate
此时我们就可以使用数据库查看我们创建的表,这里我们能看到外键字段的名字不是我们写的hbook,而是hbook_id,这是外键设置的字段名模式,使用属性名_id,(上图由于数据库跑在另一个电脑上,我截图麻烦,就使用的视频中sqlite3的客户端效果,Mysql也是这个名字)
表格创建完,我们就可以实现操作,我们还是使用python manage.py shell进入界面
导入我们创建的2个类,由于上节课我们把书给删了记录,这次我们再次创建实例对象b保存,同时也创建也一个英雄的实例对象(其实性别是默认值,可不写)并设置属性,这里需要注意的是我们如何给我们设置的外键赋值
将实例的外键属性(并非是表格字段那个带_id的,以我们定义的类为准)赋予其对应的b实例
然后保存,即可看到记录
可以看到英雄表的外键和书表对应
同理我们可以创建一个乔峰对象,然后设置他的hbook属性为b对象,当然增删改查也和之前一样,比如我们查找id=2的英雄,调用表.objects.get(条件),返回对象h3,我们的h3的hbook_id就是2,即书表的对应书的id,我们也可以调用hbook属性,hbook属性是对应书籍对象,我们就可以进一步获得书籍的信息
同样,因为b对象被关联2个英雄,我们可以用b对象的heroinfo_set.all()获得跟他关联系的信息,默认是返回列表,里面的元素是英雄类实例,与之关联的对象集合名为类名小写_set
所以,关联和被关联的查询关系如上
查询自身表
使用类名(区分大小写).objects.all()
Django后台管理
我们之前说了应用booktest下面的admin.py文件就是后台管理文件
后台管理需要以下几步:
1本地化
我们为了设计,就要用本地化设置,修改项目的settings.py文件并保存,如上图
设置语言,我们之前好像没有改LANGUAGE_CODE设置里设置为'zh-hans',
时区设置,之前说过了设置为‘Asia/Shanghai’
2创建超级管理员
终端python manage.py createsuperuser
我们输入命令,会提示我们输入超级管理员用户名,默认是Linux用户名,我们输入admin,
邮箱这里随便写就行(学习是这样),然后输入2次密码,我这里设置123
显示如上过程,超管注册成功
然后我们去运行服务,python manage.py runserver
我们就可以打开浏览器,选择127.0.0.1:8000登录服务,然而我们为了是进入后台管理,还需要如图中加上/admin
我们就有了如上的登录界面,输入我们刚才创建的admin和密码,回车或点按钮登录
登录后显示如上页面,我们可以点击左下的用户,就可以看到我们创建的admin用户
我们的组暂时没有任何内容,先不要管它
我们为什么进入管理页面,就是为了在这里管理我们的数据库,表
但是我们进来发现并没有这些信息。就需要下一步操作
3注册模型类
通知我们的Django框架注册模型类
比如我们想看到图书表内容,我们就需要给图书注册
注册需要在app应用booktest里的admin.py来写注册内容
admin.py已经给我们导入了admin,再次基础上我们只需要从models.py导入我们定义的类,并注册即可
如上图,使用admin.site.register(类名),这里我们使用BookInfo
然后我们刷新页面,就能看见有Book infos信息
我们点进来就会看见有个BookInfo object,如上图
再点击这个Bookinfo object,就可以看见我们书表里之前加进去的书信息
到此我们就能看到我们django给展示的效果。
这里还有为什么图48显示的是BookInfo object呢,是页面默认使用类实例的str方法转化成字符串,在python里,显示的就是下图的BookInfo object,
我们想让页面显示为书名呢,就需要在BookInfo加上__str__魔法方法,并返回名称,如下
我们回到后台管理界面,就可以看到信息显示为书名了
通过页面添加信息
我们页面显示所有的book info信息,我们可以点击右上角添加book info来添加信息
点击后,进入如上界面,我们给新书添加名称,日期因为我们设置了date型,可以手动输入,也可以点击今天,也可以选择日历,我们点击右下角保存,就跳转回上一页面,并且显示了我们新添加的记录,如下图
页面编辑信息
就直接点击记录,进入修改页面
如上图,我们点击保存,即可实现更改
页面删除信息
我们点击记录前面的出对勾选中,再点击动作下拉菜单,选择删除操作,点击执行
点击后跳转到如下页面,我们会看见提示是否删除,最终实现是否删除
当然删除多项就之前的界面勾选多个
当然我们也可以点击记录进修改页面
然后点击删除来调整到2次确认删除,这是实现单条删除
关于查找,我们到后面再给大家讲解(视频老师的问题)
同理,我们可以给HeroInfo类也添加进来,过程不赘述,
我们给其添加记录时,由于hbook属性设置了外键,我们选择信息只能从下拉列表框里选择
设置显示字段
我们之前的设置信息只能显示一个名称,我们期望把其他属性也显示上,我们就需要定义管理类
我们回到之前的admin.py。在这里我们需要新建一个类,类名虽然随便起,但是我们建议一看就能知道是管理谁的,比如上图,我们建立BookInfoAdmin类,并让其继承自admin.ModelAdmin,其中我们只需要设置类属性列表list_display为我们要显示的属性名的字符串列表,
建完类还不算完。我们还要给之前增加的注册信息后面,加上我们的这个管理类名(同一个表类名不能重复注册,后台会报错)
我们刷新页面就能看见我们新设置的信息
以上就是我们说的模型类的注册管理,下面我们介绍视图V
视图
我们通过浏览器请求一个页面时,会要求视图函数进行处理,处理后,返回对应的内容
视图函数要写在我们的views.py里
1 定义视图函数
我们点进views.py,会发现Django默认给我们导入了render
我们邓毅视图函数index,其参数是request(http请求)
我们返回HttpResponse对象,内容先按上图写一个字符串,这个类需要导入,我们在开始加上
from django.http import HttpResponse
2我们定义了函数,第二部就是进行url流的配置,即创建资源-处理函数的字典
我们给booktest新建urls.py文件(我们的项目test1里也有,我们都要设置)
我们先不急写booktest的urls内容,先进入到test1项目的urls,我们会发现已经有了一个列表urlpatterns,默认已经有了一个配置项
我们需要再写入一个配置项url(r'^',include('booktest.urls')) 其中url前面的参数相当于一个正则表达式,后面的是include,我们写上我们创建的文件名字符串'booktest.urls'
写完后,我们回到应用的urls
先导入url,使用from django.conf.urls import url
然后导入应用的views以导入对应的函数,from booktest import views
然后我们也创建一个urlpatterns列表(名字不能写别的!),参数为url(r'^index',views.index)网页资源和函数的对接
保存后,我们终端输入python manage.py runserver启动服务,请求index页面就会实现了我们index函数返回的字符串内容
现在就来解释下,我们之前乱七八糟设置一堆是为什么,我们访问127.0.0.1:8000/index,
django会帮我们把请求资源index给获取出来,然后就会先在项目里的urls里urlpatterns列表里找,
如上图,他会按正则依次取找对应的内容,第二个项目匹配(admin肯定不能匹配),匹配成功了,就会执行后面的include动作,django就会到booktest.urls里去继续匹配,当然我们booktest的urls已经写好了匹配内容,后面的执行的对象就是views.index函数,最终显示到网页端。
Django怎么显示的我们先不管心,我们就是需要知道如何配置项目的urls列表,和应用的urls列表,最终和我们的views里对应的函数对应上,函数如何响应,就返回对应的内容
再比如我们想定义函数index2,让我们访问本机8000端口index2时,返回字符hello python
因为项目的urls不用改,肯定能保证进booktest的urls
我们修改booktest的urls,再添加url如上图的一个元素,但是我们访问网页的时候会发现,还是index的内容而不是index2,这是为何,其实我早就明白了,因为按列表,第一个已经正则匹配了,就轮不到第二个,只有不匹配,才会在列表中继续找
我们修改正则表达式,让其严格匹配开头结尾,即可
当然我们要是想127.0.0.1:8000/index2/也能匹配就得在列表里加项目,使其带斜杠也能找到index2,否则上图的严格匹配会404
模板的应用
我们之前的应用直接返回了字符串内容页面,而不是正常项目绚丽多彩的资源,我们要生成html页面内容,就需要使用模板
1 我们创建模板之前需要先在项目的目录下创建一个模板的文件夹templates (项目这里是外边那个test1)
2 我们创建完就要配置模板路径,在项目的settings.py里我们找到TEMPLATES列表,我们修改DIRS的值,默认是空列表,这里有人或说直接写templates的绝对路径,这样如果项目移动就会出错,这个settings.py开始定义了项目的绝对路径BASE_DIR参数,我们直接使用[os.path.join(BASE_DIR,'templates')]这样如果我们项目迁移,也不会出错
3 创建模板文件
我们文件夹创建并配置完就可以创建模板文件了,我们因为一个项目有很多模板文件,我们会给templates文件夹下再跟据应用模块创建文件夹,比如我们有booktest应用了,我们就创建booktest文件夹
我们就在booktest下写一个index.html文件(看过html5简直不要太简单)
这回我们想使访问index的时候返回的是我们的模板文件,我们就需要去修改views.py的index函数,我们就按下图步骤使用模板文件
4使用模板文件
4.1 加载模板文件
我们需要修改views.py的内容。首先,比之前额外导入加载器,使用
from django.template import loader
然后创建模板对象,在index.py里写上temp = loader.get_template('booktest/index.html')
这里传入的参数是相对于之前配置DIRS的路径的相对路径字符串
4.2 定义模板上下文
定义模板上下文的目的是给模板传入数据
我们需要给views.py导入请求上下文RequestContext也在django.template里,就和loader写一起了
然后我们继续在index函数里创建上下文对象context=RequestContext(request,{})
本来要传递参数是要写在字典里的,但是我们页面还没需要传递值,这里暂时写空字典,第一个参数就是index函数接收的参数request请求
4.3 模板渲染
模板要经过渲染,生成最终的html页面
我们获得的上下文需要渲染,使用temp.render(context)返回给res_html,然后我们使用HttpResponse将res_html返回即可
当然,我们对每个函数,都有创建模板对象,创建上下文,模板渲染,我们可以按上图给封装一下(后面马上就会说这是多余的)
事实上我们真正使用的是用开始views.py已经导入的render,之前还是灰色,因为我们都没有用,这是django已经给我们封装好了,我们传入参数,第一个为请求request,第二个为模板路径,第三个为传入参数,若没有需要传的参数可以省略(所以开始介绍的几步只是为了我们理解内部机制)
我们刷新页面,就会看到我们写的html页面显示了出来
我们之前使用的是没有传递参数的渲染,我们需要传递参数时怎么用
比如说,我们给第三个参数传递了渲染字典参数,键名要求字符串,这里我们传递'content'值'hello wordl'
然后就是如何传递参数给模板文件了
使用模板变量
我们在index.html文件里,找个位置使用双花括号括起来,这就使用了模板变量,内容为我们定义的参数键,这样我们刷新页面,就会看到我们把hello world添加进来
我们还可以试着给参数字典添加一个list键,值为一个列表
我们可以看到上图,有一个是双花括号括起来的list,不用说,这部分只是打印列表,没什么说的,和上个差不多,但是下面是django对模板文件循环的写法
循环for那一行用{%%}包括起来,而且和python比没有冒号,还有要注意有空格
下面是循环的内容,使用遍历元素还是要用双花括号括起来
最后确定循环体完毕要加上{% endfor %},也注意有空格,
我们刷新下页面就会看到最终效果
模板里面还有很多其他内容,我们讲到再说~~
图书案例(重定向)
我们之前的项目定义了models.py的模型类,现在我们说一下新的案例要求,我们请求index页面的时候,显示如下图,里面ur/li显示所有数据库内BookInfo记录,点击上面的新增,就固定添加一条流星蝴蝶剑的信息,点删除就页面和数据库删除这条记录
我们配置模板和之前一样,我们这里给views.py的视图函数index做修改,接收参数当然还是request(默认要求),我们要读取数据库信息,就需要就从模型类导入BookInfo
from booktest.models import BookInfo,然后我们返回渲染,参数1还是request,参数2是booktest/index.html被渲染页面,参数3我们将数据库获得的信息列表给字典books做值
当然我们设置完记得还得确定项目的urls
然后新建应用下的urls配置
搞定urls,我们就可以写index.html页面
我们给ul.li结果显示书名,这个应该没难度了,上节课讲过list,页面写完。我们可以命令行启动服务
python manage.py runserver 8001 (视频中因为端口8000还被占用没释放,改用8001,)
如上图,但是离我们要求的还差一些,我们还需要新增,删除功能
先看新增功能,我们要做一个超链接
我们添加超链接,a的资源使用/create,这样我们点击了就会请求create资源,因为我们还没配置,所以需要给view.py曾加create函数,同时urls里增加create的url正则索引
我们先简单给请求create返回ok,这样我们点新增,就会到OK页面,同时切换到index,也能看见新增了流星蝴蝶剑,但是我们希望的是就只在index页面的效果
我们希望我们访问create又返回index页面,就需要重定向,我们使用
from django.http import HttpResponseRedirect
我们create函数这时返回的就是这个重定向对象,参数是新访问的资源,我们设置为/index字符串
这样我们点击新增就会增加了信息
可以看到运行服务的终端先访问了create资源,然后重定向到index
我们可以按上图理解重定向,服务器不返回也么,而是告诉浏览器去请求其他url地址
当然如果就简单重定向就很low了,我们这么用因为我们中间还有操作(这里是给数据库插入记录)
现在我们说删除部分,我们给每个li标签后面加一个a标签,请求资源为delete,但是我们不能点什么书后面的删除都一样,我们要指定删除,因此,我们在资源后面加上了book.id用双花括号包起来
这样我们点击不同的书,后面就请求不同的delete,比如id=1,点删除就是请求delete1
请求ok,我们就给视图view.py更新视图函数,并通过urls.py指定索引
我们新建delete函数,这里需要除了request参数外加bid参数,我们使用图书对象查找到id=bid的对象,
book=BookInfo.objects.get(id=bid)
然后调用book.delete()的方法,就实现删除,当然,我们点删除希望还是在Index页面,我们还是返回重定向到index
设置完视图函数,我们会想这个bid是怎么传入的呢,这里就要靠urls的设置
我们这里正则部分使用()组匹配,这样,就会自动识别捕获的参数给函数做参数,即(\d+)实现给bid
修改ok后,我们刷新页面就可实现删除了
关于view.py重定向,我们还可以使用函数redirect,在django.shortcuts模块下
from django.shortcuts import redirect
这样我们返回使用redirect函数,路径还是请求的资源就行了,和HttpResponseRedirect效果一样
我们需要说的是我们超链接这里还有重定向为什么要前面加/
如果我们没加斜杠,而且我们之前的浏览器访问index的时候设置可以带/结尾,就会在index页面的网址进行拼接,就变成了127.0.0.1:8000/index/create资源请求,显然我们没有配置这个页面,所以会404错误,因此我们重定向或者超链接前这种设置要加/开头
模型类属性的规范
我们在python的models.py定义模型类,里面是我们写的一堆类属性,各属性命名有如下要求
1 不能是python的关键字
2 不能出现连续下划线,像__,___甚至更多,这是django的查询方式决定的
3 属性定义
我们之前也知道models.xx给属性设置类型,我们拿几个表格看下(有之前我们没讲过的)
AutoField自增整形,通常不需要我们设置,自动创建的id一般就django直接给设置这个类型
BooleanField布尔字段,True,False没啥说的
NullBooleanField,比前者多了一个Null可选值
CharField,必须带上参数最大长度,否则会报错
TextField,存大文本字段
IntegerField,整数没啥说的
DecimalField ,FloatField 都是浮点数,2者参数都一样,max_digits显示总位数,decimal_places表示小数位数,二者区别是前者为精确存储,后者为计算机存储,如果需要价格金钱计算准确计算,我们需要使用前者
DateField时间,2个参数,默认都是false,auto_now设置为True,则创建时间给这个属性,不用我们赋值,auto_now_add=True设置每次最后一次修改记录的时间,这2个参数是互斥的,我们每次只能选一个为True
TimeField,DateTimeField参数同上
FileField,ImageField分别对文档和图片进行上传,我们后辈会讲
接着我们对数据类型的属性进行讲解
default设置默认值
primary_key设置主键(默认为False)
unique设置字段内容不能重复,可以为null,但只能一个null
db_index为字段创建索引,使我们查询快速
db_column指定字段名称,若没有指定,使用属性名
null是否允许字段内容有空值,默认为False
blank,默认为False跟django后台管理相关,我们进入后台管理页面时,对blank为True的内容可以不填保存
还有很多属性,我们可以去中文文档去看看
我们修改了模型,改了属性,记得要再次生成迁移文件并迁移(只有default,blank修改可以不用迁移)
python manage.py makemigrations
python manage.py migrate
查询
终端查询
首先我们要修改mysql日志文件
找到编辑/etc/mysql/mysql.conf.d/mysqld.cnf文件
我们将这两行注释的#删掉保存,重启mysql服务sudo service mysql restart
我们重启服务后,我们就能在/var/log/mysql里看到mysql.log日志文件,mysql每次操作,都会记录在这里
我们可以使用sudo tail -f /var/log/mysql/mysql.log我们就可以看见mysql的所有日志记录,而且光标一直停留在结尾,所有对mysql的操作都会记录显示
如上,我们连接mysql,然后显示数据库,可以看到mysql日志端有显示
模型类查询ORM
我们使用模型类查询并不需要我们使用mysql或其他的查询命令,而是通过映射封装,get,all等方法
get 返回标准可满足某种条件的一条数据(只能是1条),即一个记录对象,如果查询不到,抛出 DoesNotExist异常,如果是多条,抛出MultipleObjectsReturned异常
all 返回模型类对应所有的查询集合QuerySet
filter 返回满足条件的查询集合QuerySet(参数写查询条件)
exclude 返回不满足条件的查询集合QuerySet(参数写查询条件)
order_by对结果排序,返回的查询集合QuerySet
我们之前以为是books返回的是一个列表,其实是个查询集对象,是可遍历的
filter查询分以下几种:(都是属性双下划线符号=条件)
1精确查询
如BookInfo.objects.filter(id__exact=1)这里__exact可以省略
2模糊查询
包含contains
如Bookinfo.objects.filter(btitle__contains='传')
以xx开头startswith
如Bookinfo.objects.filter(btitle__startswith='天')
以xx结尾endswith
如Bookinfo.objects.filter(btitle__endswith='部')
3 空查询isnull
如Bookinfo.objects.filter(btitle__isnull=False)
4范围查询in
如Bookinfo.objects.filter(id__in=[1,3,5]) (可以列表,也可以元组)
5 比较查询
gt大于,lt小于,gte大于等于,lte小于等于(greater than,less than,equal我们对于到这几个词)
比如我们查询id大于等于3
Bookinfo.objects.filter(id__gte=3)
6 日期查询
Bookinfo.objects.filter(bpub_date__year=1980)
我们终端一直监视的日志信息可以看到,查询了between and的效果
当然我们也可以__month,___day查询月日
如果比较日期查询,比如查询1980.1.1以后的书籍
Bookinfo.objects.filter(bpub_date__gt=date(1980,1,1)) date使用的是datetime的date
orderby查询
例按照id从小到大排序
Bookinfo.objects.all().orderby('id') 里面填写字段名字符串
按id降序
Bookinfo.objects.all().orderby('-id') 和升序的差别就在于多个负号
以上都是查询所有信息,然后排序
关于查询所有信息。我们可以写Bookinfo.objects.all(),也可以直接写Bookinfo.objects(我想说MD不早说,这老师的顺序有点天马行空)
当然,升降序,可以填入多个参数,优先按第一个参数排序,相同再按第二个,以此类推
Bookinfo.objects.orderby('id','bpub_date')
当然我们可以给筛选后的对象排序
Bookinfo.objects.get(id>3).orderby('bpub_date')
逻辑查询
之前讲的filter,get,exclude等查询是可以加多个条件的,这是各关系是‘与’即共同满足的关系
如Bookinfo.objects.get(id>3,btitle__startswith='天')
但是我们想要实现或逻辑就不能简单显示了,就需要使用Django自带的Q对象
使用Q对象,我们需要导入Q类,
from django.db.models import Q
我们要把条件传入Q(条件),Q对象之间可以使用与或非符号& | ~ (只有一个而不是2个&&||这样)
例
Bookinfo.objects.get(Q(id>3)&Q(btitle__startswith='天'))
Bookinfo.objects.get(Q(id>3)|Q(btitle__startswith='天'))
Bookinfo.objects.get(~Q(id>3)) (也可以用exclude) 非是波浪号
如果我们想使用两个属性间的比较而不是一个属性的比较,那就需要使用F对象
导入F from django.db.models import F
Bookinfo.objects.get(id__gt=F('bread')) 找到书中所有id大于阅读量的书
找到书中所有id大于阅读量2倍的书,我们可以给F信息做乘法
Bookinfo.objects.get(id__gt=F('bread')*2)
注意F对象参数为字段名字符串
聚合函数
使用聚合函数我们需要先使用聚合类
from django.db.models import Sum,Avg,Count,Min,Max
几个聚合类看名称就知道是做什么用的了,问题是怎么使用,我们要和聚合函数aggregate配合,
聚合函数返回的结果是个字典,键是字段名__聚合类小写,值是获得的结果
比如说我们尝试获得所有书的数量
Bookinfo.objects.aggregate(Count('id'))
我们可以看到上图,返回了统计数量的字典,注意我们使用Count时,不能传入'*',必须指定字段名,因为有主键,我们使用id即可,同样,图中还演示了Sum
图中其实我们也可以使用count()统计数量,返回的是数值
至于我们为什么要返回字典这种形式呢,有人会感觉罗里吧嗦,其实我觉得应该是送给视图函数拿去做参数用的,正好直接传字典
查询集QuerySet
我们知道很多查询函数会返回查询集QuerySet,查询集有2个特性:
1惰性查询,只有使用查询集数据才会发生对数据库查询
2缓存,当使用同一个查询集,访问一次,会把结果缓存,之后使用缓存结果
对于惰性查询,我们使用books=Bookinfo.objects.all()时,终端日志监控端并没有查询Query显示,但是我们输入books获得值就会产生对应的请求
对于缓存,我们上面获得books,再次输入books查询,日志端就不会有Query请求
这2个特性都是为了缓解数据库压力,防止大量访问数据库造成压力
查询集支持切片,但是不支持负索引,切片后获得的是新的查询集,查询集可以[index]下标访问,但是要注意下标需要满足长度要求
取出查询集第一条数据可以使用b[0]也可以使用b[0:1].get()但是如果不存在,即查询集为空,分别抛出不同的异常,如上图
对于判断查询集是否有数据,我们也可以使用exists()方法,如上图
模型类之间的关系
1一对多
使用外键,我们之前图书对英雄就是1对多的关系,我们把英雄的hbook设置为models.ForeignKey('BookInfo')
这里谁为多,则属性设置为外键,参数对少的类名
2 多对多
因为是多对多,我们在任何一个多里面创建属性即可models.ManyToManyField('对方类名')
比如课程对学生,课程有很多类,每个类有很多学生选择,同时每个学生又选择很多课程
(视频中讲的例子简直是= =)
3 一对一关系
比如员工基本信息和详细信息,员工工号,在哪个类添加models.OneToOneField定义哪个类都行
模型类关联查询
我们之前讲过一对多多对一的查询
比如b=BookInfo.objects.get(id=1)那b对象就是查到id为1的数集,我们1对多查此书的所有英雄
可以使用b.heroinfo_set.all(),多对一,比如我们得到h记录,可以获得h.hbook获得对应数集的id
当然这都是建立好外键的基础上
下面我们讲的是通过模型类关联查询
首先,查询的结果要求是BookInfo的实例,所以要放前面
BookInfo.objects.filter(heroinfo__hcomment__contains='八')
我们分析语句,heroinfo为多类名小写双下划线获得其属性,再双下划线使用查询contains=
我们可以给shell输入,同时日志监视,可以看到我们的查询其实是实现使用了inner join关联查询
我们再练习下,查询书有英雄id大于3的
BookInfo.objects.filter(heroinfo__id__gt=3)
反过来我们查询英雄里书名为天龙八部的英雄
HeroInfo.object.filter(hbook__btitle='天龙八部')注意使用多对一查询前面不是类名小写而是直接使用的多类外键名,哪里定义了关联哪里就直接使用关联属性,否则使用类名
所以我们查询id=1书籍关联的所有英雄就可以用
HeroInfo.objects.filter(hbook__id=1)
我们查询英雄id=1关联的书籍
BookInfo.objects.get(heroinfo__id=1)
总结,模型类查询,我们最终要获得什么信息就要通过什么类来查(不要写反)
内关联
还记得内关联么,
我们省市县可以定义3个表,每级都是1对多
当然,我们也可以把他们定义到一张表上,id是主键,title是名称,parent_id定义他们的父id,如果是最上级就是null,这个表我们MYSQL那里也讲过
自关联是一种特殊的一对多
我们如上创建自关联,因为还是一对多,还是使用外键,参数是自身使用'self',我们允许为空null=True,不填blank=True
设置完我们可以迁移python manage.py makemigrations python manage.py migrate
我们可以mysql页面查看新建的表结构,可以看到外键的设置,视频中有areas.sql批量执行sql语句
直接在mysql界面source areas.sql批量执行,这样数据就实现导入(然而本人没有这个文件。所以就是说说)
然后我们设计当浏览器请求areas时,我们返回如上页面
既然是对应请求,我们就要视图页面写视图函数areas接收request,当前地区就由
area=AreaInfo.objects.get(atitle='广州市')
上级地区用我们的外键属性即可他对应的就是一条记录对象
parent=area.aParent
下级地区也很简单
children=area.areainfo_set.all()
最后返回渲染,(自关联是特殊的一对多,查询原则还是图127中对应的查询写法)
我们给urls添加索引,然后就是写html页面
我们写好后,就可以python manage.py runserver跑程序,实现功能
管理器
我们可以进入python shell,看到我们定义的类的objects是一个Manager对象
如果我们尝试在BookInfo类里定义管理类对象,则我们再使用BookInfo就会报错,就不能再使用objects,如下图,这是因为我们自己定义管理类后,django就不再给我们的类默认添加objects管理类了
此时若我们想访问所有书籍,就要使用BookInfo.book.all(),即下图中使用我们创建的管理类替代objects
我们有时候还创建一个新类继承这个Manager类,然后添加我们的功能,
比如说我们的书有isdelete字段,设为1是逻辑删除,但不是物理删除
我们想查询all的时候每次只在我们的isDelete为0里查找,这时我们就可以给新建的管理类添加额外的功能了
我们只需要修改all方法,将结果filter过滤,然后返回这个查询集即可
然后我们在BookInfo里新建一个实例,可以使用objects来命名
objects = BookInfoManager(),这样我们的BookInfo.objects.all()就可以查到逻辑未删除的对象
创建管理类的另一个作用时,我们想给数据库增删改查,我们可以给BookInfo里添加方法,但是我们使用中常常只定义模型类的属性对应关系,我们就可以把方法写在我们创建的管理类里
如上图,我们给管理类添加创建图书方法create_book
我们创建完就可以shell试试,添加OK,这里还需要说明的是DateField类型,我们可以给属性传字符串(之前我们用datetime.date),如上图,但是格式必须符合要求,django会自动给我们转换
然后视频老师讲着讲着比较坑的是,说Manager类自带create方法,传关键字参数即可,如下图。我真是日了,老是后返话
然后果不其然,又来说后话,我们定义的create_book声明了book=BookInfo(),但是如果我们BookInfo类名字发生变化就会比较麻烦,这里我们可以使用self.model属性,这样如果实例在哪里出现,就会将model赋值给他
最后总结自定义管理器类应用场景如上
元选项
我们的类BookInfo在数据库中存储名为booktest_bookinfo,如果有哪天我们修改了应用名称,我们的数据表就对不上,如果我们想创建的表名不依赖于应用,我们可以在模型类内部创建一个元类,方法如下
如上图,我们在BookInfo内部创建Meta类,设置属性db_table = 'bookinfo',标黑的部分都必须固定写法,要记住,然后重新迁移,这样我们就会创建bookinfo表,而不是带前面的应用下划线