Django
1. 特点
- 快速开发:Django的宗旨在于帮助开发人员快速从概念到完成应用程序。
- 安全可靠:Django认真对待安全性,帮助开发人员避免许多常见的安全错误。
- 超可伸缩性: Web上的一些最繁忙的网站利用了Django快速灵活扩展的能力。
2. 入门
MVC 设计模式
目标:程序的解耦
在行业内普遍存在的设计模式
M ==> Model 模型 ==> 数据层 ==> 针对数据的操作 ===>转化成对数据库的操作 (sql) 映射关系
V ==> view 视图 ==> 展示层 ==> 展示页面(页面中会有数据) 里面加载的是 模板(html文件)
C ==> Controller 控制器 ==> 逻辑层 ==> 业务逻辑 根据用户的请求去调用模型,获取数据后交给视图去展示数据
MVT 设计模式
目标:程序的解耦
仅限于Django中的设计模式
M ==> Model 模型(对数据的操作)
V ==> view 视图(业务逻辑)
T ==> Template 模板(页面的展示)
路由
负责请求地址的匹配,并交给指定的视图函数进行处理
如:www.pyweb.com/yichuan/p/123457/
前面到com都为域名,后面的yichuan/p/123457/这是就是url地址
在路由的url函数中可以写正则表达式进行匹配,例如:
url(r'^address/2003/$', views.year)
3. 项目的搭建
- 执行命令:
django-admin startproject 项目名
- 系统会创建一系列文件和文件夹,其中项目名下有一个文件夹与项目同名
- 项目的总目录可以重命名,但是其中的文件尽量不要重命名,否则需要修改配置
创建应用
- 命令行:
python3 manage.py startapp 应用名
- 我们需要操作的文件
views.py
和models.py
启动项目 ⭐⭐⭐
- 在终端中输入命令行:
python3 manage.py runserver
- 之后终端中会返回地址,一般都为本地地址:
http://127.0.0.1:8000
输出一个 Hello World 的过程
- 创建应用:
python3 manage.py startapp myhome
- 在应用文件夹中找到视图函数
views.py
- 定义视图函数
def hello(request):
return HttpResponse('hello world')
注意:需要导入 HttpResponse:
from django.http import HttpResponse
-
定义路由规则
- 先到根路由urls.py文件中定义规则,交给自定义应用的子路由
url(r'^', include('myhome.urls'))
- 定义子路由器,在自定义应用中创建一个urls.py文件,并添加路由规则
url(r'^', views.hello)
,其中url函数三个参数,第一个为路由规则,第二个为指定的视图函数
- 先到根路由urls.py文件中定义规则,交给自定义应用的子路由
启动服务,开始访问
请求、访问过程
1. 当用户在浏览器中访问url地址时,服务器接收请求
2. --> 交给根路由进行匹配
3. --> 交给子路由进行url地址匹配
4. --> 子路由调用相应的视图函数
5. --> 视图函数进行执行,开始响应
4. 使用模板
-
配置模板引擎
- 找到项目配置文件 settings.py
- 修改templates的配置项中的DIRS为:
'DIRS':[os.path.join(BASE_DIR,'templates')]
在manage.py文件的同级目录下创建一个templates的文件夹,里面放置模板文件,即html文件
在视图函数中使用render函数加载模板
def tmp(request):
return render(request,'t.html')
# 其中render函数有三个参数,1-请求对象,为固定写法;2-模板路径;3-传入模板的数据(字典类型)
5. 路由规则 ⭐⭐⭐
基本规则
- 路由按照从上至下的顺序执行
- 路由中可以使用()来捕获 url 请求中的一部分作为参数来使用,例如
# 请求路径:http://127.0.0.1:9000/articles/2014/
# 路由规则
url(r'^articles/([0-9]{4})/$', views.year_archive),
# 视图函数
def year_archive(request,y):
print(y)
return HttpResponse('year_archive')
注意:在有()的正则路由规则中,对应的视图函数中必须用形参来接收()中的传入的参数
正则表达式命名组
以上的路由规则中,对视图函数的接收没有命名要求,形参的名字可以随意更改,但是命名组规则要求视图函数中接收参数的形参必须为规定的名称,例如
# 路由规则
url(r'^abc/(?P<year>[0-9]{4})/$', views.abc_2003),
# 视图函数
def abc_2003(request,year):
print(year)
return HttpResponse('abc_2003')
默认值参数
使用两个路由规则,指向同一个视图函数
# 路由规则
url(r'^user/list/$', views.userlist),
url(r'^user/list/(?P<page>[0-9]+)/$', views.userlist),
# 视图函数
def userlist(request,page=1):
print(page)
return HttpResponse('userlist')
url的反向解析 ⭐⭐⭐⭐⭐
通过路由规则的名(name),动态解析路由的地址
如果在视图、模板中使用硬编码(直接写死请求地址)的链接,在你url发生改变时,维护是意见非常麻烦的事情
# 为了能够动态的解析url的规则,可以在定义路由规则时,给路由器起一个名字(name)
# 路由规则
url(r'^goods/list/$', views.goodslist,name='glist'),
# 然后可以在视图和模板中通过反向解析动态的获取路由解析地址
# 视图函数
def hello(request):
# 视图函数中进行反向解析,获取url地址,需要提前导入reverse
# from django.core.urlresolvers import reverse
r1 = reverse('goods')
r2 = reverse('order')
print(r1,r2)
return render(request, 't.html')
# 模板
<a href="{% url 'glist' %}">商品列表:反向解析格式</a>
注意:路由规则中如果有参数要求,那么模板中在使用url进行反向解析时,必须给参数
<a href="{% url 'olist' 100 %}">订单列表:反向解析格式</a>
反向解析是路由中的重中之重,必须要搞清楚!
6. 模型关系 ⭐⭐⭐⭐
一对一(人对应身份证)
定义
一对一关系指模型的关系时一一对应的,例如:
- 一个用户对应一条用户详细信息
- 一个身份证对应一个人
- 一个微信号对应一个用户
二者一一对应,如果有一个用户的详细信息,必对应一个用户
创建模型
- 在建立模型时建立两个模型,在其中一个模型中加入外键
- 在模型中使用
models.OneToOneField(关联的模型名,是否关联删除)
来建立外键,没有外键的为主数据,有外键的为副数据
# 模型关系 一对一
# 用户模型
class User(models.Model):
username = models.CharField(max_length=50)
age = models.IntegerField()
# 用户详情
class UserInfo(models.Model):
# 此语句为创建外键
uid = models.OneToOneField(User,on_delete=models.CASCADE)
xueli = models.CharField(max_length=50)
yuanxiao = models.CharField(max_length=5)
增删查
-
增加
一对一模型进行增加时不需要特殊注意,直接进行增加,Django框架会自动将两个模型创建的数据进行关联 -
删除
一对一模型在进行数据删除时,如果删除的是主数据,则副数据也会被删除;如果副数据被删除,主数据则不会被删除 -
查询
一对一模型在进行数据查询时有两种方法:- 通过主数据查找副数据:
主数据对象.副数据类名(小写).查询的属性名
- 通过副数据查找主数据:
副数据对象.外键名.查询的属性名
- 通过主数据查找副数据:
# 模型关系:一对一
def one(request):
# 添加
# # 创建用户
data = {'username':'燕小六','age':20}
user = User.objects.create(**data)
# 创建详情
infodata = {'uid':user,'xueli':'本科','yuanxiao':'家里蹲'}
info = UserInfo.objects.create(**infodata)
# 删除
# 删除用户时会关联删除对应的详情数据
user = User.objects.first()
user.delete()
# 删除详细信息,用户不会被删除
ui = UserInfo.objects.first()
ui.delete()
# 查询
# 根据用户获取详细信息
user = User.objects.first()
print(user.username)
print(user.userinfo.xueli)
# # 根据详细信息获取用户信息
ui = UserInfo.objects.first()
print(ui.xueli)
print(ui.uid.username)
return HttpResponse('<h1>模型关系:一对一<h1>')
一对多(商品分类对应分类下的商品) ⭐⭐⭐⭐
定义
- 一个商品分类对应多个商品
- 一个班级对应多个学生
- 一个国家对应多个城市
创建模型
- 在建立模型时建立两个模型,在商品中加入外键
- 在模型中使用
models.ForeignKey(关联的模型名)
来建立外键
# 模型关系:一对多
# 商品类别
class Classify(models.Model):
name = models.CharField(max_length=50)
# 商品
class Goods(models.Model):
# 设置外键,第二个参数为可选
cid = models.ForeignKey(to="Classify", to_field="id")
title = models.CharField(max_length=50)
price = models.IntegerField()
def __str__(self):
return self.title
增删查
-
增加
增加时不需要特殊注意,直接进行增加,Django框架会自动将两个模型创建的数据进行关联 -
删除
如果删除的是商品分类,则分类下的商品也会被删除;如果分类下的商品被删除,商品分类则不会被删除 -
查询
一对一模型在进行数据查询时有两种方法:- 获取商品分类下所有商品:
商品分类.商品类名(小写)_set.all()
- 获取商品所属分类:
商品对象.外键名.查找的属性
- 获取商品分类下所有商品:
# 模型关系:一对多
def two(request):
# 添加
# 创建商品分类
c = Classify.objects.create(**{'name':'手机'})
# 创建商品
g1 = Goods.objects.create(**{'cid':c,'title':'小米8','price':'2399'})
g1 = Goods.objects.create(**{'cid':c,'title':'华为P20','price':'4399'})
g1 = Goods.objects.create(**{'cid':c,'title':'OPPO Find X','price':'4999'})
# 删除
c = Classify.objects.last()
c.delete()
# 查询
# 根据分类获取下面所有的商品
c = Classify.objects.first()
print(c.name)
print(c.goods_set.all())
# 根据商品获取其分类
g = Goods.objects.first()
print(g.title)
print(g.cid.name)
return HttpResponse('模型关系:一对多')
多对多(一本书对应多个标签,一个标签对应多本书) ⭐⭐⭐⭐
定义
- 一本书对应多个标签,一个标签对应多本书
- 一个老师对应多个班级,一个班级对应多个老师
创建模型
- 需要用到第三张表,而不是单单在表中添加外键
- 第三张表用来记录书和标签互相的关系
- 在模型中使用
models.ManyToManyField(关联的模型名)
来建立对多对关系,且定义在任意模型中即可
# 模型关系:多对多
# 书
class Books(models.Model):
title = models.CharField(max_length=50)
def __str__(self):
return self.title
class Tags(models.Model):
name = models.CharField(max_length=50)
# 设置关系语句
bid = models.ManyToManyField(to="Books")
def __str__(self):
return self.name
增删查
-
增加
增加时需要进行关系声明,且方式取决于关系语句设置在哪个模板中,以上面代码为例- 给书添加标签:
书对象.tags_set.set([标签对象1,标签对象2,...])
- 给标签添加书:
标签对象.bid.add(书对象1,书对象2,...)
- 多对多关系设置方法:
- .set([对象1,对象2,...]) 添加关系
- .add(对象1,对象2,...) 添加关系
- .clear() 清空关系
- 给书添加标签:
-
删除
- 无论删除书或标签,表3中相应的关系记录都会关联删除
- .clear() 清空指定对象的所有关系
-
查询
- 获取一本书的所有标签:
书对象.tags_set.all()
- 获取一个标签下的所有书:
标签对象.bid.all()
- 获取一本书的所有标签:
7. 模型查询
1. 查询集
- 在管理器上调用过滤器方法会返回查询集,查询集表示从数据库中获取的对象集合
- 查询集经过过滤器筛选后返回新的查询集,因此可以写成链式过滤
- 惰性执行:创建查询集不会带来任何数据库的访问,直到调用数据时,才会访问数据库
- 何时对查询集求值:迭代,序列化,与if合用
- 返回查询集的方法,称为过滤器
- all(): 获取所有的返回值
- filter(): 过滤掉不符合条件的值
filter(键1=值1,键2=值2)
==filter(键1=值1).filter(键2=值2)
- exclude(): 获取除了满足条件之外的值
- order_by(): 分组返回值根据条件进行排序
- values(): 一个对象构成一个字典,然后构成一个列表返回
返回单个值
- get(): 返回单个满足条件的对象
- 如果未找到则引发
模型类.DoesNotExist
异常 - 如果返回多条,会引发"模型类.MultipleObjectsReturned"异常
- 如果未找到则引发
- count(): 返回当前查询的总条数
- first(): 返回第一个对象
- last(): 返回最后一个对象
- exists(): 判断查询集中是否有数据,如果有返回True,反之返回False
限制查询集
- 查询集返回列表,可以使用下标的方式进行限制,等同于sql中的limit和offset子句
- 注意:不支持负数索引
- 使用下标后返回一个新的查询集,不会立即执行查询
- 如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常,[0:1].get()引发DoesNotExist异常
#这会返回前5个对象 LIMIT 5
Entry.objects.all()[:5]
#这将返回第六个到第十个对象 OFFSET 5 LIMIT 5
Entry.objects.all()[5:10]
2. 字段查询
- 实现where子名,作为方法filter()、exclude()、get()的参数
- 语法:属性名称__比较运算符=值
- 表示两个下划线,左侧是属性名称,右侧是比较类型
- 对于外键,使用“属性名_id”表示外键的原始值
- 转义:like语句中使用了%与,匹配数据中的%与,在过滤器中直接写,例如:filter(title__contains="%")=>where title like '%%%',表示查找标题中包含%的
比较运算符
- exact:判断等,区分大小写;如果没有写'比较运算符',表示判断等
filter(isDelete=False)
- contains:是否包含,区分大小写
exclude(btitle__contains='传')
- startwith\endswith:以value开头或结尾,区分大小写
exclude(btitle__endswith='传')
- isnull\isnotnull:判断是否为null
filter(btitle__isnull=False)
在前面加个i表示不区分大小写,如iexact、icontains、istarswith、iendswith
in:是否包含在范围内
filter(pk__in=[1, 2, 3, 4, 5])
- gt、gte、lt、lte:大于、大于等于、小于、小于等于
filter(id__gt=3)
- year、month、day、week_day、hour、minute、second:对日期间类型的属性进行运算
filter(bpub_date__year=1980)
filter(bpub_date__gt=date(1980, 12, 31))
8.View视图
1. GET\POST
一键一值
# 这种方式当键不存在或多个键时会报错
request.GET['name']
# 这种方式None为默认值,如果数据不存在则返回默认值
request.GET.get('name',None)
request.POST['name']
request.POST.get('name',None)
一键多值
# 返回值为一个列表
request.GET.getlist('name',None)
request.POST.getlist('name',None)
2. HttpResponse对象
- 在django.http模块中定义了HttpResponse对象的API
- HttpRequest对象由Django自动创建,HttpResponse对象由程序员创建
- 在每一个视图函数中必须返回一个HttpResponse对象,当然也可以是HttpResponse子对象
3. HttpResponse
- 不使用模板,直接返回数据
- 返回数据类型为字符串
- 如果字符串中是标签,浏览器可以进行解析
return HttoResponse('你好')
4. render
- 调用模板返回数据
- 参数1:request,固定形式
- 参数2:'模板路径',即html文件的路径
- 参数3:字典类型的数据,用于传入模板中
return render(request,'user/edit.html',{'info':'你好'})
5. 子类 HttpResponseRedirect
- 重定向,服务器端跳转
- 构造函数的第一个参数用来指定重定向的地址
- 可以简写为 redirect
return redirect(reverse('myindex')
6. 子类 JsonResponse
- 返回json书,一般用于异步请求
- 帮助用户创建JSON编码的响应
- JsonResponse的默认类型为application/json
- 参数1:字典类型数据
- 参数2:safe=True/False,默认为True,False表示关闭数据安全,参数1可以传入非字典类型
return JsonResponse([{'list': 'abc'},{'d': 'ac'}],safe=False)
7. set_cookie 方法
- Cookie 是由 Web 服务器保存在用户浏览器(客户端)上的小文本文件,它可以包含有关用户的信息。
- 服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态。
- Cookies最典型的应用是判定注册用户是否已经登录网站
- 设置cookie
# 设置cookie
def setcok(request):
# 获取当前的 响应对象
res = HttpResponse('设置cookie')
# 使用响应对象进行cookie的设置
res.set_cookie('a', 'abcd')
# 返回响应对象
res.set_cookie('c', 'cdef')
return res
- 获取cookie
# 获取cookie
def getcok(request):
cok = request.COOKIES.get('a', None)
return HttpResponse(cok)
8. set_session 方法
- sesison方式所有数据存储在服务器端,在客户端cookie中存储唯一的身份标识
- 当用户进行请求时,判断cookie中的唯一标识符是否与用户请求中携带的唯一标识符相同
- 认证成功后页面可以调用session中的所有内容
- 向对于cookie更加安全,信息不容易被截获
- 存储的内容比cookie更加丰富
- 开启session
- 使用django-admin startproject创建的项目默认启用
- 禁用会话:删除下面指定的两个值,禁用会话将节省一些性能消耗
- Django 中session需要依赖数据库,因此需要确认数据库中是否存在 与session相关的 表
- 在settings.py文件中
* 向INSTALLED_APPS列表中添加:
* 'django.contrib.sessions',
* 项MIDDLEWARE_CLASSES列表中添加:
* 'django.contrib.sessions.middleware.SessionMiddleware',
- 设置session
# 设置session
def setsess(request):
# 设置session
request.session['user'] = {'username':'李四','userid':'12314','age':20}
request.session['vip'] = {'username':'王五','userid':'12314','age':20}
return HttpResponse('设置session')
- 获取session
# 获取session
def getsess(request):
res = request.session.get('user',None)
if res:
return HttpResponse('欢迎'+res['username'])
else:
return HttpResponse('请登录')
- 删除session
# 删除session
def outsess(request):
# 删除会话中的一个key;注意:删除时,如果不存在,则报错
# del request.session['user']
# 清除所有会话信息,但不会删除会话记录,会话依然存在
# request.session.clear()
# 删除当前的会话所有的数据及记录
# request.session.flush()
return HttpResponse('退出')
- session配置
在 settings.py 文件中进行设置:
# session 设置
SESSION_COOKIE_AGE = 60 * 30 # 30分钟
SESSION_SAVE_EVERY_REQUEST = True #如果SESSION_SAVE_EVERY_REQUEST是True,会话cookie将在每个请求中发送
SESSION_EXPIRE_AT_BROWSER_CLOSE = True # 关闭浏览器,则COOKIE失效
#来自 <https://docs.djangoproject.com/en/1.11/topics/http/sessions/>
以下设置为:10秒后过期
# request.session['abc'] = 'abcdef'
# request.session.set_expiry(10)
Ajax 实例
四级城市联动 - 思路
前提:
创建模型
填充数据
配置静态文件
1. 定义路由,获取一级城市数据,返回城市联动的html页面
2. 在html页面中循环并显示一级城市数据
3. 定义一个路由,接收ajax请求
4. 在视图函数中接收页面传回的选项id,回去下一级数据,并返回json格式
5. 在html的页面中动态绑定change事件,获取id,发送ajax请求
6. 在ajax中判断是否有返回数据:如果有,返回数据,并动态创建下拉框,添加数据
注意:
最后一级没有数据,但是创建选框;解决:在返回数据时进行判断
每次选择元素都创建新的选项框,不会删除原来的选项框
解决:当选中元素时,移除当前元素之后的所有元素