静态文件
我们给应用创建完需要创建static文件夹,里面一般是css,js,images三个文件夹,然后settings.py
之前我们讲了可以设置STATICFILES_DIRS,这回我们设置STATIC_URL,原来的默认是static,比如我们改成abc
我们如果让STATIC_URL是原来的默认值,我们给images里面加个图片文件,我们超链接就要用src='/static/images/mm.jpg',而如果我们STATIC_URL设置为'/abc/',我们的资源就要写成'/abc/images/mm.jpg'才能访问到
我们设置STATIC_URL的作用是,让页面使用我们定义的路径资源,这样我们检查源代码也会发现其a连接是'/abc/images/mm.jpg',而不是知道我们服务器具体的文件夹
静态文件加载目录
django是怎么识别我们的文件呢,
我们可以尝试在视图函数里导入settings
from django.conf import settings
然后再函数里打印settings.STATICFILES_FINDERS,我们就把结果写到注释里,可以看到是个元组,就知道django是现在项目的static资源里查找,然后再去app应用里的static资源里去查找(这里就是让我们了解下机制)
静态文件配置页面使用
我们知道静态资源STATIC_URL可能随时会变(比如index或abc),我们怎么能保证随着设置修改呢
我们首先需要导入静态配置{% load staticfiles %},如上图,然后在资源文件里写上
{%static 'images/mm.jpg'%}就实现了访问我们的图片,当STATIC_URL变就不怕了
所以建议项目进行静态资源配置使用,防止多次修改
中间件
我们使用中间件可以干预请求应答过程,比如某些ip我们不让访问
我们可以通过request.META['REMOTE_ADDR']获得浏览器端的ip地址
我们可以打印ip,同样,也可以设置一个列表,如果Ip在禁止列表中,就将其返回特殊页面
如果我们还希望访问其他资源也禁止,那我们可以定义个装饰器函数,内层判断的是否在禁止列表,然后给每个访问资源的函数加上装饰器,但是如果我们这么写,如果定义的函数多了,就会很麻烦,django让我们使用中间件,就可以让一部分内容在函数执行前先执行,
我们使用中间件,先创建一个middleware.py函数(不能写别的,在应用文件夹下面),然后内部需要自己定义一个类名,一般都带Middleware,把禁止列表作为类属性,然后定义实例方法process_view(self,request,view_func,*view_args,**view_kwargs),这个也必须按这个写
然后就是注册中间件,我们在项目的settings.py的MIDDLEWARE_CLASSES列表里添加我们的中间件'booktest.middleware.BlockedIPSMiddleware',这时我们之前就不用定义装饰器函数了,django自动给我们配置好,让该禁止的ip走流程~~
当然还有其他预留函数,如上
中间件执行流程
浏览器请求我们Django网站时,首先会生成一个request对象,这个对象会调用中间件的process_request函数,然后进行url匹配,然后调用中间件类的process_view,然后调用view视图函数,调用中间件类的process_response方法,最后响应给浏览器,
这个过程中,没到中间件类函数,django就会到MIDDLEWARE_CLASSES里面的列表里注册的中间件类逐个查找,哪个有定义了对应的执行函数,就执行哪个,这些函数都是Django预留给我们让我们定制处理用
我们在middleware.py新建一个类TestMiddleware,然后分别添加几个方法和打印
这里说明下init是服务器启动第一次接受request请求(不需要接收request参数)
process_request是生成request对象(需要request参数),配置url之前
process_view是url匹配之后,视图函数处理前,参数要request和view_func,*view_args,**view_kwargs,就是我们之前讲的装饰器
process_response是视图函数调用后,内容返回浏览器之前,我们要接收request,response,而且最后要把response返回去!!!
我们将中间件在settings.py里注册后运行,就可以看到如上的打印效果
有了中间件,我们的执行过程就变得可以定制化,比如我们对process_request直接返回个response,就会跳过中间的几步,直接去执行,process_response
我们在上图中修改process_request函数,这回直接返回HttpResponse,可以看到如下图结果,没有process_view和视图函数过程
所以各个中间件函数,如果有返回response就直接到最后,返回none就继续按流程执行
当然中间件还有process_exception函数,是视图函数出现exception异常执行,参数为request,exception,上图我们定义了2个中间件都是处理异常,然后都给他们注册,我们故意在视图函数里写上bug,然后运行,
可以看到调用的顺序和我们setting.py里注册的顺序是相反的
后台管理
1后台管理我们之前也说过,这里进行强化,我们首先要配置setting.py里语言时区设置
2创建超级管理员,视频的pdf拼错了manager
3在models.py里创建模型类,下图是实现给地区类设计模型,使用了自关联
4这个表格因为之前创建过,就没必要迁移了
5注册模型类,我们在admin.py里导入我们的类,然后使用admin.site.register注册
注册完就能看见admin登录后有我们的类
列表页编辑
点进来默认显示是对象,这是因为我们模型类没定义__str__方法
模型类添加方法
显示信息变成如下
现在我们就要学习如何自定义显示内容,就需要自定义模型管理类
我们在admin.py里创建管理类AreoInfoAdmin,继承自admin.ModelAdmin,比如我们添加类属性list_per_page = 10 #设置每页显示10条数据,然后在注册哪里添加上我们的管理类作为参数
这样刷新页面,就会看见每页显示10条信息了
我们之前也说过,显示模型类的某些信息,我们可以使用list_display=[属性字符串1...]来指定我们显示内容
我们设定好,刷新页面,可以看到是按id,和Atitle显示内容
当然list_display不光可以写属性名,还可以写属性方法
比如我们又给AreaInfo定义了title方法,返回值是实例的atitle
我们给列表加上title字符串,刷新页面,
可以看到我们方法反回的内容也显示到了页面上
我们点击管理页面的字段名时,会默认从小到大排列,但是只有属性的有效,而函数返回的点没效果
我们想让函数返回的也能点击排序,我们需要在类函数下面加上
函数名.admin_order_field=属性名字符串
设置完,我们点击也能进行排序了
如果我们不想使用原方法名作为管理页面的列名,我们可以使用
函数名.short_description=替代字符串
我们指定后,可以看到管理页面的列名变化了
如果我们想给属性的列名重命名呢,我们定义字段的时候,可以传入参数
verbose_name=替代字符串
我们刷新页面可以看到效果
我们知道管理页面里面有个下拉列表框,里面有点击删除数据,而且是在字段之前,如果我们想页面数据底下也多一个下拉框呢
我们管理类有个属性actions_on_bottom,将其设置为True
然后就可以看到下面也有一个下拉页面,当然还有个actions_on_top是对应上面下拉框的,默认True,设置False则不显示
我们还可以给页面右侧添加过滤栏,list_filter=[xxx] 列表内为指定字段名,可以传多个
我们点击以什么过滤,就可以显示指定的信息
我们之前讲了增删改,见第一章,其实我们还可以添加查询框,search_fields=[xxx] 对列表指定的字段进行搜索
效果如上
当然我们还可以返回父地区,但是这里需要做一个判断,因为空对象是没有aParent的,即层级最高的辖区(省,直辖市)
编辑页选项
修改顺序
我们点进某一个记录,进入编辑页,我们可以看到标题在上面,父位置在下面,我们若想修改顺序
我们给管理类添加fields属性,里面为属性字段,设置好刷新
分组显示
我们如果想给字段分组显示,同时给每个组起个标题,就是分组设置
我们要按如上图上设置,创建fieldsets元祖,里面也是元祖构成,每个元祖的第一个为分组名称,第二个为字典,键为固定'fields',
值为这个组的字段构成的列表(注意,fields和fieldsets只能用一个)
关联对象嵌入
行嵌入
一对多关联显示比如我们修改广州市时,把下级的关联城市获得
首先我们需要在admin.py里创建类,这里起名AreaStackedInline,需要继承自admin.StackedInlie类,并在类属性里设置model=多类的类名(这里因为是自关联,就是AreaInfo)
然后我们在一类里添加inlines列表,里面存放我们刚才创建的内嵌类,这里因为是自关联,所以就在AreaInfo里添加
刷新页面就会发现广州市下面辖区的显示
而且在最后3行可以看到是3个空行,可以填入信息,若想修改空行数量按下图在内嵌类里加上extra=xxx设置数目,默认是3
除了行嵌入,我们还可以实现表格嵌入
表格嵌入
表格嵌入和行嵌入类似,需要继承admin.TabularInline
我们修改inlines为表格嵌入类列表,刷新页面,可以看到不一样的嵌入效果
当然其实我们也可以自己根据django的页面进行修改,我们可以在linux虚拟环境的Python---sitepackages里django--admin--templates里找到baseurl,然后进行编辑
上传文件
我们有时候比如商家自己上传图片还有用户上传头像都需要上传图片
首先,我们需要配置上传文件夹目录media,放在static下,然后settings.py里加上MEDIA_ROOT=os.path.join(BASE_DIR,'static/media')
上传方式分为后台管理员上传图片,用户上传图片2种
后台管理员上传图片
首先,models里建立模型类,使用models.ImageField创建字段,实例初始化加上upload_to='booktest'目录(相对与media文件夹)
然后迁移生成表格,最后管理页面注册模型类
操作如下
models.py里创建类PicTest,别忘了还是继承自models.Model类,创建图片字段,上传到media文件夹下的booktest
我们像以往一样进行迁移python manage.py makemigrations,python manage.py migrate
我们执行后会发现,No migrations to apply,这表示我开门的迁移没有执行!!!
这是为什么呢,我们进入mysql,查看django之前给我们创建的表格,里面有保存迁移记录的表django_migrations
我们查看表格信息,可以看到其中booktest里已经有0001_initial记录了,其实这是因为我们之前可能鼓捣了很多项目,都往booktest里迁移,导致django有重名的无法迁移
我们想实现迁移就只有2种方法,第一删除表格这项迁移记录,第二种是改迁移文件xx.py的名字
比如我们删掉记录(这种方法必须谨慎,我们只能删除对应记录,其他不要删掉,否则一堆出错!!)
这时,执行迁移出现了一堆错误,提示类已经存在了,因为之前的2次不同的项目的确都写了AreaInfo,只不过第二次没迁移
我们的做法是,在迁移文件.py里,把创建模型,已经建立的删掉,上图就是把第一个migrations.CreateModel关于AreaInfo的都给删掉
这次就可以执行迁移中(python manage.py migrate不要再执行makemigratioins),我们在mysql里就可以看见表格booktest_pictest
这里执行迁移的时候,提示我们的bookinfo,heroinfo可没有对应的模型类,问是否删除,视频建议不删,我觉得这都是视频作者懒的坑,以后可千万不要多个项目往一个数据库里塞
迁移成功后,在admin.py里导入模型类,然后注册下
首页就有了图片类展示,点击进去,什么数据都没有点击右侧增加
就会看到如下界面,我们点击选择文件,然后选择图片保存
就可以看到多了一条记录(此时已经上传成功)
同时可以卡看到我们的media的booktest下有了我们上传的文件
我们查询表格,可以看到数据库里存储了信息,但是数据是图片的相对路径
ImageField会帮我们识别上传的是否为有效图片文件,比如我们上传个py文件,会下图提示
用户自定义上传图片
1页面form里,方法post, action= '/upload action/' entype='multipart/form-data'
我们新建页面upload_pic.html,在表单里填入信息,而且加上了csrf防护,如上
我们定义视图函数,渲染刚才的页面,并在urls里配置好
我们就可以访问资源show_upload,页面如上
然而之前的页面只是能上传,并没有指定如何上传,上传得到哪里,我们还得按要求加上action属性。访问的资源为upload_handle,我们就得给这个资源定义函数和配置urls
我们视图函数里,首先要获取处理对象,然后创建文件,将文件写入到文件中,数据库保留记录,然后返回。对于上传的文件request.FILES通过我们html里的name属性值可以访问到,我们之前name='pic',就如上,访问,我们打印下他的类型,当然这里应该返回个HttpResponse('OK')
可以看到后台打印了类型,django.core.files.uploadedfile.InMemroyUploadedFile
我们再试着上传一个大图片11.6M,可以看到类变化了,django.core.files.uploadedfile.TeporaryUploadedFile
为什么处理对象不一样,这是Django根据上传文件大小判断的,如果文件不大于2.5M,为django.core.files.uploadedfile.InMemroyUploadedFile,放在内存中,否则为django.core.files.uploadedfile.TeporaryUploadedFile,放在临时文件中(防止占用过多内存)
不管哪个对象,我们的pic都有.name属性,获得用户上传文件名,也都有.chunks属性,其为生成器,通过遍历可以获得文件内容
路径需要导入settings,使用from django.conf import settings
知道这些,我们就可以创建文件,with open as f,用二进制wb写入,这里要导入settings,使用其配置路径,加上我们的文件名,将其写入
写入好苦,我们导入模型类,通过模型类创建记录PicTest.objects.create(goods_pic=xxx)
然后我们点击就成功实现了上传
当然pic对象无论是哪种上传,都是继承UploadedFile,有很多属性,我们可以自己查文档
如获得大小,类型,根据类型可以判断是否符合要求,
分页
分页显示在现在的很多信息多的网站很常用
我们先实现在一个页面显示AreaInfo所有省信息,定义视图函数,导入AreaInfo类,使用过滤器,然后获得的QuerySet给渲染作为参数,我们配置urls
创建页面,在页面里通过url>li显示内容
然后可以看到信息,当然,我们想实现的是分页显示,例如每页显示10个
django里实现分页要用到Paginator类from django.core.paginator import Paginator
我们要创建一个实例对象,第一个参数为查询集,这里是areas,第二个是每页显示数量
我们使用类实例方法.page(number)获得第number页的Page类实例对象
比如我们先获得第一页内容,我们导入类,然后page=paginator.page(1)获取到第一页的page对象,然后我们将这个配置作为参数传入
Page类对象有object_list方法,可以遍历到每个查询对象,我们就可以修改html页面内容
刷新页面,可以看到页面已经是10个内容了
Page实例其实有iter方法,返回的内容和object_list一样,我们也可以直接遍历page这个实例,效果一样
Paginator类实例属性有num_pages,返回分页后总页数,page_range返回分页后的页码列表(从1开始,如[1,2,3,4])
Page类实例有如上属性,都是有利于我们分页的,一会儿讲到
我们为了能实现访问不同页码,给资源加数字的格式设置资源,当然,页码也可以为空这里就用\d*,同时给子组起名pindex,然后就需要我们在show_area里加上参数pindex
我们需要判断pindex的内容,注意pindex捕获返回的是字符串,我们要先判断是否为空,为空设置为1,否则设置为转换的整数,这样我们就可以实例化page为指定页的
我们除了之前的显示十项内容,增加了,4个页码,页码是通过paginator类的方法的page_range,因为page有属性获得其paginator我们就如图中的写法,这样,我们点击不同的页码链接就会实现不同的页内容显示
当然当前页可以不让其成为链接,而是让其成为一个数字,这就用到Page类实例.number获得页码即可,然后索引哪里进行判断
html如上,当然我们还可以考虑做出来上一页,下一页实现跳转
这里利用了Page实例对象的方法,.has_previous,has_next判断是否有前一页,下一页,.previous_page_number,.next_page_number获得前一页码和后一页码,通过判断,显示如上
效果如上
当然Page,Paginator有很多方法属性,可以去文档查看
案例,省市县,关联下拉栏
案例效果如上,3个下拉栏,每个栏里的分别显示省市县,其中,只有选中省,才能选择对应市,然后选择对应县,这些信息都是动态ajax填写进来,不用重复发请求
我们先设计个带下拉框无内容的页面,上图设计视图函数,然后urls里进行配置
我们创建页面,3个select
我们访问这个页面,现在点击并没有任何内容,下面我们先做一进页面就加载省的信息,这部分用ajax做,我们还是用jquery,放到static的js文件夹如下
在页面部分,我们导入js,这里说下,我们可以直接使用$.get(资源,成功的回调函数)实现ajax的get方法,$.post(资源,表单字典,回调函数)实现ajax的post方法,当然我们这里是使用get,使用get是获取信息使用,使用post时修改数据使用
获取省信息
视频中是示范写法,最终写的函数是使用get方法
因为ajax请求需要返回json数据,我们返回HttpResponse数据,但是这里有坑
我们配置好urls,尝试访问网页,会发现ajax请求prov是500错误,查看提示数据不是json序列化的
为什么出这个错误,就是因为查询集对象为object我们只能将属性集合来序列化
我们修改视图函数,创建列表,从areas导出每个实例的id和atitle属性,作为二元组依次添加到列表中,然后再次刷新页面,发现OK了
在回调函数里,因为json存储数据键为data,我们使用data.data获取到数组,同时通过id获得prov下拉框对象,我们就可以遍历数组,将id,atitle解析,然后创建option字符串对象,使用下拉框的append方法,实现添加option标签(给id是为了对市县进行限定方便筛选,必须是选定省下面的)
除了for循环遍历,jquery还支持$.each(遍历对象,function(index,item){})这里,index为列表索引,item为每个元素,我们通过0,1即可获得id,atitle,其他相同,也同样能达到显示省的效果
获取市信息
我们获取市信息,首先要获得prov对象,这里绑定change事件,使用$(this)可获取到当前被操作的option对象,使用val()方法获得id,我们得到id就可以根据省份去请求资源,对于get方法,我们可以使用2种方式写资源,第一种$.get('city?prov='+prov_id,function(data)){},第二种如上图,使用正则捕获,让urls配置帮我们获得id,我们这里暂用第二种
我们配置urls如上设置,然后写视图函数
在视图函数里,我们接收的参数命名pid,因为正则捕获没有给子组起名,我们获得所有相关的市级信息有2种方法,第一种就是获得省级信息,然后使用关联的集合.areoinfo_set.all(),如图中被注释的部分,因为aParent属性代表父级id,我们还可以使用filter过滤器获得属性满足要求的,如上图,获得了查询集合,我们发送数据基本就和省份一样了
我们回到页面js部分,市级信息里,当然还是通过id获取到元素,这里需要注意的是,city需要清空,不然我们选择多次省份就会导致其下拉框是多个省的城市,使用.empty()获得清空后的对象,然后我们还要给清空的市添加一个默认的option标签,对于遍历部分,和之前一样。设置好后,城市即可搞定
当然县级流程也一样,绑定city下拉框的change事件,对其进行事件处理,这里由于市级和县级的视频函数其实是可以用一个函数实现,所以我们可以都给其绑定一个视图函数
这里需要注意的是,我们更换省的同时还要把县给清空了,我们回来改省的change时间,加上清空县的功能