MVC最初是由施乐公司旗下的帕罗奥多研究中心中的一位研究人员给 smalltalk语言发明的一中软件设计模式。
MVC的思想被应用在的web开发的方面,产生了web MVC框架。
通过浏览器注册用户信息
M:Model,模型, 和数据库进行交互。
V:View,视图, 产生html页面。
C:Controller,控制器, 接收请求,进行处理,与M和V进行交互,返回应答。
Django是劳伦斯出版集团的开发人员为开发新闻内容网站而设计出来的一个软件,它遵循MVC思想,但是有自己的一个名词,叫做MVT。Django遵循快速开发和DRY原则。
M:Model,模型, 和MVC中M功能相同,和数据库进行交互。
V:View,视图, 和MVC中C功能相同,接收请求,进行处理,与M和T进行交互,返回应答。
T:Template,模板, 和MVC中V功能相同,产生html页面。
安装python包的命令: sudo pip3 install 包名
包的安装路径:/usr/local/lib/python3.5/dist-packages
在同一个python环境中安装同一个包的不同版本,后安装的包会把原来安装的包覆盖掉。这样,如果同一台机器上两个项目依赖于相同包的不同版本,则会导致一些项目运行失败。
解决的方案就是:虚拟环境。
虚拟环境是真实python环境的复制版本。
在虚拟环境中使用的python是复制的python,安装python包也是安装在复制的python中。
/home/.virtualenvs/虚拟环境/lib/python
安装虚拟环境的命令:
sudo pip install virtualenv #安装虚拟环境
sudo pip install virtualenvwrapper #安装虚拟环境扩展包
编辑家目录下面的.bashrc文件,添加下面两行。
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh
使用source .bashrc使其生效一下。
创建虚拟环境命令:
mkvirtualenv + 虚拟环境名
创建python3虚拟环境:
mkvirtualenv -p python3 + 虚拟环境名
进入虚拟环境工作:
workon + 虚拟环境名
查看机器上有多少个虚拟环境:
workon 空格 + 两个tab键
退出虚拟环境
deactivate
删除虚拟环境:
rmvirtualenv + 虚拟环境名
虚拟环境下安装命令:
pip install 包名
apt-get install 软件名
注意:
不能使用sudo pip install 包名,这个命令会把包安装到真实的主机环境上而不是安装到虚拟环境中。
查看虚拟环境中安装了哪些python包:
pip list
pip freeze
安装django环境:
pip install Django==x.x.x # x.x.x为指定版本号
进入虚拟环境后
django-admin startproject + 项目名
项目目录如下:
.
├── dj_test
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
__init__.py: 说明dj_test是一个python包。
settings.py: 项目的配置文件。
urls.py: 进行url路由的配置。
wsgi.py: web服务器和Django交互的入口。
manage.py: 项目的管理文件。
进入项目目录后创建应用,命令如下:
python manage.py startapp + 应用名
应用目录如下:
.
├── admin.py
├── __init__.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
__init__.py: 说明目录是一个Python模块。
models.py: 写和数据库项目的内容, 设计模型类。
views.py: ,接收请求,进行处理,与M和T进行交互,返回应答。
定义处理函数,视图函数。
tests.py: 写测试代码的文件。
admin.py: 网站后台管理相关的文件。
建立应用和项目之间的联系,需要对应用进行注册。
修改settings.py中的INSTALLED_APPS配置项。
开发服务器
在开发阶段,为了能够快速预览到开发的效果,django提供了一个纯python编写的轻量级web服务器,仅在开发阶段使用。
返回上级项目目录中,运行服务器命令如下:
python manage.py runserver ip:端口
可以不写IP和端口,默认IP是127.0.0.1,默认端口为8000。
ORM框架
O是object,也就类对象的意思,R是relation,翻译成中文是关系,也就是关系数据库中数据表的意思,M是mapping,是映射的意思。在ORM框架中,它帮我们把类和数据表进行了一个映射,可以让我们通过类和类对象就能操作它所对应的表格中的数据。ORM框架还有一个功能,它可以根据我们设计的类自动帮我们生成数据库中的表格,省去了我们自己建表的过程。
django中内嵌了ORM框架,不需要直接面向数据库编程,而是定义模型类,通过模型类和对象完成数据表的增删改查操作。
使用django进行数据库开发的步骤如下:
模型类定义在models.py文件中,必须继承自models.Model类。
不需要定义主键列,在生成时会自动添加,并且值为自动增长。
设计BookInfo类。
# 图书类-一类
class BookInfo(models.Model):
"""需要继承models.Model才是模型类"""
# 图书名称:CharField()指定为字符串类型,max_length为指定字符串最大长度
btitle = models.CharField(max_length=20)
# 图书出版日版:DataField()声明是一个日期类型
bpub_date = models.DateField()
定义好的模型类,通过迁移生成相应的表,迁移前目录结构如下:
迁移由两步完成:
生成迁移文件
python manage.py makemigrations
执行迁移生成表
python manage.py migrate
生成表名的默认格式:应用名_模型类名小写
迁移后,目录文件如下:
Django默认采用sqlite3数据库,上图中的db.sqlite3就是Django框架帮我们自动生成的数据库文件。 sqlite3是一个很小的数据库,通常用在手机中,它跟mysql一样,我们也可以通过sql语句来操作它。
安装sqliteman命令
sudo apt-get install sqliteman
同样的方法设计HeroInfo类。
# 英雄人物类-多类
# 英雄名hname 英雄性别hgender 年龄hage 备注hcomment
# 关系属性:book 建立图书类和英雄类之间的一对多关系
class HeroInfo(models.Model):
hname = models.CharField(max_length=20)
# 性别:BooleanField说明是bool类型,default默认值,false为男
hgender = models.BooleanField(default = False)
hcomment = models.CharField(max_length=128)
# 即在多类表中创建与一类表关联的外键
# 关系属性对应的表的字段名格式为:关系属性名_id 例如:hbook_id
hbook = models.ForeignKey("BookInfo")
Models.ForeignKey可以建立两个模型类之间一对多的关系,django在生成表的时候,就会在多端的表中创建一列作为外键,建立两个表之间一对多的关系。
完成数据表的迁移之后,下面就可以通过进入项目的shell,进行简单的API操作。如果需要退出项目,可以使用ctrl+d快捷键或输入quit()。
进入项目shell的命令:
python manage.py shell
首先导入模型类:
from booktest.models import BookInfo
查询所有图书信息:
BookInfo.objects.all()
向booktest_bookinfo表中插入一条数据
b = BookInfo() #定义一个BookInfo类的对象
b.btitle ='天龙八部' #定义b对象的属性并赋值
from datetime import date
b.bpub_date = date(1990,10,11)
b.save() #才会将数据保存进数据库
查询出booktest_bookinfo表中id为1的数据。
b = BookInfo.objects.get(id=1)
修改b对应图书的出版日期
b.bpub_date = date(1989,10,21)
b.save() #才会更新表格中的数据
删除图书信息
b.delete()
向booktest_heroInfo表中插入一条数据。
from booktest import HeroInfo
h = HeroInfo()
h.hname = '郭靖'
h.hgender = False
h.hcomment = ‘降龙十八掌’
b2 = BookInfo.objects.get(id=2)
h.hbook = b2 #给关系属性赋值,英雄对象所属的图书对象
h.save()
图书与英雄是一对多的关系,django中提供了关联的操作方式。
获得关联集合:返回当前book对象的所有hero。
b.heroinfo_set.all()
使用Django的管理模块,需要按照如下步骤操作:
本地化
语言和时区的本地化。
修改settings.py文件。
LANGUAGE_CODE = 'zh-hans' #使用中国语言
TIME_ZONE = 'Asia/Shanghai' #使用中国上海时间
创建管理员
创建管理员的命令如下,按提示输入用户名、邮箱、密码。
python manage.py createsuperuser
python manage.py runserver
输入前面创建的用户名、密码完成登录。
登录成功后界面如下,但是并没有图书、英雄的管理入口,接下来进行模型类注册。
登录后台管理后,默认没有我们创建的应用中定义的模型类,需要在自己应用中的admin.py文件中注册,才可以在后台管理中看到,并进行增删改查操作。
重写models中定义模型类的__str__方法可以改变后台管理页面的显示
自定义管理页面
自定义模型管理类。模型管理类就是告诉django在生成的管理页面上显示哪些内容。
打开booktest/admin.py文件,自定义类,继承自admin.ModelAdmin类。
属性list_display表示要显示哪些属性
class BookInfoAdmin(admin.ModelAdmin):
list_display = ['id', 'btitle', 'bpub_date']
修改模型类BookInfo的注册代码
admin.site.register(BookInfo, BookInfoAdmin)
在Django中,通过浏览器去请求一个页面时,使用视图函数来处理这个请求的,视图函数处理之后,要给浏览器返回页面内容。
使用视图时需要进行两步操作:
定义视图
视图就是一个Python函数,被定义在views.py中。
视图的必须有一个参数,一般叫request,视图必须返回HttpResponse对象,HttpResponse中的参数内容会显示在浏览器的页面上。
打开booktest/views.py文件,定义视图index如下
from django.http import HttpResponse
def index(request):
return HttpResponse("index")
配置URLconf
查找视图的过程
请求者在浏览器地址栏中输入url,请求到网站后,获取url信息,然后与编写好的URLconf逐条匹配,如果匹配成功则调用对应的视图函数,如果所有的URLconf都没有匹配成功,则返回404错误。
一条URLconf包括url规则、视图两部分:
url规则使用正则表达式定义。
视图就是在views.py中定义的视图函数。
需要两步完成URLconf配置:
在booktest/应用下创建urls.py文件,
包含到项目中:打开test1/urls.py文件,为urlpatterns列表增加项如下:
配置url时,有两种语法格式:
url匹配的过程
请求访问
视图和URLconf都定义好了,运行服务后:
在Django中,将前端的内容定义在模板中,然后再把模板交给视图调用
创建模板
为应用booktest下的视图index创建模板index.html,目录结构如下图:
设置查找模板的路径:打开test1/settings.py文件,设置TEMPLATES的DIRS值
'DIRS': [os.path.join(BASE_DIR, 'templates')]
定义模板
打开templtes/booktest/index.html文件
<html>
<head>
<title>图书列表title>
head>
<body>
<h1>{{title}}h1>
{%for i in list%}
{{i}}<br>
{%endfor%}
body>
html>
给模板文件传递数据
在模板中输出变量语法如下,变量可能是从视图中传递过来的,也可能是在模板中定义的。
{{变量名}}
在模板中编写代码段语法如下:
{%代码段%}
for循环:
{% for i in list %}
list不为空时执行的逻辑
{% empty %}
list为空时执行的逻辑
{% endfor %}
视图调用模板
调用模板分为三步骤:
打开booktst/views.py文件,调用上面定义的模板文件
from django.http import HttpResponse
from django.template import loader,RequestContext
def index(request):
# 1.获取模板
template=loader.get_template('booktest/index.html')
# 2.定义模板上下文
context=RequestContext(request,{'title':'图书列表','list':range(10)})
# 3.渲染模板
res_html = template.render(context)
# 4.返回浏览器应答
return HttpResponse(res_html)
打开浏览器刷新页面,显示效果如下
视图调用模板简写
视图调用模板都要执行以上三部分,于是Django提供了一个函数render封装了以上代码。 方法render包含3个参数:
打开booktst/views.py文件,调用render的代码如下:
from django.shortcuts import render
def index(request):
context={'title':'图书列表','list':range(10)}
return render(request,'booktest/index.html',context)
对象-关系映射(ORM,Object-Relation Mapping),是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射ORM系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。O/R中字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。目前流行的ORM产品如Java的Hibernate,.Net的EntityFormerWork等。
在MVC框架中的Model模块中都包括ORM,对于开发人员主要带来了如下好处:
O(objects):类和对象。
R(Relation):关系,关系数据库中的表格。
M(Mapping):映射。
Django ORM框架的功能:
mysql部分命令
登录mysql数据库:
mysql –uroot –p
查看有哪些数据库:
show databases
创建数据库:
create database test2 charset=utf8; #切记:指定编码
使用数据库:
use test2;
查看数据库中的表:
show tables;
创建新的Django项目test2
django-admin startproject test2
打开test2/settings.py文件,找到DATABASES项,默认使用SQLite3数据库
修改settings.py中的DATABASES。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test2', #数据库名字,
'USER': 'root', #数据库登录用户名
'PASSWORD': 'mysql', #数据库登录密码
'HOST': 'localhost', #数据库所在主机
'PORT': '3306', #数据库端口
}
}
注意:django框架不会自动帮我们生成mysql数据库,所以我们需要自己去创建。
进入test2目录,创建应用booktest
python manage.py stratapp booktest
将应用booktest注册到项目中:打开test2/settings.py文件,找到INSTALLED_APPS项,注册booktest
打开booktest/models.py文件,定义模型类如下
from django.db import models
#定义图书模型类BookInfo
class BookInfo(models.Model):
btitle = models.CharField(max_length=20)#图书名称
bpub_date = models.DateField()#发布日期
bread = models.IntegerField(default=0)#阅读量
bcomment = models.IntegerField(default=0)#评论量
isDelete = models.BooleanField(default=False)#逻辑删除
#定义英雄模型类HeroInfo
class HeroInfo(models.Model):
hname = models.CharField(max_length=20)#英雄姓名
hgender = models.BooleanField(default=True)#英雄性别
isDelete = models.BooleanField(default=False)#逻辑删除
hcomment = models.CharField(max_length=200)#英雄描述信息
hbook = models.ForeignKey('BookInfo')#英雄与图书表的关系为一对多,所以属性定义在英雄模型类中
生成迁移文件
python manage.py makemigrations
生成迁移文件的时候出现错误,原因是把数据库切换成了mysql,需要安装pymysql模块之后,Django框架才可以操作mysql数据库。安装命令如下:
python2需要安装mysql-python
pip install mysql-python
python3需要安装pymysql:
pip install pymysql
安装成功之后,在test2/_init_.py文件中加上如下代码:
import pymysql
pymysql.install_as_MySQLdb()
设计BookInfo,增加属性bread和bcomment,另外设置软删除标记属性isDelete。
设计HeroInfo类,增加软删除标记属性isDelete。
软删除标记:删除数据时不做真正的删除,而是把标记数据设置为1表示删除,目的是防止重要的数据丢失
执行迁移
python manage.py makemigrations
python manage.py migrate
insert into booktest_bookinfo(btitle,bpub_date,bread,bcomment,isDelete) values
('射雕英雄传','1980-5-1',12,34,0),
('天龙八部','1986-7-24',36,40,0),
('笑傲江湖','1995-12-24',20,80,0),
('雪山飞狐','1987-11-11',58,24,0);
insert into booktest_heroinfo(hname,hgender,hbook_id,hcomment,isDelete) values
('郭靖',1,1,'降龙十八掌',0),
('黄蓉',0,1,'打狗棍法',0),
('黄药师',1,1,'弹指神通',0),
('欧阳锋',1,1,'蛤蟆功',0),
('梅超风',0,1,'九阴白骨爪',0),
('乔峰',1,2,'降龙十八掌',0),
('段誉',1,2,'六脉神剑',0),
('虚竹',1,2,'天山六阳掌',0),
('王语嫣',0,2,'神仙姐姐',0),
('令狐冲',1,3,'独孤九剑',0),
('任盈盈',0,3,'弹琴',0),
('岳不群',1,3,'华山剑法',0),
('东方不败',0,3,'葵花宝典',0),
('胡斐',1,4,'胡家刀法',0),
('苗若兰',0,4,'黄衣',0),
('程灵素',0,4,'医术',0),
('袁紫衣',0,4,'六合拳',0);
定义视图
打开booktest/views.py文件,定义视图代码如下:
from django.shortcuts import render,redirect
from booktest.models import BookInfo,HeroInfo
from datetime import date
#查询所有图书并显示
def index(request):
books=BookInfo.objects.all()
return render(request,'booktest/index.html',{'books':books})
#创建新图书
def create(request):
book=BookInfo()
book.btitle = '流星蝴蝶剑'
book.bpub_date = date(1995,12,30)
book.save()
#转向到首页
return redirect('/index')
#逻辑删除指定编号的图书
def delete(request,id):
book=BookInfo.objects.get(id=int(id))
book.delete()
#转向到首页
return redirect('/index')
配置url
打开test2/urls.py文件,配置url如下:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
#引入booktest的url配置
url(r'^',include('booktest.urls')),
]
在booktest应用下创建urls.py文件,代码如下:
from django.conf.urls import url
from booktest import views
urlpatterns=[
url(r'^index$',views.index),
url(r'^delete(\d+)/$',views.delete),
url(r'^create/$',views.create),
]
创建模板
打开test2/settings.py文件,配置模板查找目录TEMPLATES的DIRS。
'DIRS': [os.path.join(BASE_DIR,'templates')],
创建templates/booktest/index.html文件。
模板代码如下:
复习案例
创建
{%for book in books%}
- {{book.btitle}}--删除
{%endfor%}
运行
运行服务器。
python manage.py runserver
Django根据属性的类型确定以下信息:
django会为表创建自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后django不会再创建自动增长的主键列。
默认创建的主键列属性为id,可以使用pk代替,pk全拼为primary key。
**注意:**pk是主键的别名,若主键名为id2,那么pk是id2的别名。
属性命名限制:
属性=models.字段类型(选项)
字段类型
使用时需要引入django.db.models包,字段类型如下:
选项
通过选项实现对字段的约束,选项如下:
mysql.log是mysql的日志文件,里面记录的对MySQL数据库的操作记录。默认情况下mysql的日志文件没有产生,需要修改mysql的配置文件,步骤如下:
使用下面的命令打开mysql的配置文件,去除68,69行的注释,然后保存。
sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
sudo service mysql restart
使用如下命令打开mysql日志文件。
sudo tail -f /var/log/mysql/mysql.log #可以实时查看数据库的日志内容
通过模型类.objects属性可以调用如下函数,实现对模型类对应的数据表的查询。
get
返回表中满足条件的一条且只能有一条数据。
返回值是一个模型类对象。 参数中写查询条件。
all
返回模型类对应表格中的所有数据。
返回值是QuerySet类型 查询集
filter
返回满足条件的数据。
返回值是QuerySet类型 参数写查询条件。
exclude
返回不满足条件的数据。
返回值是QuerySet类型 参数写查询条件。
order_by
对查询结果进行排序。
返回值是QuerySet类型 参数中写根据哪些字段进行排序。
条件格式:
模型类属性名__条件名=值 (双下划线)
BookInfo.objects.get(id=1);
BookInfo.objects.get(id__exact=1);
BookInfo.objects.filter(btitle__contains = '传');
查询书名以’部’结尾的图书 endswith 开头:startswithBookInfo.objects.filter(btitle__endwith='传');
BookInfo.objects.filter(btitle__isnull==False);
BookInfo.objects.fiter(id__in=[1,3,5]);
BookInfo.objects.filter(id__gt=3);
from datetime import date
BookInfo.objects.filter(bpub_date__gt=(1980,1,1));
BookInfo.objects.filter(id__exclude=3);
BookInfo.objects.all().order_by('id');
查询所有图书的信息,按照id从大到小进行排序BookInfo.objects.all().order_by('-id');
把id大于3的图书信息按阅读量从大到小排序显示BookInfo.objects.filter(id__gt=3).order_by('-bread');
F对象用于类属性之间的比较
使用之前需要先导入
from django.db.models import F
查询图书阅读量大于评论量图书信息
BookInfo.objects.filter(bread__gt=F('bcomment'));
查询图书阅读量大于2倍评论量图书信息
BookInfo.objects.filter(bread__gt=F('bcomment')*2);
多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字
使用之前需要先导入:
from django.db.models import Q
查询id大于3且阅读量大于30的图书的信息
BookInfo.objects.filter(id__gt=3,bread__gt=30);
BookInfo.objects.filter(Q(id__gt=3)&Q(bread__gt=30));
Q对象可以使用&、|连接,&表示逻辑与,|表示逻辑或
查询id大于3或者阅读量大于30的图书的信息
BookInfo.objects.filter(Q(id__gt=3)|Q(bread__gt=30));
Q对象前可以使用~操作符,表示非not
查询id不等于3图书的信息
BookInfo.objects.filter(~Q(id=3));
对查询结果进行聚合操作:sum count avg max min
aggregate:调用这个函数来使用聚合。 返回值是一个字典
使用前需先导入聚合类:
from django.db.models import Sum,Count,Max,Min,Avg
查询所有图书阅读量的总和
BookInfo.objects.aggregate(Sum('bread'));
查询所有图书的数目
BookInfo.objects.all().aggregate(Count('id'));
count函数 返回值是一个数字
作用:统计满足条件数据的数目
统计所有图书的数目
BookInfo.objects.all().count();
BookInfo.objects.count();
统计id大于3的所有图书的数目
BookInfo.objects.filter(id__gt=3).count();
查询集表示从数据库中获取的对象集合,在管理器上调用某些过滤器方法会返回查询集,查询集可以含有零个、一个或多个过滤器。过滤器基于所给的参数限制查询的结果,从Sql的角度,查询集和select语句等价,过滤器像where和limit子句。
返回查询集的过滤器如下:
返回单个值的过滤器如下:
判断某一个查询集中是否有数据:
两大特性
惰性执行:创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用。
缓存:使用同一个查询集,第一次使用时会发生数据库的查询,然后把结果缓存下来,再次使用这个查询集时会使用缓存的数据。
限制查询集
可以对查询集进行取下标或切片操作,等同于sql中的limit和offset子句。
注意:不支持负数索引。
对查询集进行切片后返回一个新的查询集,不会立即执行查询。
如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常,[0:1].get()如果没有数据引发DoesNotExist异常。
关系字段类型
关系型数据库的关系包括三种类型:
一对多关系
#定义图书模型类BookInfo
class BookInfo(models.Model):
btitle = models.CharField(max_length=20)#图书名称
bpub_date = models.DateField()#发布日期
bread = models.IntegerField(default=0)#阅读量
bcomment = models.IntegerField(default=0)#评论量
isDelete = models.BooleanField(default=False)#逻辑删除
#定义英雄模型类HeroInfo
class HeroInfo(models.Model):
hname = models.CharField(max_length=20)#英雄姓名
hgender = models.BooleanField(default=True)#英雄性别
isDelete = models.BooleanField(default=False)#逻辑删除
hcomment = models.CharField(max_length=200)#英雄描述信息
hbook = models.ForeignKey('BookInfo')#英雄与图书表的关系为一对多,所以属性定义在英雄模型类中
多对多关系
class TypeInfo(models.Model):
tname = models.CharField(max_length=20) #新闻类别
class NewsInfo(models.Model):
ntitle = models.CharField(max_length=60) #新闻标题
ncontent = models.TextField() #新闻内容
npub_date = models.DateTimeField(auto_now_add=True) #新闻发布时间
ntype = models.ManyToManyField('TypeInfo') #通过ManyToManyField建立TypeInfo类和NewsInfo类之间多对多的关系
Django中也能实现类似于join查询。
通过对象执行关联查询
在定义模型类时,可以指定三种关联关系,最常用的是一对多关系,在一对多关系中,一对应的类叫做一类,多对应的那个类叫做多类,多类中定义的建立关联的类属性叫做关联属性
查询id为1的图书关联的英雄的信息
b = BookInfo.objects.get(id__exact = 1);
b.heroinfo_set.all();
通过模型类查询
HeroInfo.objects.filter(hbook__id=1);
查询id为1的英雄关联的图书信息
h = HeroInfo.objects.filter(id__exact=1);
h.hbook;
通过模型类查询
BookInfo.objects.fliter(heroinfo__id=1);
通过对象执行关联查询
由一到多的访问语法:
一对应的模型类对象.多对应的模型类名小写_set
由多到一的访问语法:
多对应的模型类对象.多对应的模型类中的关系类属性名
访问一对应的模型类关联对象的id语法
多对应的模型类对象.关联类属性_id
通过模型类执行关联查询
由多模型类条件查询一模型类数据:
关联模型类名小写__属性名__条件运算符=值
如果没有"__运算符"部分,表示等于,结果和sql中的inner join相同。
由一模型类条件查询多模型类数据: 语法如下:
一模型类关联属性名__一模型类属性名__条件运算符=值
调用一个模型类对象的save方法的时候就可以实现对模型类对应数据表的插入和更新。
调用一个模型类对象的delete方法的时候就可以实现对模型类对应数据表数据的删除。
对于地区信息、分类信息等数据,表结构非常类似,每个表的数据量十分有限,为了充分利用数据表的大量数据存储功能,可以设计成一张表,内部的关系字段指向本表的主键,这就是自关联的表结构。
自关联是一种特殊的一对多的关系。
案例:显示广州市的上级地区和下级地区。
模型类:
#定义地区模型类,存储省、市、区县信息
class AreaInfo(models.Model):
atitle=models.CharField(max_length=30) # 名称
aParent=models.ForeignKey('self',null=True,blank=True) # 关系
由一类的对象查询多类的时候:
一类的对象.多类名小写_set.all() #查询所用数据
由多类的对象查询一类的时候:
多类的对象.关联属性 #查询多类的对象对应的一类的对象
由多类的对象查询一类对象的id时候:
多类的对象. 关联属性_id
定义视图
定义模板html
地区
当前地区:{{area.atitle}}
上级地区:{{parent.atitle}}
下级地区:
{%for child in children%}
- {{child.atitle}}
{%endfor%}
配置urls.py
运行服务 python manage.py runserver
属性objects:管理器,是models.Manager类型的对象,用于与数据库进行交互。
当没有为模型类定义管理器时,Django会为每一个模型类生成一个名为objects的管理器,自定义管理器后,Django不再生成默认管理器objects。
为模型类BookInfo定义管理器books语法如下:
class BookInfo(models.Model):
...
books = models.Manager()
管理器是Django的模型进行数据库操作的接口,Django应用的每个模型类都拥有至少一个管理器。Django支持自定义管理器类,继承自models.Manager。
自定义管理器类主要用于两种情况:
1) 修改原始查询集,重写all()方法。
打开booktest/models.py文件,定义类BookInfoManager
#图书管理器
class BookInfoManager(models.Manager):
def all(self):
#默认查询未删除的图书信息
#调用父类的成员语法为:super().方法名
return super().all().filter(isDelete=False)
在模型类BookInfo中定义管理器
class BookInfo(models.Model):
...
books = BookInfoManager()
2) 在管理器类中定义创建对象的方法
对模型类对应的数据表进行操作时,推荐将这些操作数据表的方法封装起来,放到模型管理器类中。
打开booktest/models.py文件,定义方法create。
class BookInfoManager(models.Manager):
...
#创建模型类,接收参数为属性赋值
def create_book(self, title, pub_date):
#创建模型类对象self.model可以获得模型类
book = self.model()
book.btitle = title
book.bpub_date = pub_date
book.bread=0
book.bcommet=0
book.isDelete = False
# 将数据插入进数据表
book.save()
return book
为模型类BookInfo定义管理器books语法如下
class BookInfo(models.Model):
...
books = BookInfoManager()
调用语法如下:
调用:book=BookInfo.books.create_book("abc",date(1980,1,1))
在模型类中定义类Meta,用于设置元信息,如使用db_table自定义表的名字。
视图负责接收请求,进行处理,与M和T进行交互,返回应答。
返回html内容 HttpResponse,也可能重定向 redirect,还可以返回json数据。
定义视图函数
request参数必须有。是一个HttpRequest类型的对象。参数名可以变化,但不要更改。
配置url
建立url和视图函数之间的对应关系。
语法一
url(正则,include('应用.urls'))
例如:
url(r'^',include('booktest.urls')),
这种语法用于项目目录下的urls.py中,目的是将应用的urls配置到应用内部,数据更清晰并且易于维护。
语法二:定义,指定URL和视图函数的对应关系。
在应用内部创建的urls.py文件,指定请求地址与视图的对应关系。
url(正则,'视图函数名称')
例如
from django.conf.urls import url
from booktest import views
urlpatterns=[
url(r'^$',views.index),
]
404:找不到页面,关闭调试模式之后,默认会显示一个标准的错误页面,如果要显示自定义的页面,则需要的templates目录下面自定义一个404.html文件。
500: 服务器端的错误。如果要显示自定义的页面,则需要的templates目录下面自定义一个500.html文件
网站开发完成需要关闭调试模式,在settings.py文件中:
DEBUG=False
ALLOWED_HOST=[ ‘*’]
进行url匹配时,把所需要的捕获的部分设置成一个正则表达式组,这样django框架就会自动把匹配成功后相应组的内容作为参数传递给视图函数。
在匹配过程中从url中捕获参数,每个捕获的参数都作为一个普通的python字符串传递给视图。
获取值需要在正则表达式中使用小括号,分为两种方式:
位置参数
关键字参数
注意:两种参数的方式不要混合使用,在一个正则表达式中只能使用一种参数方式。
位置参数
直接使用小括号,通过位置参数传递给视图。
关键字参数
在正则表达式部分为组命名。
其中?P部分表示为这个参数定义的名称为num
服务器接收到http协议的请求后,会根据报文创建HttpRequest对象,这个对象不需要我们创建,直接使用服务器构造好的对象就可以。视图的第一个参数必须是HttpRequest对象,在django.http模块中定义了HttpRequest对象的API。
属性
下面除非特别说明,属性都是只读的。
path:一个字符串,表示请求的页面的完整路径,不包含域名和参数部分。
method:一个字符串,表示请求使用的HTTP方法,常用值包括:‘GET’、‘POST’。
在浏览器中给出地址发出请求采用get方式,如超链接。
在浏览器中点击表单的提交按钮发起请求,如果表单的method设置为post则为post请求。
encoding:一个字符串,表示提交的数据的编码方式。
如果为None则表示使用浏览器的默认设置,一般为utf-8。
这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值。
GET:QueryDict类型对象,类似于字典,包含get请求方式的所有参数。
POST:QueryDict类型对象,类似于字典,包含post请求方式的所有参数。
FILES:一个类似于字典的对象,包含所有的上传文件。
COOKIES:一个标准的Python字典,包含所有的cookie,键和值都为字符串。
session:一个既可读又可写的类似于字典的对象,表示当前的会话,只有当Django 启用会话的支持时才可用。
运行服务器,在浏览器中浏览首页,可以在浏览器“开发者工具”中看到请求信息如下图:
dict.get('键',默认值)
可简写为
dict['键']
dict.getlist('键',默认值)
请求格式:在请求地址结尾使用?,之后以"键=值"的格式拼接,多个键值对之间以&连接。
使用form表单请求时,method方式为post则会发起post方式的请求,需要使用HttpRequest对象的POST属性接收参数,POST属性是一个QueryDict类型的对象。
表单控件name属性的值作为键,value属性的值为值,构成键值对提交。
#接收请求参数
def show_reqarg(request):
if request.method == 'GET':
a = request.GET.get('a') #获取请求参数a
b = request.GET.get('b') #获取请求参数b
c = request.GET.get('c') #获取请求参数c
return render(request, 'booktest/show_getarg.html', {'a':a, 'b':b, 'c':c})
else:
name = request.POST.get('uname') #获取name
gender = request.POST.get('gender') #获取gender
hobbys = request.POST.getlist('hobby') #获取hobby
return render(request, 'booktest/show_postarg.html', {'name':name, 'gender':gender, 'hobbys':hobbys})
视图在接收请求并处理后,必须返回HttpResponse对象或子对象。在django.http模块中定义了HttpResponse对象的API。HttpRequest对象由Django创建,HttpResponse对象由开发人员创建。
运行服务器,在浏览器中浏览首页,可以在浏览器“开发者工具”中看到响应信息如下图:
标号3为响应头信息,点击标号4处可以查看响应体信息。
属性
set_cookie(key, value='', max_age=None, expires=None)
在浏览器中使用javascript发起ajax请求时,返回json格式的数据,此处以jquery的get()方法为例。类JsonResponse继承自HttpResponse对象,被定义在django.http模块中,创建对象时接收字典作为参数。
JsonResponse对象的content-type为’application/json’。
异步的javascript。在不全部加载某一个页面部的情况下,对页面进行局的刷新,ajax请求都在后台。
ajax代码执行过程如下:
案例:模拟登陆
将图片,css文件,js文件等静态文件,放入static目录下,并配置settings.py。
设计url对应的视图函数login_ajax和负责ajax校验的函数login_ajax_check
编写模板文件login_ajax.html
配置url
当一个逻辑处理完成后,不需要向客户端呈现数据,而是转回到其它页面,如添加成功、修改成功、删除成功后显示数据列表,而数据的列表视图已经开发完成,此时不需要重新编写列表的代码,而是转到这个视图就可以,此时就需要模拟一个用户请求的效果,从一个视图转到另外一个视图,就称为重定向。
Django中提供了HttpResponseRedirect对象实现重定向功能,这个类继承自HttpResponse,被定义在django.http模块中,返回的状态码为302。
views.py文件中定义视图
from django.http import HttpResponseRedirect
...
# 定义重定义向视图,转向首页
def red1(request):
return HttpResponseRedirect('/')
重定向简写函数redirect
在django.shortcuts模块中为重定向类提供了简写函数redirect。
修改views.py文件中视图:
from django.shortcuts import redirect
...
def red1(request):
return redirect('/')
浏览器请求服务器是无状态的。无状态指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。无状态的应用层面的原因是:浏览器和服务器之间的通信都遵守HTTP协议。根本原因是:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的Socket连接,而且服务器也会在处理页面完毕之后销毁页面对象。
有时需要保存下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等。 实现状态保持主要有两种方式:
Cookie
cookie是由服务器生成,存储在浏览器端的一小段文本信息。有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。
Cookie的特点
设置cookie和获取cookie
案例:记住用户名
view.py下定义登陆login视图函数和登陆检验函数login_check
编写模板文件
配置urls.py
对于敏感、重要的信息,建议要储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息。
在服务器端进行状态保持的方案就是Session。
启用Session
Django项目默认启用Session。
打开settings.py文件,在项MIDDLEWARE_CLASSES中启用Session中间件。禁用Session:将Session中间件删除。
存储方式
打开settings.py文件,设置SESSION_ENGINE项指定Session数据存储的方式,可以存储在数据库、缓存、Redis等。
存储在数据库中,如下设置可以写,也可以不写,这是默认存储方式。
SESSION_ENGINE='django.contrib.sessions.backends.db'
存储在缓存中:存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。
SESSION_ENGINE='django.contrib.sessions.backends.cache'
混合存储:优先从本机内存中存取,如果没有则从数据库中存取。
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用。
迁移后会在数据库中创建出存储Session的表。
表结构如下图。
由表结构可知,操作Session包括三个数据:键,值,过期时间。
session的特点:
对象及方法
以键值对的格式写session。
request.session['键']=值
根据键读取值。
request.session.get('键',默认值)
清除所有session,在存储中删除值部分。
request.session.clear()
清除session数据,在存储中删除session的整条数据。
request.session.flush()
删除session中的指定键及值,在存储中只删除某个键及对应的值。
del request.session['键']
设置会话的超时时间,如果没有指定过期时间则两个星期后过期。
request.session.set_expiry(value)
如果value是一个整数,会话将在value秒没有活动后过期。
如果value为0,那么用户会话的Cookie将在用户的浏览器关闭时过期。
如果value为None,那么会话永不过期。
使用Redis存储Session
安装包
pip install django-redis-sessions==0.5.6
修改settings文件,增加如下项:
SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_HOST = 'localhost'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 2
SESSION_REDIS_PASSWORD = ''
SESSION_REDIS_PREFIX = 'session'
views.py文件,修改session_test视图
def session_test(request):
request.session['h1']='hello'
return HttpResponse('ok')
管理redis的命令,需要保证redis服务被开启。
查看:ps ajx|grep redis
启动:sudo service redis start
停止:sudo service redis stop
使用客户端连接服务器:redis-cli
切换数据库:select 2
查看所有的键:keys *
获取指定键的值:get name
cookie:记住用户名。安全性要求不高。
session:涉及到安全性要求比较高的数据。银行卡账户,密码
作为Web框架,Django提供了模板,用于编写html代码,还可以嵌入模板代码更快更方便的完成页面开发,再通过在视图中渲染模板,将生成最终的html字符串返回给客户端浏览器。模版致力于表达外观,而不是程序逻辑。模板的设计实现了业务逻辑view与显示内容template的分离,一个视图可以使用任意一个模板,一个模板可以供多个视图使用。
模板包含两部分:
Django模板语言,简写DTL,定义在django.template包中。 创建项目后,在"项目名称/settings.py"文件中定义了关于模板的配置。
通常是在视图函数中使用模板产生html内容返回给客户端。
模板语言简称为DTL。(Django Template Language),模板语言包括4种类型,分别是:变量
标签 过滤器 注释
模板变量的作用是计算并输出,变量名必须由字母、数字、下划线(不能以下划线开头)和点组成。
语法如下:
{{模板变量名}}
例如:{{ book.btitle }}
例如:{{book.0}}
如果解析失败,则产生内容时用空字符串填充模板变量。
使用模板变量时,.前面的可能是一个字典,可能是一个对象,还可能是一个列表。
语法如下:
{%代码段%}
for标签语法如下:
{%for item in 列表%}
循环逻辑
{{forloop.counter}}表示当前是第几次循环,从1开始
{%empty%}
列表为空或不存在时执行此逻辑
{%endfor%}
{%if ...%}
逻辑1
{%elif ...%}
逻辑2
{%else%}
逻辑3
{%endif%}
比较运算符如下:
注意:运算符左右两侧不能紧挨变量或常量,必须有空格。
==
!=
<
>
<=
>=
逻辑运算:
and
or
not
过滤器用于对模板变量进行操作。
date:改变日期的显示格式。
length:求长度。字符串,列表.
default:设置模板变量的默认值。
语法如下:
变量|过滤器:参数
长度length,返回字符串包含字符的个数,或列表、元组、字典的元素个数。
默认值default,如果变量不存在时则返回默认值。
data|default:'默认值'
日期date,用于对日期类型的值进行字符串格式化,常用的格式化字符如下:
Y表示年,格式为4位,y表示两位的年。
m表示月,格式为01,02,12等。
d表示日, 格式为01,02等。
j表示日,格式为1,2等。
H表示时,24进制,h表示12进制的时。
i表示分,为0-59。
s表示秒,为0-59。
value|date:"Y年m月j日 H时i分s秒"
过滤器就是python中的函数,注册后就可以在模板中当作过滤器使用,自定义过滤器。 新建python packages-命名固定 templatetags,在templatetags中自定义过滤器的文件
首先使用load标签引入模块
{%load filters%}
单行注释
{# 注释内容 #}
多行注释
{% comment %}
注释内容
{% endcomment %}
模板注释在开发者模式下看不到注释代码,而html注释()可以看到
模板继承和类的继承含义是一样的,主要是为了提高代码重用
典型应用:网站的头部、尾部信息。
父模板
如果发现在多个模板中某些内容相同,那就应该把这段内容定义到父模板中。
标签block:用于在父模板中预留区域,留给子模板填充差异性的内容,名字不能相同。 为了更好的可读性,建议给endblock标签写上名字,这个名字与对应的block名字相同。父模板中也可以使用上下文中传递过来的数据。
{%block 名称%}
预留区域,可以编写默认内容,也可以没有默认内容
{%endblock 名称%}
{% extends "父模板路径"%}
子模版不用填充父模版中的所有预留区域,如果子模版没有填充,则使用父模版定义的默认值。
填充父模板中指定名称的预留区域。
{%block 名称%}
实际填充内容
{{block.super}}用于获取父模板中block的内容
{%endblock 名称%}
模板对上下文传递的字符串进行输出时,会对以下字符自动转义。
小于号< 转换为 <
大于号> 转换为 >
单引号' 转换为 '
双引号" 转换为 "
与符号& 转换为 &
{{ 模板变量|safe}}
也可以使用
{% autoescape off %}
模板语言代码
{% endautoescape %}
模板硬编码中的字符串默认不会经过转义,如果需要转义,那需要手动进行转义。
Cross Site Request Forgery,译为跨站请求伪造。CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…造成的问题包括:个人隐私泄露以及财产安全
如果想防止CSRF,首先是重要的信息传递都采用POST方式而不是GET方式,接下来就说POST请求的攻击方式以及在Django中的避免。
django防止csrf的方式:
在用户注册、登录页面,为了防止暴力请求,可以加入验证码功能,如果验证码错误,则不需要继续处理,可以减轻业务服务器、数据库服务器的压力。
安装包Pillow3.4.1
pip install Pillow==3.4.1
在booktest/views.py文件中,创建视图verify_code。
提示1:随机生成字符串后存入session中,用于后续判断。
提示2:视图返回mime-type为image/png。
from PIL import Image, ImageDraw, ImageFont
from django.utils.six import BytesIO
...
def verify_code(request):
#引入随机函数模块
import random
#定义变量,用于画面的背景色、宽、高
bgcolor = (random.randrange(20, 100), random.randrange(
20, 100), 255)
width = 100
height = 25
#创建画面对象
im = Image.new('RGB', (width, height), bgcolor)
#创建画笔对象
draw = ImageDraw.Draw(im)
#调用画笔的point()函数绘制噪点
for i in range(0, 100):
xy = (random.randrange(0, width), random.randrange(0, height))
fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
draw.point(xy, fill=fill)
#定义验证码的备选值
str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
#随机选取4个值作为验证码
rand_str = ''
for i in range(0, 4):
rand_str += str1[random.randrange(0, len(str1))]
#构造字体对象,ubuntu的字体路径为“/usr/share/fonts/truetype/freefont”
font = ImageFont.truetype('FreeMono.ttf', 23)
#构造字体颜色
fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
#绘制4个字
draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
#释放画笔
del draw
#存入session,用于做进一步验证
request.session['verifycode'] = rand_str
#内存文件操作
buf = BytesIO()
#将图片保存在内存中,文件类型为png
im.save(buf, 'png')
#将内存中的图片数据返回给客户端,MIME类型为图片png
return HttpResponse(buf.getvalue(), 'image/png')
配置url
url(r'^verify_code/$', views.verify_code),
def verify_show(request):
return render(request,'booktest/verify_show.html')
配置url
url(r'^verify_show/$', views.verify_show),
创建verify_show.html。
<html>
<head>
<title>验证码title>
head>
<body>
<form method="post" action="/verify_yz/">
{%csrf_token%}
<input type="text" name="yzm">
<img id="yzm" src="/verify_code/"/>
<span id="change">看不清,换一个span>
<br>
<input type="submit" value="提交">
form>
body>
html>
def verify_yz(request):
yzm=request.POST.get('yzm')
verifycode=request.session['verifycode']
response=HttpResponse('no')
if yzm==verifycode:
response=HttpResponse('ok')
return response
配置url。
url(r'^verify_yz/$', views.verify_yz),
当某一个url配置的地址发生变化时,页面上使用反向解析生成地址的位置不需要发生变化
根据url 正则表达式的配置动态的生成url
在项目urls中包含具体应用的urls文件时指定namespace
在应用的urls中配置是指定name
在模板文件中使用时,格式如下:
{% url 'namespace名字:name' %} 例如{% url 'booktest:fan2'%}
带位置参数:
{% url 'namespace名字:name' 参数 %} 例如{% url 'booktest:fan2' 1%}
{% url 'namespace名字:name' 关键字参数 %} 例如{% url 'booktest:fan2' id=1 %}
在重定向的时候使用反向解析:
from django.core.urlresolvers import reverse
无参数:
reverse(‘namespace名字:name名字’)
如果有位置参数
reverse(‘namespace名字:name名字’, args = 位置参数元组)
如果有关键字参数
reverse(‘namespace名字:name名字’, kwargs=字典)