Django—常用功能
索引
一、静态文件
项目中的css、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用时,也需要指定静态文件的路径,django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。
示例
1)在项目的settings.py文件中定义静态文件存放的物理目录。
static_url = '/static/' staticfiles_dirs = [ os.path.join(base_dir, 'static'), ]
2)在项目根目录下创建static目录,再创建img、css、js目录。
3)在booktest/views.py中定义视图static_test。
def static_test(request): return render(request,'booktest/static_test.html')
4)在booktest/urls.py中配置url。
url(r'^static_test$',views.static_test),
5)在templates/booktest/下创建static_test.html文件。
<html> <head> <title>静态文件</title> </head> <body> <img src="/static/img/test.png"/> </body> </html>
6)保存图片到static/img/目录下,名称为test.png。
7)运行服务器,就能在浏览器看到图片了。
配置静态文件
django提供了一种配置,可以在html页面中可以隐藏真实路径。
1)在项目的settings.py文件中修改static_url项。
# static_url = '/static/' static_url = '/abc/'
2)刷新浏览器,图片找不到了
3)修改templates/booktest/static_test.html如下:
<html> <head> <title>静态文件</title> </head> <body> 修改前:<img src="/static/img/test.png"/> <hr> 修改后:<img src="/abc/img/test.png"/> </body> </html>
3)刷新浏览器,发现又能显示;
为了安全可以通过配置项隐藏真实图片路径,在模板中写成固定路径,后期维护太麻烦,可以使用static标签,根据配置项生成静态文件路径。
1)修改templates/booktest/static_test.html如下:
<html> <head> <title>静态文件</title> </head> <body> 修改前:<img src="/static/img/test.png"/> <hr> 修改后:<img src="/abc/img/test.png"/> <hr> 动态配置: {%load static from staticfiles%} <img src="{%static "img/test.png" %}"/> </body> </html>
2)刷新浏览器,动态配置的能显示。
二、中间件
中间件是一个用来处理django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变django的输入和输出。每个中间件组件都负责做一些特定的功能。
但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。
说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,django框架会在请求的特定的时间去执行这些方法。
自定义中间件
中间件可以定义五个方法,分别是:(主要的是process_request和process_response)
- process_request(self,request)
- process_view(self, request, view_func, view_args, view_kwargs)
- process_template_response(self,request,response)
- process_exception(self, request, exception)
- process_response(self, request, response)
以上方法的返回值可以是none或一个httpresponse对象,如果是none,则继续按照django定义的规则向后继续执行,如果是httpresponse对象,则直接将该对象返回给用户。
自定义一个中间件示例
from django.utils.deprecation import middlewaremixin class md1(middlewaremixin): def process_request(self, request): print("md1里面的 process_request") def process_response(self, request, response): print("md1里面的 process_response") return response
process_request
process_request有一个参数,就是request,这个request和视图函数中的request是一样的。
它的返回值可以是none也可以是httpresponse对象。返回值是none的话,按正常流程继续走,交给下一个中间件处理,如果是httpresponse对象,django将不执行视图函数,而将相应对象返回给浏览器。
我们来看看多个中间件时,django是如何执行其中的process_request方法的。
from django.utils.deprecation import middlewaremixin class md1(middlewaremixin): def process_request(self, request): print("md1里面的 process_request") class md2(middlewaremixin): def process_request(self, request): print("md2里面的 process_request") pass
在settings.py的middleware配置项中注册上述两个自定义中间件:
middleware = [ 'django.middleware.security.securitymiddleware', 'django.contrib.sessions.middleware.sessionmiddleware', 'django.middleware.common.commonmiddleware', 'django.middleware.csrf.csrfviewmiddleware', 'django.contrib.auth.middleware.authenticationmiddleware', 'django.contrib.messages.middleware.messagemiddleware', 'django.middleware.clickjacking.xframeoptionsmiddleware', 'middlewares.md1', # 自定义中间件md1 'middlewares.md2' # 自定义中间件md2 ]
此时,我们访问一个视图,会发现终端中打印如下内容:
md1里面的 process_request md2里面的 process_request ======index======
把md1和md2的位置调换一下,再访问一个视图,会发现终端中打印的内容如下:
md2里面的 process_request md1里面的 process_request ======index======
看结果我们知道:视图函数还是最后执行的,md2比md1先执行自己的process_request方法。
在打印一下两个自定义中间件中process_request方法中的request参数,会发现它们是同一个对象。
由此总结一下:
- 中间件的process_request方法是在执行视图函数之前执行的。
- 当配置多个中间件时,会按照middleware中的注册顺序,也就是列表的索引值,从前到后依次执行的。
- 不同中间件之间传递的request都是同一个对象
多个中间件中的process_response方法是按照middleware中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。
process_response
它有两个参数,一个是request,一个是response,request就是上述例子中一样的对象,response是视图函数返回的httpresponse对象。该方法的返回值也必须是httpresponse对象。
给上述的m1和m2加上process_response方法:
from django.utils.deprecation import middlewaremixin class md1(middlewaremixin): def process_request(self, request): print("md1里面的 process_request") def process_response(self, request, response): print("md1里面的 process_response") return response class md2(middlewaremixin): def process_request(self, request): print("md2里面的 process_request") pass def process_response(self, request, response): print("md2里面的 process_response") return response
访问一个视图,看一下终端的输出:
md2里面的 process_request md1里面的 process_request ======index====== md1里面的 process_response md2里面的 process_response
看结果可知:
process_response方法是在视图函数之后执行的,并且顺序是md1比md2先执行。(此时settings.py中 md2比md1先注册)
多个中间件中的process_response方法是按照middleware中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。
process_view
process_view(self, request, view_func, view_args, view_kwargs)
该方法有四个参数
request是httprequest对象。
view_func是django即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称作为字符串。)
view_args是将传递给视图的位置参数的列表.
view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。
django会在调用视图函数之前调用process_view方法。
它应该返回none或一个httpresponse对象。 如果返回none,django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个httpresponse对象,django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该httpresponse并返回结果。
给md1和md2添加process_view方法:
from django.utils.deprecation import middlewaremixin class md1(middlewaremixin): def process_request(self, request): print("md1里面的 process_request") def process_response(self, request, response): print("md1里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("md1 中的process_view") print(view_func, view_func.__name__) class md2(middlewaremixin): def process_request(self, request): print("md2里面的 process_request") pass def process_response(self, request, response): print("md2里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("md2 中的process_view") print(view_func, view_func.__name__)
访问index视图函数,看一下输出结果:
md2里面的 process_request md1里面的 process_request -------------------------------------------------------------------------------- md2 中的process_view <function index at 0x000001b973777bf8> index -------------------------------------------------------------------------------- md1 中的process_view <function index at 0x000001b973777bf8> index ======index====== md1里面的 process_response md2里面的 process_response
process_view方法是在process_request之后,视图函数之前执行的,执行顺序按照middleware中的注册顺序从前到后顺序执行的
process_exception
process_exception(self, request, exception)
该方法两个参数:
一个httprequest对象
一个exception是视图函数异常产生的exception对象。
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个none也可以是一个httpresponse对象。如果是httpresponse对象,django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个none,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
给md1和md2添加上这个方法:
from django.utils.deprecation import middlewaremixin class md1(middlewaremixin): def process_request(self, request): print("md1里面的 process_request") def process_response(self, request, response): print("md1里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("md1 中的process_view") print(view_func, view_func.__name__) def process_exception(self, request, exception): print(exception) print("md1 中的process_exception") class md2(middlewaremixin): def process_request(self, request): print("md2里面的 process_request") pass def process_response(self, request, response): print("md2里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("md2 中的process_view") print(view_func, view_func.__name__) def process_exception(self, request, exception): print(exception) print("md2 中的process_exception")
如果视图函数中无异常,process_exception方法不执行。
想办法,在视图函数中抛出一个异常:
def index(request): print('======index======') raise valueerror("主动抛出异常") return render(request, 'booktest/index.html')
在md1的process_exception中返回一个响应对象:
class md1(middlewaremixin): def process_request(self, request): print("md1里面的 process_request") def process_response(self, request, response): print("md1里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("md1 中的process_view") print(view_func, view_func.__name__) def process_exception(self, request, exception): print(exception) print("md1 中的process_exception") return httpresponse(str(exception)) # 返回一个响应对象
看输出结果:
md2里面的 process_request md1里面的 process_request -------------------------------------------------------------------------------- md2 中的process_view <function index at 0x0000019288fb7bf8> index -------------------------------------------------------------------------------- md1 中的process_view <function index at 0x0000019288fb7bf8> index ======index====== 主动抛出异常 md1 中的process_exception md1里面的 process_response md2里面的 process_response
注意,这里并没有执行md2的process_exception方法,因为md1中的process_exception方法直接返回了一个响应对象。
process_template_response(用的比较少)
process_template_response(self, request, response)
它的参数,一个httprequest对象,response是templateresponse对象(由视图函数或者中间件产生)。
process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个templateresponse对象或等价方法)。
class md1(middlewaremixin): def process_request(self, request): print("md1里面的 process_request") def process_response(self, request, response): print("md1里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("md1 中的process_view") print(view_func, view_func.__name__) def process_exception(self, request, exception): print(exception) print("md1 中的process_exception") return httpresponse(str(exception)) def process_template_response(self, request, response): print("md1 中的process_template_response") return response class md2(middlewaremixin): def process_request(self, request): print("md2里面的 process_request") pass def process_response(self, request, response): print("md2里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("md2 中的process_view") print(view_func, view_func.__name__) def process_exception(self, request, exception): print(exception) print("md2 中的process_exception") def process_template_response(self, request, response): print("md2 中的process_template_response") return response
view.py中:
def index(request): print('======index======') def render(): print("in index/render") return httpresponse("ok") rep = httpresponse("ok") rep.render = render return rep
访问index视图,终端输出的结果:
md2里面的 process_request md1里面的 process_request -------------------------------------------------------------------------------- md2 中的process_view <function index at 0x0000020e931a8bf8> index -------------------------------------------------------------------------------- md1 中的process_view <function index at 0x0000020e931a8bf8> index ======index====== md1 中的process_template_response md2 中的process_template_response in index/render md1里面的 process_response md2里面的 process_response
从结果看出:
视图函数执行完之后,立即执行了中间件的process_template_response方法,顺序是倒序,先执行md1的,在执行md2的,接着执行了视图函数返回的httpresponse对象的render方法,返回了一个新的httpresponse对象,接着执行中间件的process_response方法。
中间件的执行流程
原文引用:
三、admin站点
内容发布的部分由网站的管理员负责查看、添加、修改、删除数据,开发这些重复的功能是一件单调乏味、缺乏创造力的工作,为此,django能够根据定义的模型类自动地生成管理模块。
在第一部分对管理站点做了简单介绍,现在做详细讲解。在django项目中默认启用admin管理站点。
1)准备工作:创建管理员的用户名和密码。
python manage.py createsuperuser
按提示填写用户名、邮箱、密码。
2)使用:在应用的admin.py中注册模型类
例:打开booktest/admin.py文件,注册地区模型。
from django.contrib import admin from booktest.models import * admin.site.register(areainfo)
3)登录后台,登录成功后,可以看到areainfos,可以进行增加、修改、删除、查询的管理。
列表页显示效果如下图:
控制管理页展示
类modeladmin可以控制模型在admin界面中的展示方式,主要包括在列表页的展示方式、添加修改页的展示方式。
1)在booktest/admin.py中,注册模型类前定义管理类areaadmin。
class areaadmin(admin.modeladmin): pass
管理类有两种使用方式:
- 注册参数
- 装饰器
注册参数:打开booktest/admin.py文件,注册模型类代码如下:
admin.site.register(areainfo,areaadmin)
装饰器:打开booktest/admin.py文件,在管理类上注册模型类,代码如下:
@admin.register(areainfo) class areaadmin(admin.modeladmin): pass
接下来介绍如何控制列表页、增加修改页展示效果。
3.1 列表页选项
页大小
每页中显示多少条数据,默认为每页显示100条数据,属性如下:
list_per_page=100
1)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): list_per_page = 10
2)在浏览器中查看区域信息的列表页面,效果如下图:
"操作选项"的位置
顶部显示的属性,设置为true在顶部显示,设置为false不在顶部显示,默认为true。
actions_on_top=true
底部显示的属性,设置为true在底部显示,设置为false不在底部显示,默认为false。
actions_on_bottom=false
1)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): ... actions_on_top = true actions_on_bottom = true
2)在浏览器中刷新效果如下图:
列表中的列
属性如下:
list_display=[模型字段1,模型字段2,...]
1)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): ... list_display = ['id','atitle']
2)在浏览器中刷新效果如下图:
点击列头可以进行升序或降序排列。
将方法作为列
列可以是模型字段,还可以是模型方法,要求方法有返回值。
1)打开booktest/models.py文件,修改areainfo类如下:
class areainfo(models.model): ... def title(self): return self.atitle
2)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): ... list_display = ['id','atitle','title']
3)在浏览器中刷新效果如下图:
方法列是不能排序的,如果需要排序需要为方法指定排序依据。
admin_order_field=模型类字段
1)打开booktest/models.py文件,修改areainfo类如下:
class areainfo(models.model): ... def title(self): return self.atitle title.admin_order_field='atitle'
2)在浏览器中刷新效果如下图:
列标题
列标题默认为属性或方法的名称,可以通过属性设置。需要先将模型字段封装成方法,再对方法使用这个属性,模型字段不能直接使用这个属性。
short_description='列标题'
1)打开booktest/models.py文件,修改areainfo类如下:
class areainfo(models.model): ... title.short_description='区域名称'
2)在浏览器中刷新效果如下图:
关联对象
无法直接访问关联对象的属性或方法,可以在模型类中封装方法,访问关联对象的成员。
1)打开booktest/models.py文件,修改areainfo类如下:
class areainfo(models.model): ... def parent(self): if self.aparent is none: return '' return self.aparent.atitle parent.short_description='父级区域名称'
2)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): ... list_display = ['id','atitle','title','parent']
3)在浏览器中刷新效果如下图:
右侧栏过滤器
属性如下,只能接收字段,会将对应字段的值列出来,用于快速过滤。一般用于有重复值的字段。
list_filter=[]
1)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): ... list_filter=['atitle']
2)在浏览器中刷新效果如下图:
搜索框
属性如下,用于对指定字段的值进行搜索,支持模糊查询。列表类型,表示在这些字段上进行搜索。
search_fields=[]
1)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): ... search_fields=['atitle']
2)在浏览器中刷新效果如下图:
中文标题
1)打开booktest/models.py文件,修改模型类,为属性指定verbose_name参数
class area(models.model): atitle = models.charfield(max_length=24,verbose_name="标题") aparent = models.foreignkey(to='self', null=true)
2)在浏览器中刷新效果如下图:
3.2 编辑页选项
显示字段顺序
属性如下:
fields=[]
1)点击某行id的链接,可以转到修改页面,默认效果如下图:
2)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): ... fields=['aparent','atitle']
3)刷新浏览器效果如下图:
在下拉列表中输出的是对象的名称,可以在模型类中定义str方法用于对象转换字符串。
1)打开booktest/models.py文件,修改areainfo类,添加str方法。
class areainfo(models.model): ... def __str__(self): return self.atitle
2)刷新浏览器效果如下图:
分组显示
属性如下:
fieldset=( ('组1标题',{'fields':('字段1','字段2')}), ('组2标题',{'fields':('字段3','字段4')}), )
1)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): ... # fields=['aparent','atitle'] fieldsets = ( ('基本', {'fields': ['atitle']}), ('高级', {'fields': ['aparent']}) )
2)刷新浏览器效果如下图:
说明:fields与fieldsets两者选一使用。
关联对象
在一对多的关系中,可以在一端的编辑页面中编辑多端的对象,嵌入多端对象的方式包括表格、块两种。 类型inlinemodeladmin:表示在模型的编辑页面嵌入关联模型的编辑。子类tabularinline:以表格的形式嵌入。子类stackedinline:以块的形式嵌入。
1)打开booktest/admin.py文件,创建areastackedinline类。
class areastackedinline(admin.stackedinline): model = area # 关联子对象 extra = 2 # 额外编辑2个子对象
2)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): ... inlines = [areastackedinline]
3)刷新浏览器效果如下图:
可以用表格的形式嵌入。
1)打开booktest/admin.py文件,创建areatabularinline类。
class areatabularinline(admin.tabularinline): model = area#关联子对象 extra = 2#额外编辑2个子对象
2)打开booktest/admin.py文件,修改areaadmin类如下:
class areaadmin(admin.modeladmin): ... inlines = [areatabularinline]
3)刷新浏览器效果如下图:
3.3 重写模板
1)在templates/目录下创建admin目录
2)打开当前环境中django的目录,再向下找到admin的模板
3)在admin文件夹中创建base_site.html
编辑base_site.html文件:
{% extends "admin/base.html" %} {% block title %}{{ title }} | {{ site_title|default:_('django site admin') }}{% endblock %} {% block branding %} <h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('django administration') }}</a></h1> <hr> <h1>自定义的管理页模板</h1> <hr> {% endblock %} {% block nav-global %}{% endblock %}
4)在浏览器中转到列表页面,刷新后如下图:
其它后台的模板可以按照相同的方式进行修改。
四、上传图片
在django中上传图片包括两种方式:
- 在管理页面admin中上传图片
- 自定义form表单中上传图片
上传图片后,将图片存储在服务器上,然后将图片的路径存储在表中。
创建包含图片的模型类
将模型类的属性定义成models.imagefield类型。
1)打开booktest/models.py文件,定义模型类pictest。
class pictest(models.model): pic = models.imagefield(upload_to='booktest/')
2)生成迁移,执行迁移。
python manage.py makemigrations python manage.py migrate
3)打开项目的settings.py文件,设置图片保存路径。
因为图片也属于静态文件,所以保存到static目录下。
media_root=os.path.join(base_dir,"static/media")
4)在static目录下创建media目录,再创建应用名称的目录,此例为booktest。
4.1 在管理页面admin中上传图片
1)打开booktest/admin.py文件,注册pictest。
from django.contrib import admin from booktest.models import * admin.site.register(pictest)
2)运行服务器,进入后台管理,点击add按钮添加数据
3)选择图片,点击“save”按钮完成图片上传。
4)回到数据库命令行,查询表pictest中的数据如下图:
5)图片被保存到目录static/media/booktest/下,如下图:
4.2 自定义form表单中上传图片
1)打开booktest/views.py文件,创建视图pic_upload。
def pic_upload(request): return render(request,'booktest/pic_upload.html')
2)打开booktest/urls.py文件,配置url。
url(r'^pic_upload/$', views.pic_upload),
3)在templates/booktest/目录下创建模板pic_upload.html。
在模板中定义上传表单,要求如下:
- form的属性enctype="multipart/form-data"
- form的method为post
- input的类型为file
<form method="post" action="/pic_handle/" enctype="multipart/form-data"> {%csrf_token%} <input type="file" name="pic"/><br> <input type="submit" value="上传"> </form>
4)打开booktest/views.py文件,创建视图pic_handle,用于接收表单保存图片。
request对象的files属性用于接收请求的文件,包括图片。
from django.conf import settings from django.http import httpresponse ... def file_handler(request): # 1.获取文件对象 photo = request.files["photo"] # 2.创建一个文件 file_path = "%s/booktest/%s"%(settings.media_root, photo.name) with open(file_path, 'wb') as f: # 3.将图片保存到文件中 for content in photo.chunks(): f.write(content) # 4.将文件同步到数据库 picture.objects.create(photo="booktest/%s"%photo.name) return httpresponse("ok")
5)打开booktest/urls.py文件,配置url。
url(r'^pic_handle/$', views.pic_handle),
6)运行服务器,在浏览器中上传图片。
7)图片上传目录如下图:
4.3显示图片
1)打开booktest/views.py文件,创建视图pic_show。
from booktest.models import pictest ... def pic_show(request): pic=pictest.objects.get(pk=1) context={'pic':pic} r
相关文章:
-
-
一、动态语⾔的定义 动态语言是在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。现在比较热门的动态语言有... [阅读全文]
-
s21day15 python笔记 一、内容回顾及补充 1. 回顾 2. 补充 range / xrange(python2与python3的区别六... [阅读全文]
-
索引 一、静态文件 二、中间件 三、Admin站点 3.1 列表页选项 3.2 编辑页选项 3.3 重写模板 四、上传图片 4.1 在管理页面adm... [阅读全文]
-
1.游戏思路和流程图 实现功能:玩家猜测三位不一样的数字,猜错了有提示,提示分别为(位置错误数字正确),(位置和数字正确),(数字和位置都不正确) ... [阅读全文]
-
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
下一篇: Python模块搜索路径
发表评论