第二章 搞定url,Views,Admin,Template
√ 路由系统
√ Models
√ Admin
√ Views视图
√ Template模板
1.路由系统:
(1)静态路由
(2)动态路由
1.Django的路由本质上是通过正则表达式来对用户请求的url进行匹配
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('test', views.test_view),
path('login', views.login_view),
path('main', views.main_view),
re_path(r'^articles/2003/', views.year_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})', views.year_archive3)
]
用r'str'声明字符串str是一个源字符串,防止一些转义字符被转义(\n,\等)而使正则失效
注意:1.路由转发表是按照从上向下顺序匹配,如果在目的规则之前匹配到符合的则会停止匹配,我们想真正匹配的目的函数不会被执行
使用分组匹配时对应的逻辑函数的参数除了默认的request外,还应添加上分组中的参数名,且名称必须保持一致,否则会出错
2.也可以使用**s,意为解引用,因为路由分发器给逻辑函数传的参数是字典类型,不解引用就需要用其键取出其值,
3.如果想使用任意参数,可以不使用分组匹配(正则表达式去掉?P
4.但是随机参数和分组匹配不能混合使用
2.Django2.0之后,推荐了新用法path:
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/
path('articles/
path('articles/
path('articles/
path('articles/
path('
]
1.str:除了'/'外的任意非空字符串('dfsdfs15,..'',.,.'.fdgfd');
2.int:0或任意整型,返回int类型(0,1,2,3...);
3.slug:任意slug字符串(例:sdad-25d-dWS22efs),可以包含所有的字母大小写和'-';
4.uuid:匹配一个格式化的UUID,避免从相同页出发的多个url,但包含的字母必须是小写(例:43d1bf74-4d18-11e8-9931-c4b301ce05d1);
5.path:匹配为一个非空字符串,但可以包含路径标识符'/',比起使用str,path允许获取完整的url
3.如果以上五种内置的类型不能满足需求,django还支持自定义指定正则匹配的数据类型:
1.定义:
1.urls.py同级目录新建.py(例如converters.py),在其中新建一个类定义我们自定义的新规则(例如:YearConverter)
2.类中先用regex定义所需的正则匹配串
3.类中还必须包含to_python(self,value)和to_url两个方法将传入的数据转换成python或url对应格式的字符串:
# 自定义路由匹配数据类型
class YearConverter:
regex = r'[0-9]{4}' # 自定义的匹配正则规则
# to_python和to_url为固有的两个方法(分别负责转换匹配串为python字符串和url字符串对应的格式)
def to_python(self, value): # 转换为python字符串方法
return int(value)
def to_url(self, value):#转换为url中对应类型的方法
return "%04d" % value # 格式必须对应
2.调用:
1.在urls.py中引入注册方法进行注册:
from django.urls import path, re_path, register_converter#register_converter为自定义的规则的注册方法
from . import converters#导入自定义路由类所在的文件
register_converter(converters.YearConverter, 'yyyy') # 注册converters下自定义的匹配规则类,并给类型命名'yyyy'
2.在urlpatterns列表内进行调用:
path('articles//', views.special_year_archive), # 使用自定义的'yyyy'数据类型进行匹配
4.include子url(嵌套子app的url路由器):
当有多个app时,每个app可以有自己的urls.py,只需在顶级urls.py中使用include方法即可:
1.在顶级分发器urls.py中借助include配置规则
from django.urls import include, path
urlpatterns = [
path("app01/", include("app01.urls")) # 检索到'app01/'开头就会自动转发到app01下的urls.py
path('community/', include('aggregator.urls')),
path('contact/', include('contact.urls')),
]
2.在include中指定的子app下的子路由器内配置路由规则:
from django.urls import path
from app01 import views # 此处红色异常为正常现象,不影响使用
urlpatterns = [
# 此处的规则默认的开头必须是顶级路由中指定的关键字(例如url为:/app01/articles/1234/)
path("articles//", views.special_year_archive),
]
5.include子url2(嵌套具有公共开头的子url):
url中重复出现的部分,可以按下面的方法聚合:
即提取开头的公共部分放在urlpatterns中,后续所有以此关键字开头的子url放在另一个patterns列表里,
通过include包含子url的列表即可达到聚合的目的(理论上可以无限嵌套,但是嵌套过多会显得过于复杂)
form django.urls import include, path
from apps.main import views as main_views
from credit import views as credit_views
extra_patterns = [ # 子url列表
path('reports/', credit_views.port),
path('reports//', credit_views.report),
path('charge/', credit_views.charge),
]
urlpatterns = [ # 主url列表
path('', main_views.homepage),
path('help/', include('apps.help.urls')),
path('credit/', include(extra_patterns)),
]
6.路由分发器传递额外参数给views(实际用处不多):
1.以字典键值对形式传递
path('articles/test5/', views.test5_view, {'message': '额外参数测试'}),
2.逻辑函数的参数命名必须与传递的字典键命名一致
def test5_view(request, message):
return HttpResponse(f'消息是 {message}')
2.models(模型)
1.views中操作数据库普通方式:
掌握对于pymysql等数据库模块的使用,执行一些数据库操作,然后返回数据,结合实际自行发挥
一个例子:
1.路由分发器:
# 操作数据库测试
path("sql_test", views.sql_test),
2.views逻辑函数操作数据库
# 操作数据库测试
def sql_test(request):
conn = pymysql.connect(host='localhost', user='root', passwd='shuai20011025.',
db='pymysql', port=3306, charset='utf8') # 连接对象
cursor = conn.cursor() # 鼠标对象
# sql语句
action = "select * from pymysql.douban_movies_top250 where name='你的名字。 君の名は。'"
cursor.execute(action) # 执行sql语句
data = cursor.fetchall() # 获取数据结果
return HttpResponse(data)
3.但是,简单的数据库操作具有很多风险:
自己写sql的问题:
1.sql注入:参考截图笔记2.2sql注入相关示例截图
解决方法:没有绝对安全的方法,能做的只能是尽可能的去测试发现此类问题,然后规避问题
2.代码与sql语句写死在一起,导致 解耦性差,即可扩展性低(比如要更换数据库类型MySQL->MongoDB),
特别是项目成型后,几乎不可能把所有sql语句全都修改一遍,因为语法也会存在很多问题
3.开发人员的sql水平不一,导致性能等多方面问题
4.开发效率低,实际中很多代码开发人员操作数据库的能力都不高(程序员不一定是专业的数据库人员,不是天天写sql语句)
2.views中利用ORM操作数据库:
1.什么是ORM?
1.概念
对象关系映射 (Object Relational Mapping),它的实质就是将关系数据(库)中的业务数据用对象的形式表示出来并通过
面向对象( Object- Oriented)的方式将这些对象组织起来实现系统业务逻辑的过程。
ORM过程中最重要的概念是映射( Mapping)·通过这种映射可以使业务对象与数据库分离。从面向对象来说数据库不应该和业务
逻辑绑定到一起,ORM则起到这样的分离作用·使数据库层透明,开发人员真正的面向对象。
2.实质
虽然是把数据库的操作转换为面向对象的方式,但其最底层实质肯定也是sql语句,只不过经过了ORM的封装,变为了面向对象的方式,
使数据库层变得透明。
3.优缺点
1.优点:
·实现了代码与数据库操作的解耦合
·不需要自己写原生的sql,提高开发效率
·可以有效防止sql注入的问题
2.缺点:
·牺牲了一点性能(但牺牲相对较少):
封装后只是对开发人员透明,实质却多了一个操作转换处理的过程,程序需要把我们的操作转换为对应的sql语句操作
·处理复杂操作语句时可能会力不从心:
但已经能够适用于大多数的一般场景
2.ORM映射字段:
1.参考网络资料,其中一个参考:https://zhuanlan.zhihu.com/p/55438668
2.一个ORM常用映射字段参考整理:见本目录下"ORM常用映射字段.txt" 文档
3.映射字段概览:
1.字段类型:
1.AutoField:自增
2.BigAutoField:64-bit自增
3.BigIntegerField:大整数
4.BinaryField:二进制(可以图片转换为二进制存储)
5.BooleanField:布尔类型(True/False)
6.CharField:字符类型
7.DataField:日期(精确到天:2021-11-28)
8.DateTimefield:日期时间(精确到秒:2021-11-28 16:49:23)
9.DecimalField:
10.DurationField:区间格式的时间:[DD] [HH:[MM:]]SS[.UUUUUU]
11.EmailField:数据库没有这个类型,属于ORM一个扩展的数据类型,实质是一个符合邮箱格式的字符串,可以帮助做格式校验
12.FileField:存储文件
13.FloatField:浮点类型
14.ImageField:图片类型,也是一个扩展的,验证符合图片格式的,本质也是二进制类型
15.IntegerField:普通整型
16.GenericIPAddressField:IP地址,同时支持支持IPV4/IPV6
17. NullBooleanField:也是布尔类型,但是允许设为NULL(空值)
18.PositiveIntegerField:也是一个扩展类型--正整数,数据本身没有这个类型
19. PositiveSmallIntegerField:正的小数,0~32767
20.SlugField:也是扩展的类型--标签类型,实质也是一个字符串,不常用
21.SmallIntegerField:小整数
22.TextField:大的文本类型(longtext)
23.TimeField:扩展--Python中的时间类型
24.URLField:扩展--网页URL类型
25.UUIDField:扩展--Python中的UUID类型
2.字段参数:
1.ForeignKey: 外键关联
2.ManyToManyField:
多对多,比如给一个论坛的帖子打标签,一个标签可以对应多个帖子,一个帖子也可能被多个标签所关联
3.OneToOneField: 1对1
4.更多参数见笔记"ORM常用映射字段.txt"
3.ORM操作数据库实例:
1.设计数据库,见笔记"ORM操作数据库实例.txt"
2.数据库的配置在setting.py内的database参数下设置,默认为sqlite,更改可参考官方说明文档:
https://docs.djangoproject.com/zh-hans/3.2/ref/settings/#databases
1.mysql配置示例:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test_django_blog_db',
'USER': 'mydatabaseuser',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '5432',
}
}
2.还需要在settings.py中的INSTALLED_APPS列表内加上我们要操作的app,否则同步数据库时会检测不到
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01', # 需要被操作的app需要添加在这里,否则同步数据库等操作时可能会检测不到
]
3.要注意的是,Python3连接mysql得使用pymysql。MysqlDB模块300年没更新了,但django默认调用的还是MySQLdb,
so pymysql有个功能可以让django以为是用了MySQLdb。即在"项目"目录下的__init__.py中加上句代码就好:
import pymysql
pymysql.install_as_MySQLdb() # 让django以为是用了MySQLdb
3.同步数据库:
1.需提前在mysql中建好前面项目数据库名称对应的数据库,因为django只能帮我们操作数据库,但前提是先要有这个数据库
2.django数据库同步工具 migration,后期数据库字段的增减等都会被这个工具同步到数据库,其操作有两步:
1.生成同步文件
python manage.py makemigrations
2.同步操作
python manage.py migrate
4.ORM操作数据库,见笔记"ORM操作数据库实例.txt"
3.Admin(管理员)
1.django admin简述及简单使用:
由于在命令行(shell)敲很多代码操作数据库不是那么方便,django默认自带了一个组件admin,是一个专门提供给用户的web管理界面,
用以管理数据库中的表信息。在页面中进行一些点击操作就能间接操作数据库,非常方便。
同时还提供了很多定制化功能,甚至能用来进行公司内部的内容管理。
一般在使用startproject命令创建项目时django admin就默认启动了。
但是在使用前,还有一些配置上的注意事项:
1.在INSTALLED_APPS(settings.py中的变量) 设置中添加 'django.contrib.admin'(一般会默认生成)
2.四个app依赖关系(一般默认生成,否则手动添加在在INSTALLED_APPS列表内):
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
3.两个模板依赖
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
一般默认生成,否则手动添加在TEMPLATES字典列表内的OPTIONS键下的context_processors内,如下:
TEMPLATES = [ # 模板,处理html文件
{
......
'OPTIONS': {
'context_processors': [
......
'django.contrib.auth.context_processors.auth', # 依赖1
'django.contrib.messages.context_processors.messages', # 依赖2
],
},
},
]
4.简单使用admin:
1.admin页面默认访问地址:'http://localhost:port/admin/'
2.在访问到admin页面后,需要先登录,但是登录需要用户名和密码,默认我们是没有的,因此需要先创建一个用户名和密码
创建admin系统的用户名和密码(需在和项目启动同样的路径下执行):
命令行输入 'python manage.py createsuperuser'
然后依据提示设置自己的用户名和密码,例如:
...\my_site>python manage.py createsuperuser # 执行命令
Username (leave blank to use 'admin'): wxs # 设置用户名
Email address: [email protected] # 设置邮箱,测试时可随意填,也可留空
Password: # 保密起见,密码默认是不会显示出来的,敲就完了,但注意别敲错或忘记
Password (again): # 二次确认
Superuser created successfully. # 创建成功的响应
3.使用创建好的用户名及密码登录admin系统
4.默认首页有两个表:Users和Groups(用户和组),users表内存放的是管理员信息
5.通过点击表可查看表的数据项,选择数据项即可直接可视化地进行该数据项的修改
6.默认管理界面并没有我们建立的app的表,要通过admin管理我们自己的表,就需要进行一些配置。
在每个app下都有一个admin.py文件,需要在该文件内配置注册我们app自建的表,示例如下:
from app01 import models # 注意要引入我们的models
# 对我们自建的app表进行admin的注册
admin.site.register(models.Account) # Account表
admin.site.register(models.Article) # Article表
admin.site.register(models.Tag) # Tag表
然后刷新admin页面即可看到我们的自建的app表
然后就可以可视化的管理我们的表了
如果设置了级联删除,在删除时会提示可能会被级联删除的其他数据项,并再次让用户确认
同时可以在右侧选择按照某个字段来过滤,搜索等操作(但是这些功能都属于定制功能,需要配置)
看起来,操作相当方便。
2.admin的自定制
1.要为自建的app表定制功能,需要在admin.py下定义一个类,该类要继承于admin.ModelAdmin类:
class AccountAdmin(admin.ModelAdmin): # 创建一个类来为自己的表自定制功能,类名随意
pass # 编写自定制的内容
# 还要将自定制的类与对应的表相关联,如下:
admin.site.register(models.Account, AccountAdmin) # Account表,同时关联自定制类
2.admin自定制(重写父类admin.ModelAdmin字段及方法):
1.常用字段示例(以下列表字段如果以元组形式显示单个元素也要加逗号):
1.list_display = ['username', 'email', 'signature'] # 可见性功能,定义表内数据项的简略可见信息
通过点击这些字段表头,还可以进行排序,支持混合多字段进行排序。
list_display的一些自定制功能:
1.显示临时字段(数据库中实际没有,但可以显示在admin数据表页面):
1.先在对应数据表对象类中创建临时字段(自定义一个方法),示例如下:
# 临时字段方法:用户评论字段
def get_comment(self): # 定义一个临时字段,不会出现在数据库,但可以被admin当做一个字段调用
return 10
2.在admin.py中调用示例:
class ArticleAdmin(admin.ModelAdmin): # 创建一个类来为自己的表自定制功能
"""编写Article表自定制的内容"""
......
list_display = ['title', 'account', 'pub_date', 'get_comment'] # 定义表内数据项的简略可见信息
......
3.用途举例1:
由于list_display列表不支持显示多对多关联字段,因此可以用这种自定义方法,将其间接显示出来,示例:
1.文章表标签字段(多对多关联)转换方法示例:
def get_tags(self): # 定义一个方法用于返回多对多字段tags的内容
return ','.join([tag.name for tag in self.tags.all()]) # 构建列表生成式并用逗号连接成字符串
2.调用临时字段方法:
list_display = ['title', 'account', 'pub_date', 'get_tags'] # 定义表内数据项的简略可见信息
3.然后就可在admin中看到文章表的对应标签信息
4.用途举例2:
设置临时字段的样式(借助format_html方法,返回html文本):
1.标签名渲染方法(根据标签的颜色编码返回对应颜色的标签文本,用span包裹):
from django.utils.html import format_html # 引入html文本格式化方法
...
color_code = models.CharField(max_length=6) # 颜色编码字段
...
def colored_name(self): # 临时字段,根据编码及标签名返回带颜色的span标签
return format_html(
'{}',
self.color_code,
self.name
)
2.在admin.py下标签表自定制类中调用:
class TagAdmin(admin.ModelAdmin):
list_display = ['name', 'colored_name']
3.然后就可以看到admin标签表展示了带颜色的标签样式,文章表临时字段展示多对多关联标签时也可以调用:
def get_tags(self): # 定义一个方法用于返回多对多字段tags的内容
# 构建列表生成式并用逗号连接成字符串,然后再格式化成html文本返回
return format_html(', '.join([tag.colored_name() for tag in self.tags.all()]))
2.search_fields = ['username', 'email'] # 搜索功能,支持多字段模糊查询
3.list_filter = ['account', 'pub_date'] # 过滤功能,支持定义不同字段的过滤,对不重复字段意义不大
4.# fields定义数据项详情页,设置数据项详情页字段对用户的可见范围以及其布局格式
fields = [('title', 'account'), 'content', ('tags', 'pub_date')] # 例如将title和account放在一块区域...
但是要注意,如果必填字段被隐藏,再进行修改添加时可能会保存失败,因为有必填项为空,无法保存
并且针对admin,无论数据表设置字段是否可以为空,在admin默认都是必填的,如果要明确这些情况非必填,需要进行配置
在对应models下的数据表类内的该字段设置中添加blank=True(表示在admin中非必填项),详情页的该字段也不再是粗体,
意为非必填项,粗体为必填项。必填项不填会报错,非必填项不填默认提交的是空字符串''
注意null限制的是数据表字段是否可为空,blank限制的是admin页面是否必填,但是两者应当一致,例如非必填项设置了
不为空或者必填项设置了可以为空均可能导致在admin系统提交时出现异常情况。
5.exclude = ['tags'] # 与fields相反,exclude决定了不显示哪些字段,注意不能和fields同时使用
6.date_hierarchy = 'pub_date' # 按指定日期类型字段进行分组显示数据表项
7.# fieldsets数据详情页分组显示,将某几个相关字段划分为一个组进行显示,并且可以增加样式
fieldsets = [
['文章相关', { # 组名
'fields': [('title', 'account'), 'tags', 'content'], # 组字段划分
'classes': ['wide', 'extrapretty', ] # 定义样式宽、自动美化等
}],
['发布相关', {
'fields': [('read_count', 'pub_date')],
'classes': ['collapse'] # 定义样式collapse收缩:默认该分组为收缩态(折叠未展开)
}]
]
8.filter_horizontal = ['tags', ] # 水平过滤,只针对多对多字段,让用户在选择时更方便
9.filter_vertical = ['tags'] # 垂直式过滤,和上一行功能上一样,但是不美观,因此较少用
10.list_per_page = 20 # 限制每页数据项数,实现分页显示
11.list_display_links = ['username', 'email'] # 设置数据项哪些字段可以被点击跳转到修改页面
12.radio_fields = {'account': admin.VERTICAL} # 设置外键选择方式为单选按钮,本来外键选择默认为下拉框
注意admin.VERTICAL设置其布局为垂直式,admin.HORIZONTAL为水平式
13.autocomplete_fields = ['account'] # 设置带有自动补全功能,对外键有效
14.raw_id_fields = ['account'] # 设置外键选择会默认展示其id,但是貌似没太大用,与上面部分设置不能混用
15.readonly_fields = ['read_count'] # 设置字段属性为只读(不可修改)
16.list_editable = ['signature'] # 设置数据表项某些字段可编辑(不用进详情页)
可以同时批量修改多条数据项的某些字段
17.自定义admin中显示的表名(在对应models.py下对应对象类内定义):
class Meta: # 修改源类以自定义表名
# verbose_name = "标签表" # 自定义表名
verbose_name_plural = "标签表" # 针对英文,否则默认是复数会加s
4.views(视图)
1.views基础回顾与介绍
在路由系统中已经初步接触过views的一些基础操作,现简单进行回顾,其中一些基础配置可以回顾第一章笔记相关内容。
视图主要负责处理业务逻辑,根据路由系统分发指定url请求到views,views处理请求,并返回响应(数据库资源、
html、js、css、image等)
views中的业务逻辑函数必须有一个默认的参数request,用以接收请求的各项参数(url、请求头、编码等等)
1.将处理结果用HttpResponse包裹并返回,一个业务逻辑函数实例如下:
# 业务函数固有格式,必须有一个默认参数,request参数内封装了各种请求信息(请求头、环境、编码、cookies等)
def test_view(request):
print("执行业务逻辑中, 计算第一次。。。", request)
# django框架通过调用相应HttpResponse返回数据,不需要繁琐的定义状态码,文本类型转换等
print(dir(request)) # dir查看被封装的对象的实际属性
return HttpResponse("
500一次!
")2.对于实际中的一个html文件,直接放在HttpResponse中返回显得不够规范,因此可以考虑使用render方法:
def login_view(request):
# html = """xxx..."""
# return HttpResponse(html)
return render(request, 'test_login.html') # 返回指定html页面文件可以使用这种方式
2.views详细学习-->HttpRequest对象
1.HttpRequest简述
每一个用户请求在到达视图函数的同时,django会自动创建一个HttpRequest对象并把这个对象当做第一个参数传给要调用的
views方法。HttpRequest对象例封装了本次请求所涉及的用户浏览器数据、服务器端数据等,在views里可以通过默认参数
request对象来调取相应的属性:
['COOKIES', 'FILES', 'GET', 'META', 'POST', 'class', 'delattr', 'dict', 'dir', 'doc', 'eq', 'for
mat', 'ge', 'getattribute', 'gt', 'hash', 'init', 'init_subclass', 'iter', 'le', 'lt
', 'module', 'ne', 'new', 'reduce', 'reduce_ex', 'repr', 'setattr', 'sizeof', 'str', '
subclasshook', 'weakref', '_current_scheme_host', '_encoding', '_get_full_path', '_get_post', '_get_raw_host', '_g
et_scheme', '_initialize_handlers', '_load_post_and_files', '_mark_post_parse_error', '_messages', '_read_started', 'set
content_type_params', '_set_post', '_stream', 'upload_handlers', 'accepted_types', 'accepts', 'body', 'build_absolute_uri
', 'close', 'content_params', 'content_type', 'csrf_processing_done', 'encoding', 'environ', 'get_full_path', 'get_full_pa
th_info', 'get_host', 'get_port', 'get_raw_uri', 'get_signed_cookie', 'headers', 'is_ajax', 'is_secure', 'method', 'parse
file_upload', 'path', 'path_info', 'read', 'readline', 'readlines', 'resolver_match', 'scheme', 'session', 'upload_handler
s', 'user']
2.HttpRequest对象常用属性:
1.HttpRequest.scheme:标识请求的协议(一般为http或https)
2.HttpRequest.path:返回当前请求的url路径(不会显示根路径' localhost:8000/')
3.HttpRequest.method:返回请求类型(get、post...)
在网站接口开发规范(Restful API)中:
1.GET:一般用于获取数据,但不修改;
2.POST:一般用于修改、创建(提交数据);
3.PUT:一般用于修改、更新;
4.DELETE:一般用于删除;
实际在url-->views的中途还经过了django的一个中间件(可看做防火墙)的检验,在请求到达views前会先经过防火墙
的检验(安全验证),验证不通过则不会到达views,而是返回403等错误信息;
但是可以人为地设置关闭防火墙:
在settings.py中MIDDLEWARE(中间件服务)列表中注释掉 'django.middleware.csrf.CsrfViewMiddleware'
4.HttpRequest.content_type:显示请求的MIME类型(MIME消息可包含文本、图像、音频、视频及其他应用程序专用数据)
MIME详细说明可查看MIME参考手册:'https://www.w3school.com.cn/media/media_mimeref.asp'
5.HttpRequest.GET/POST(具体选择哪一个依据前端页面发送的请求类型):
返回请求发送的数据(以字典形式),以get方式请求会将数据明文显示在浏览器地址栏;post则会隐藏数据;
6.HttpRequest.FILES:获取到前端页面提交的文件对象(可以进行保存等处理)
7.HttpRequest.META:获取到 请求头 的所有源数据字典
3.HttpRequest对象常用方法:
1.HttpRequest.get_host():返回网站服务器地址,例如:' localhost:8000'
2.HttpRequest.get_port():返回服务器主机端口,例如:8000
3.HttpRequest.get_full_path():返回请求的路径(和path一样不包含服务器地址),例如:'/app01/test'
4.HttpRequest.build_absolute_uri(location):
参数location为空默认返回包含服务器地址的当前请求路径,例如:'http://localhost:8000/app01/test'
参数location不为空时表示将服务器地址和指定的location进行拼接,
例如:location='/app01/asdasdas'时 --> 返回值为'http://localhost:8000/app01/asdasdas'
5.HttpRequest.is_secure():判断请求是否为https,是则为True,否则为False
6.request.is_ajax():判断是否为ajax请求
3.views详细学习-->HttpResponse对象
1.HttpResponse对象基本属性:
HttpResponse._init_(content=",content_type=None,status=200,reason=None,charset=None)
HttpResponse对象具有以下内容:
1.状态码(200、404...),具体可查阅'https://www.w3cschool.cn/http/g9prxfmx.html'
2.content_type(text/html、text/plain...)
3.content(给前端返回的内容)
4.charset(编码规范)
5.reason(产生返回结果的原因)
默认状态码为200,文本类型为html,但是支持修改
2.HttpResponse返回文件(前端下载文件请求):
一个views下载文件函数实例:
def download_file(request): # HttpResponse下载文件测试
f = open('static/images/test_form.png', 'rb') # 以字节流打开一个.png文件
data = f.read() # 读取文件数据
f.close() # 关闭文件
# 默认返回是文本,因此要返回各类文件流,要设置content_type
# 各种content_type类型见'https://www.w3cschool.cn/http/ahkmgfmz.html'
response = HttpResponse(data, content_type='application/x-png')
# Content-Disposition定义了文件保存到客户端的配置,文件名等
response['Content-Disposition'] = 'attachment; filename="form_test.png"' # 定义文件保存的命名
return response # 返回数据
通过路由分发器定义指定url分发到该逻辑函数,访问前端页面即可看到文件下载到本地,打开内容也没有问题。
3.HttpResponse对象子类:
1.HttpResponseRedirect:地址重定向(将的url重定向到新的url),状态码为302
def redirect_test(request): # HttpResponseRedirect重定向测试
print('HttpResponseRedirect test!')
return HttpResponseRedirect("/app01/request_test") # 返回重定向的目标url
2.HttpResponsePermanentRedirect:永久性重定向,类似上面普通重定向,但状态码为301
3.HttpResponseNotFound:状态码为404,但实际还可以返回指定的内容,一般用于错误页的重定向
from django.http import HttpResponseNotFound
4.类似的子类还有很多,都是状态码的区别
4.基于类的视图(在views中定义类)
前面一直是用的都是FBV(function based views),基于函数的视图;
下面来看CBV(class based views),基于类的视图。
用面向对象的思想定义逻辑函数,方便代码的复用,提高可读性,省去了根据method调用相关函数的判断步骤代码,支持类的继承等
1.一个定义view类的实例:
1.在views中定义view类继承view基类:
from django.views import View # 导入view基类
class TestView(View): # 自定义view类继承django的view基类
def get(self, request): # 对应的处理get请求的方法(命名需为get)
return HttpResponse("Class view get request!")
2.在url中定义分发关系:
Django的url收到请求后,需要将其分配给一个可调用的函数,而不是一个直接的类。因此针对这个问题,
CBV 的view基类提供了一个as_view()静态方法,也就是类方法,调用这个方法,回先创建一个类的实例,
然后通过实例调用一个dispatch()方法,dispatch()方法会根据request的method的不同调用相应的
方法处理request(如get()、post()等)。如果请求的方法不存在则会有Method Not Allowed...错误。
一个CBV路由规则实例:
path('class_view', views.Test_view.as_view()), # 基于类的视图测试
2.返回类的属性:
time = datetime.datetime.now() # datetime时间
def get(self, request): # 对应的处理get请求的方法(命名需为get)
return HttpResponse("Class view get request! time is %s" % self.time)
3.类视图的继承
class TestView2(TestView): # 继承自TestView类
time = 10 # 重写属性time的值为10
4.路由分发给类视图传递参数:
path('class_view', views.TestView.as_view(time=20)), # 基于类的视图测试
同样也改变了类视图的time属性值,但不常用
------------------Alex B站课程到此断更,以下完全为查找资料整理---------------------
5.Template(模板)
官方文档:'https://docs.djangoproject.com/zh-hans/3.2/topics/templates/'
官方文档摘录:
作为一个网络框架,Django 需要一种方便的方式来动态生成 HTML。最常见的方法是依靠模板。一个模板包含了所需 HTML 输出
的静态部分,以及一些特殊的语法,描述了如何插入动态内容。
一个 Django 项目可以配置一个或多个模板引擎(如果你不使用模板,甚至可以不配置模板)。Django 内置了自己的模板系统后端,
创造性地称为 Django 模板语言(DTL)。其他模板语言的后端可以从第三方获得。你也可以编写自己的自定义后端
Django 定义了一个标准的API,用于加载和渲染模板,而不考虑后端。加载包括为给定的标识符找到模板并对其进行预处理,通常是
将其编译成内存中的表示形式。渲染是指将上下文数据插入模板,并返回结果字符串。
Django 模板语言 是 Django 自己的模板系统。在 Django 1.8 之前,它是唯一的内置选项。它是一个很好的模板库,尽管它是
相当有主见的,并且有一些特殊的地方。如果你没有迫切的理由选择另一个后端,你应该使用 DTL,特别是当你正在编写一个可插拔的
应用程序,并且你打算发布模板时。Django 的 contrib 应用如果包含模板,比如 django.contrib.admin,就使用 DTL。
由于历史原因,模板引擎的通用支持和 Django 模板语言的实现都在 django.template 的命名空间中。
警告:
模板系统对于不受信任的模板作者并不安全。例如,一个网站不应该允许其用户提供自己的模板,因为模板作者可以做一些事情,
如执行 XSS 攻击和访问可能包含敏感信息的模板变量的属性。
1.Template简述
前面的学习中,直接将HTML硬编码在Python代码中处理,尽管看上去一目了然,更容易理解视图的工作原理,但这显得不够
系统化和专业化,原因有以下几点:
1.前端页面与视图耦合度太高,前端页面的设计改变都必须修改相应的Python代码,显得不够方便;
2.没有体现前后端分离的思想,现在实际工作中,一个完整网站系统的开发都是由多个子部门共同完成,
各有分工。设计者和HTML/CSS编码人员不应该被要求同时去编辑Python的代码工作;
3.程序员专注于编写Python代码,设计人员专注于制作模板,两者同时进行的工作效率是最高的;
基于以上原因,将页面设计与Python代码编写工作进行分离,代码会更简洁和易于维护。Django提供的
模板系统(Template System)就是为了实现这种前后端分离的模式:
Template模板:HTML代码 + 模板语法
在views中使用render方法处理html文件,实现前后端分离:
return render(request, 'form.html') # 返回指定html页面文件可以使用这种方式
2.Django 模板语言:
参考1:'https://www.jianshu.com/p/3e3b6fa8d861'
1.语法:
Django 模板是使用 Django 模板语言标记的一个文本文档或Python字符串。模板引擎可以识别和解释一些构造。
主要是变量和标签。
模板是通过上下文来渲染的。渲染用变量的值替换变量,变量的值在上下文中查找,并执行标签。其他的一切都按原样输出。
Django 模板语言的语法涉及四个构造。
1.模板语法之变量
1.语法:在 Django 模板中遍历复杂数据结构的关键是句点字符。语法形式为{{var_name}}
2.一个template实例:
{{s}}
列表:{{ l.0 }}
列表:{{ l.2 }}
字典:{{ dic.name }}
日期:{{ date.year }}
类对象列表:{{ person_list.0.name }}
注意:句点符也可以用来引用对象的方法(无参数方法):
字典:{{ dic.name.upper }}
3.一个views.py实例:
def index(request):
import datetime
s="hello"
l=[111,222,333] # 列表
dic={"name":"yuan","age":18} # 字典
date = datetime.date(1993, 5, 2) # 日期对象
class Person(object):
def __init__(self,name):
self.name=name
person_yuan=Person("yuan") # 自定义类对象
person_egon=Person("egon")
person_alex=Person("alex")
person_list=[person_yuan,person_egon,person_alex]
return render(request,"index.html",{"l":l,"dic":dic,"date":date,"person_list":person_list})
4.测试实例(配置好相应的路由规则):
列表:111
列表:333
字典:yuan
日期:1993
类对象列表:yuan
2.模板语法之标签
1.Django模板标签简述:
标签在渲染过程中提供了任意逻辑。
这个定义是故意含糊的。例如,标签可以输出内容,或用作控制结构如 “if” 语句和 “for” 循环,或从数据库
中抓取内容,甚至可以访问其他模板标签。
标签被 {% 和 %} 包围,如下所示:{% csrf_token %}
大多数标签都接受参数:{% cycle 'odd' 'even' %}
一些标签需要开始和结束标签:
{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}
2.Django模板内联标签:可查看官方文档 'https://tinyurl.com/yab7wxll' 或
'内置模板标签和过滤器 Django 文档 Django.htm'
3.模板语法之过滤器:
1.语法:过滤器转换变量和标签参数的值。它们看起来像这样:{{ django|title }}
有些过滤器需要一个参数:{{ my_date|date:"Y-m-d" }}
2.Django模板内联过滤器参考:可查看官方文档 'https://tinyurl.com/yab7wxll'
或查看笔记 'Django内联标签与过滤器整理.txt' 以及 '内置模板标签和过滤器 Django 文档 Django.htm'
几个常用标签举例:
1.for标签:遍历每一个元素:
可以利用{% for obj in list reversed %}反向完成循环。
1.遍历一个字典:
{% for key,val in dic.items %}
{{ key }}:{{ val }}
{% endfor %}
2.注:循环序号可以通过{{forloop}}显示:
forloop.counter The current iteration of the loop (1-indexed)
forloop.counter0 The current iteration of the loop (0-indexed)
forloop.revcounter The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0 The number of iterations from the end of the loop (0-indexed)
forloop.first True if this is the first time through the loop
forloop.last True if this is the last time through the loop
3.for ... empty:for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,
可以有所操作。
{% for person in person_list %}
{{ person.name }}
{% empty %}
sorry,no person here
{% endfor %}
2.if 标签:{% if %}会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),
对应的内容块会输出。
{% if num > 100 or num < 0 %}
无效
{% elif num > 80 and num < 100 %}
优秀
{% else %}
凑活吧
{% endif %}
3.with标签:使用一个简单地名字缓存一个复杂的变量,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次
的时候是非常有用的
例如:{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
4.csrf_token标签:这个标签用于跨站请求伪造保护
4.模板语法之Comments:
注释看起来像这样:
{# this won't be rendered #}
{% comment %} 标签提供多行注释。
2.自定义标签和过滤器
自定义标签和过滤器的步骤如下:
1、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.
2、在app中创建templatetags模块(模块名只能是templatetags)
3、创建任意 .py 文件,如:my_tags.py,一个实例:
from django import template
from django.utils.safestring import mark_safe
register = template.Library() #register的名字是固定的,不可改变
@register.filter
def filter_multi(v1,v2):
return v1 * v2
@register.simple_tag
def simple_tag_multi(v1,v2):
return v1 * v2
@register.simple_tag
def my_input(id,arg):
result = "" %(id,arg,)
return mark_safe(result)
4、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py
{% load my_tags %}
5、使用simple_tag和filter(如何调用)
-------------------------------.html
{% load xxx %}
# num=12
{{ num|filter_multi:2 }} #24
{{ num|filter_multi:"[22,333,4444]" }}
{% simple_tag_multi 2 5 %} 参数不限,但不能放在if for语句中
{% simple_tag_multi num 5 %}
注意:filter可以用在if等语句后,simple_tag不可以
{% if num|filter_multi:30 > 100 %}
{{ num|filter_multi:30 }}
{% endif %}
3.模板的继承(extend)
在前端页面开发中。有些代码是需要重复使用的。这种情况可以使用include标签来实现。也可以使用另外一个比较强大的方式
来实现,那就是模版继承。模版继承类似于Python中的类,在父类中可以先定义好一些变量和方法,然后在子类中实现。模版
继承也可以在父模版中先定义好一些子模版需要用到的代码,然后子模版直接继承就可以了。并且因为子模版肯定有自己的不同
代码,因此可以在父模版中定义一个block接口,然后子模版再去实现。以下是父模版的代码:
{% load static %}
{% block title %}我的站点{% endblock %}
{% block content %}{% endblock %}
这个模版,我们取名叫做base.html,定义好一个简单的html骨架,然后定义好两个block接口,让子模版来根据具体需求
来实现。子模板然后通过extends标签来实现,示例代码如下:
{% extends "base.html" %}
{% block title %}博客列表{% endblock %}
{% block content %}
{% for entry in blog_entries %}
{{ entry.title }}
{{ entry.body }}
{% endfor %}
{% endblock %}
需要注意的是:extends标签必须放在模版的第一行。
子模板中的代码必须放在block中,否则将不会被渲染。
如果在某个block中需要使用父模版的内容,那么可以使用{{block.super}}来继承。比如上例,{%block title%},
如果想要使用父模版的title,那么可以在子模版的title block中使用{{ block.super }}来实现。
在定义block的时候,除了在block开始的地方定义这个block的名字,还可以在block结束的时候定义名字。比如
{% block title %}{% endblock title %}。这在大型模版中显得尤其有用,能让你快速的看到block包含在哪里。