此文章完成度【100%】留着以后忘记的回顾。多写多练多思考,我会努力写出有意思的demo,如果知识点有错误、误导,欢迎大家在评论处写下你的感想或者纠错。
【Django version】: 2.1
【pymysql version】:0.9.3
【python version】: 3.7
【Pillow version】:6.0.0
常用
到此为止,关于Django框架的三大块MVT已经告一段落,让我们扩充一些Django其他的功能,为了更好的完成开发,而努力吧
主要知识点如下:
-
静态文件处理
-
中间件
-
上传图片
-
admin站点
-
分页
-
示例:省市区选择、jquery、ajax
接下来才是每天都最重要的环节,重复重复不断重复的创建项目:
创建项目test5
django-admin startproject test5
进入到项目目录test5,创建应用school
cd test5 python manage.py startapp school
在test5下的settings中的INSTALLED_APPS中注册应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'school',
]
在test5下的settings中的DATABASES中指定数据库引擎,并配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'school',
'USER': 'root',
'PASSWORD': 'toor',
'HOST': 'localhost',
'PORT': 3306,
}
}
在test5下的settings中的TEMPLATS中添加模板路径
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
创建模板目录,并将所有school的html添加到school这个文件夹中

在test5下的urls.py,添加url配置指向到school应用下的urls.py中
from django.contrib import admin
from django.urls import path, include
app_url_patterns = ('school.urls', 'school')
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(app_url_patterns, namespace='school')),
]
在school应用下创建urls.py 并且添加index测试路径
from django.urls import re_path, include
from . import views
urlpatterns=[
re_path(r'^$', views.index, name='index'),
]
在school应用下的views.py中创建index视图
from django.shortcuts import render
def index(request):
return render(request, 'school/index.html')
在模板templates/school目录下创建index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>常用技术练习</title>
</head>
<body>
<h1>常用技术练习</h1>
</body>
</html>
在应用school下的模型models.py中定义AreaInfo
from django.db import models
class AreaInfo(models.Model):
"""地区模型类"""
name = models.CharField(max_length=50)
area_parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
静态文件
项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用,也需要指定静态文件的路径,django中提供了一个解析的方式配置静态文件路径。静态文件可以放在项目根目录下,也可以放在应用的目录下, 由于有些静态文件在项目中是通用,所以推荐放在项目的根目录下、方便使用。
创建静态文件存放目录 ,这里选择的是在项目的根目下创建,并且分别常见css、js、img目录

在应用下的urls.py中创建img_static的url规则
from django.urls import re_path, include
from . import views
urlpatterns=[
re_path(r'^$', views.index, name='index'),
re_path(r'^img_static$', views.img_static),
]
在视图views.py中定义img_static视图函数
from django.shortcuts import render
...
def img_static(request):
return render(request, 'school/img_static.html')
在templates/school下创建img_static.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<img src="{% static 'img/cat.jpg'%}">
<img src="static/img/cat.jpg">
</body>
</html>
当然这些还是不够的,最重要的一步就是,在test5/settings文件中配置路径
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
保存图片到static/img/目录下

访问127.0.0.1:8000/img_static,两个img标签的图片都显示在浏览器中,所以这两种写路径的方式都可以。

配置静态文件
Django提供了一种配置,可以在html页面中隐藏真是路径
<1> 在test5/settings.py中修改STATIC_URL项
# STATIC_URL = '/static/' STATIC_URL = '/abc/'
再次访问127.0.0.1:8000/img_static,可以看出动态获取的还是可以显示,但是静态写入的地址就不能生效了

为了安全考虑可以通过配置项隐藏真实图片路径
注意:这种方案可以隐藏真实的静态文件路径,但是结合Nginx布署时,会将所有的静态文件都交给Nginx处理,而不用转到Django部分,所以这项配置就无效了。
如果你想禁用一些非法操作的用户,那么你需要怎么呢?
当然就是获取用户的IP将它废掉,那么如何获取IP呢,我就不再罗嗦
# 获取浏览器端的IP地址 # 使用request对象的META属性 request.META['REMOTE_ADDR']
既然要封其IP,断其连接,那就是我们应用下的所有网页都不允许访问,我们这就可以写成一个函数装饰器
在视图中views.py 定义装饰器函数
from django.shortcuts import render
from django.http import HttpResponse
# 禁用的IP列表
EXCLUDE_IPS = ['192.168.3.5'] # 这个ip是我自己的,
def blocked_ips(view_func):
"""阻止访问IP的装饰器函数"""
def wrapper(request, *view_args, **view_kwargs):
user_ip = request.META['REMOTE_ADDR'] # 获取用户的ip
if user_ip in EXCLUDE_IPS:
# 直接返回一个response页面给他
return HttpResponse('<h1>禁用访问111</h1>')
return view_func(request, *view_args, **view_kwargs)
return wrapper
# 将所有禁止访问的页面都添加一个函数装饰器
@blocked_ips # 阻止IP访问的函数装饰器
def index(request):
user_ip = request.META['REMOTE_ADDR']
return render(request, 'school/index.html')
访问127.0.0.1:8000 因为禁止的是我自己ip,所以成功得到了禁止访问111

那么你会说这依然还是很麻烦。那么就让我们学习一下中间件,看看他当中是否有能解决,我们这个问题的能力
中间件
Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性,其他的MVC框架也有这个功能,名称为IoC。
>>> : 接下来是插播,理解不深也不用着急,下面会有更全面的解释
那么接下来说人话,就好比刚才用函数装饰器来阻断一个ip的访问,我们会在他访问每一个页面的时候返回一个response给他。所以他看到的是我们准备给他的一个针对他的页面。如果启用中间件的话,就比那个简单得多了,首先应用好比一个大楼,在发来请求的时候,在访问视图之前,在通过url匹配之后,我们准备函数阻断他的行为,这样就不需要在每一个函数上给装了一个防盗门(装饰器),就好比直接在大楼外安插了一个保安对每一个违规请求者, 在直接拒绝在大门外,之后的扩展的新视图函数也不需要依次添加装饰器。
<1> 将之前的装饰器注释掉,或者删除掉,因为你了解了这个方法,就不会在管那些装饰器啥啥的。
<2> 在school应用下创建一个中间件名字叫middleware.py的文件,并在里面创建一个BlankedIPMiddleware这个中间件类
< 2-1 > 简单的讲解一下,先从我们导入的这个类说起 【from django.utils.deprecation import MiddlewareMixin】,为啥是它,因为NB,为啥NB,Django提供了 django.utils.deprecation.MiddlewareMixin 来简化中间件类的创建,这些中间件类同时兼容 MIDDLEWARE 和 旧的MIDDLEWARE_CLASSES。所以我们这里就继承自MiddlewareMixin
在school目录下创建middleware.py文件
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
class BlockedIPMiddleware(MiddlewareMixin):
"""阻断ip访问的中间件类"""
EXCLUDE_IPS = ['192.168.3.5','127.0.0.1'] # 定义一个类属性,来记录所有阻止访问的IP
# 处理视图前,url匹配成功后,调用。
# 返回None就会继续接下来的操作,什么都没有发生一样或直接返回HttpResponse对象阻断那些不友好者
def process_view(self, request, view_func, *view_args, **view_kwargs):
user_ip = request.META['REMOTE_ADDR'] # 获取当前的浏览器提交的IP
if user_ip in BlockedIPMiddleware.EXCLUDE_IPS:
return HttpResponse('<h1>通过中间件禁止访问</h1>')
<3>在test5/settings中注册中间件
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',
'school.middleware.BlockedIPMiddleware', # 注册中间件
]
<4>访问12.0.0.1:8000

每个中间件组件都负责执行一些特定的功能,例如,Django中包含的一个中间件组件,AuthenticationMiddleware它将用户与使用的请求会话关联起来。接下来我将讲解一下中间件的工作原理,如何注册中间件,你如何创建自己的中间件,Django有一些内置的中间件,您可以立即使用它们,他们已经设置在settings中的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',
]
中间件将获取一个get_response 然后返回一个中间件,它接受请求并返回响应,就像视图一样。例如:
def simple_middleware(get_response):
# 只初始化一次
def middleware(request):
# 这里的代码会在每一个请求之前执行
# 调用视图(和之后的中间件)
response = get_response(request)
# 这里的代码会在请求/响应之后执行
# 视图已经被调用
return response
return middleware
或者他也可以写成一个类
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# 只初始化一次
def __call__(self, request):
# 这里的代码会在每一个请求之前执行
# 调用视图(和之后的中间件)
response = self.get_response(request)
# 这里的代码会在请求/响应之后执行
# 视图已经被调用
return response
初始化:必须只有get_response参数,你也可以使用中间件初始化一些全局状态。请注意这两点:
- Django的get_response只是来初始化你的中间件,不能在这里定义其他参数
- 当Web应用运行时,__init__() 只被调用一次
def __init__(self, get_response): self.get_response = get_response
在请求阶段,在调用视图之前,Django按中间件中定义的顺序自上向下定义中间件。您可以把它看作是一个洋葱:每个中间件类都是一个“层”,它们包裹在视图之外,视图位于洋葱的核心。如果请求通过洋葱的所有层(每个层都调用get_response将请求传递到下一层),一直传递到核心视图,那么在返回的过程中,响应将通过每一层(在有内向外)。
除了前面描述的基本请求/响应中间件模式之外,您还可以向基于类的中间件添加其他三种特殊方法:
<1> 处理视图前:在每一个请求上,视图函数调用之前,返回None找到指定的视图或HttpResponse对象直接返回:
def process_view(self, request, view_func, *view_args, **view_kwargs):
pass
它会返回None或者HttpResponse对象,如果返回None,Django将继续处理这个请求,执行其他中间件中的process_view(),然后执行匹配的视图。如果它返回HttpResponse对象,Django就不会调用视图,它将把响应中间件应用到HttpResponse并返回结果。
注意:请不要在中间件中访问request.POST
<2> 异常处理:当视图抛出异常时调用,在每一个请求上调用,返回一个HttpResponse对象返回给浏览器,否则,将启动缺省异常处理:
process_exception(self, request,exception)
<3>模版响应:在视图完成后执行,如果响应实例具有render()方法,表明它是一个模板响应或等效的响应。
process_template_response(request, response)
中间件常见的用途:缓存、会话认证、日志记录、异常
中间件执行流程
演示
在school下的middleware.py中创建中间件类:
class MyMiddleware:
def __init__(self, get_response):
print('-----init-----')
self.get_response = get_response
def __call__(self, request):
print("中间件开始")
response = self.get_response(request)
print("中间件结束")
return response
def process_view(self, request, view_func, *view_args, **view_kwargs):
print("请求实际函数前执行")
在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',
# 'school.middleware.BlockedIPMiddleware',
'school.middleware.MyMiddleware',
]
修改views.py中index视图:
from django.shortcuts import render
def index(request):
print("----index 视图执行-----")
return render(request, 'school/index.html')
运行服务器

访问127.0.0.1:8000,然后再访问一次

__init__()只会在web服务运行时加载一次
注意:如果多个注册的中间件类中都有process_exception的方法,则先注册的后执行。
Admin站点
内容发布的部分由网站的管理员负责增、删、改、查数据的,开发这些重复的功能是一件单调乏味、缺乏创造性的工作,为此,Django能够根据自定义的模块类自动地生成管理模块。
在第一部分对管理站点坐了简单的介绍,现在我们将零碎的知识扩充一下, 在Django项目中默认启用admin管理站点。
第一步:准备工作:创建管理员的用户名和密码。
python manage.py createsuperuser

第二步:使用在应用的admin.py中注册模型类
from django.contrib import admin from school.models import AreaInfo admin.site.register(AreaInfo) # 将区域模型类注册到Admin站点
第三步:访问127.0.0.1:8000/admin

列表页显示效果

更改管理页展示效果
ModelAdmin类可以控制模型admin界面中的展示方式,主要包括在列表页的展示方式、添加修改页面的展示方式
第一步:在应用下的admin.py中,注册魔心那个累前定义管理类AreaAdmin
from django.contrib import admin
from school.models import AreaInfo
class AreaAdmin(admin.ModelAdmin):
pass
admin.site.register(AreaInfo)
管理类有两种使用方式:注册参数、装饰器
注册参数:打开应用下的admin.py文件,注册模型类
admin.site.register(AreaInfo, AreaAdmin)
装饰器:打开应用下的admin.py文件,在管理类上注册模型类
from django.contrib import admin from school.models import AreaInfo @admin.register(AreaInfo) class AreaAdmin(admin.ModelAdmin): pass
更改列表页展示效果
列表页选项
页大小
默认每页显示100条数据,list_per_page属性
list_per_page = 100
打开应用下的admin.py文件,修改AreaAdmin类
from django.contrib import admin
from school.models import AreaInfo
@admin.register(AreaInfo)
class AreaAdmin(admin.ModelAdmin):
list_per_page = 20
访问127.0.0.1:8000/admin/school/areainfo/

“操作选项”的位置
顶部显示的属性,设置为True在顶不显示,设置为False不在顶部显示,默认为True
actions_on_top = True
效果区分

底部显示的属性,设置为True在底部显示,默认为False
actions_on_bottom = True # 设置选项在底部,默认值是False

列表中的列
属性
list_display = ['id', 'name', 'area_parent_id'] # 借用AreaInfo模型类的信息展示

点击列头可以进行排序的转换
将方法作为列
列可以是模型里的字段,也可以是模型里的方法,如果列表项显示的是方法,该方法必须要有返回值
修改一:在应用下的models.py文件中,修改AreaInfo类:
from django.db import models
class AreaInfo(models.Model):
"""地区模型类"""
name = models.CharField(max_length=50)
area_parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
def title(self):
return str(self.name) + ":" +str(self.id)
修改二:在应用下的admin.py文件中,将方法名添加到列表中
from django.contrib import admin
from school.models import AreaInfo
@admin.register(AreaInfo)
class AreaAdmin(admin.ModelAdmin):
list_per_page = 20 # 每一页展示多少数据
actions_on_top = True # 设置选项在顶部,默认值是True
actions_on_bottom = True # 设置选项在底部,默认值是False
list_display = ['id', 'name', 'area_parent_id', 'title'] # 添加了一个方法名(title)
访问127.0.0.1:8000/admin/school/areainfo/?o=1

方法列默认是不能排序的。如果需要排序,需要为方法指定排序依据
在应用下的models.py文件中修改AreaInfo类
title.admin_order_field = 'id' # 设置title这个方法的排序依据为id

列头上的1和2是排序的优先级
列表题:列标题默认为属性或方法的名称,可以通过属性设置,需要先将模型字段封装成方法,在对方法使用这个属性,模型字段不能直接使用这个属性:short_description
在应用下的models.py文件,修改AreaInfo类
from django.db import models
class AreaInfo(models.Model):
"""地区模型类"""
name = models.CharField(max_length=50)
area_parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
def title(self):
return str(self.name) + ":" +str(self.id)
title.admin_order_field = 'id'
title.short_description = '区域名称:id' # 更改列标题
访问127.0.0.1:8000/admin/school/areainfo/?o=1

关联对象
优化访问关联对象显示它的name属性
在应用下models.py文件中修改AreaInfo类:
from django.db import models
class AreaInfo(models.Model):
"""地区模型类"""
...
def parent(self):
if self.area_parent is None:
return ""
return self.area_parent.name
parent.short_description = "父级地区"
在应用下的admin.py中修改list_display
from django.contrib import admin
from school.models import AreaInfo
@admin.register(AreaInfo)
class AreaAdmin(admin.ModelAdmin):
list_per_page = 20 # 每一页展示多少数据
actions_on_top = True # 设置选项在顶部,默认值是True
actions_on_bottom = True # 设置选项在底部,默认值是False
# list_display = ['id', 'name', 'area_parent_id', 'title']
list_display = ['id', 'name', 'parent', 'title'] # 修改为parent
访问127.0.0.1:8000/admin/school/areainfo/?o=1

右侧栏过滤器
属性 list_filter
只能接受字段,会将对应字段的值列出来,用于快速郭列。一般用于重复值的字段。
在应用下的admin.py文件中修改AreaAdmin类
from django.contrib import admin
from school.models import AreaInfo
@admin.register(AreaInfo)
class AreaAdmin(admin.ModelAdmin):
...
list_filter = ['name']
访问127.0.0.1:8000/admin/school/areainfo/?o=1

搜索框
属性 search_fields
用于对指定字段的值进行搜索,支持模糊查询。类型为列表,表示这些字段上进行搜索。
在应用下的admin.py修改AreaAdmin类
from django.contrib import admin
from school.models import AreaInfo
@admin.register(AreaInfo)
class AreaAdmin(admin.ModelAdmin):
...
search_fields = ['id','name']
访问127.0.0.1:8000/admin/school/areainfo/?o=1


中文标题
在应用下的models.py文件中修改AreaInfo模型类,为字段指定verbose_name参数。第一个参数
from django.db import models
class AreaInfo(models.Model):
"""地区模型类"""
name = models.CharField('地区名称', max_length=50)
...
访问127.0.0.1:8000/admin/school/areainfo/?o=1

编辑页选项
显示字段顺序
属性fields,类型是个列表
在应用下的admin.py文件,修改AreaAdmin类
from django.contrib import admin
from school.models import AreaInfo
@admin.register(AreaInfo)
class AreaAdmin(admin.ModelAdmin):
...
fields = ['area_parent', 'name']
随便点击一行id,可以转到修改页面

Area parent点开下拉列表你会发现输出的都是AreaInfo object(id),这样会非常不好识别,选择父级非常的麻烦,这样我就需要想一个解决办法,把它格式化成字符串。
str方法考虑一下,在应用下打开models.py,修改AreaInfo类,添加str方法
from django.db import models
class AreaInfo(models.Model):
"""地区模型类"""
...
def __str__(self):
return self.name
分组显示
属性fieldset
fieldset=(
('组1标题',{'fields':('字段1','字段2')}),
('组2标题',{'fields':('字段3','字段4')}),
)
在应用下的admin.py文件修改AreaAdmin类:
from django.contrib import admin
from school.models import AreaInfo
@admin.register(AreaInfo)
class AreaAdmin(admin.ModelAdmin):
...
# fields = ['area_parent', 'name']
fieldset = (
('基本',{'fields': ['name']}),
('高级',{'fields': ['area_parent']}),
)
随便点击一个id

注意: fields与fieldsets两者需要选择一个使用
关联对象
在一对多的关系中,可以在一这边的编辑页面中编辑多这边的对象,嵌入多这边的对象的方式包括表格、块两种。类型是InlineModelAdmin:表示在模型的编辑页面嵌入关联模型的编辑,子类TabularInline:以表格的形式嵌入。子类StackedInline:以块的形式嵌入。
StackedInline:在应用下的admin.py文件中创建AreaStackedInline类,并且修改AreaAdmin类
from django.contrib import admin
from school.models import AreaInfo
class AreaStackedInline(admin.StackedInline):
model = AreaInfo # 关联子对象
extra = 2 # 额外编辑2个子对象
@admin.register(AreaInfo)
class AreaAdmin(admin.ModelAdmin):
list_per_page = 20 # 每一页展示多少数据
actions_on_top = True # 设置选项在顶部,默认值是True
actions_on_bottom = True # 设置选项在底部,默认值是False
# list_display = ['id', 'name', 'area_parent_id', 'title']
list_display = ['id', 'name', 'parent', 'title']
list_filter = ['name']
search_fields = ['id','name']
# fields = ['area_parent', 'name']
fieldsets = (
('基本',{'fields': ['name']}),
('高级',{'fields': ['area_parent']}),
)
inlines = [AreaStackedInline]
刷新浏览器,下面将显示出所有关联:北京市:110100 的区域,并且还有可以额外增加两条地区的地方

下面我们感受一下表格的形式嵌入式什么感觉,注释你刚才添加块的代码。
TabularInline:在应用下修改admin.py文件,并创建AreaTabularInline类:
from django.contrib import admin
from school.models import AreaInfo
# class AreaStackedInline(admin.StackedInline):
# model = AreaInfo # 关联子对象
# extra = 2 # 额外编辑2个子对象
class AreaTabularInline(admin.TabularInline):
model = AreaInfo # 关联子对象
extra = 2 # 额外编辑2个子对象
@admin.register(AreaInfo)
class AreaAdmin(admin.ModelAdmin):
list_per_page = 20 # 每一页展示多少数据
actions_on_top = True # 设置选项在顶部,默认值是True
actions_on_bottom = True # 设置选项在底部,默认值是False
# list_display = ['id', 'name', 'area_parent_id', 'title']
list_display = ['id', 'name', 'parent', 'title']
list_filter = ['name']
search_fields = ['id','name']
# fields = ['area_parent', 'name']
fieldsets = (
('基本',{'fields': ['name']}),
('高级',{'fields': ['area_parent']}),
)
# inlines = [AreaStackedInline]
inlines = [AreaTabularInline]
刷新页面,点击id进入修改模式,多观察一些数据

重写模板
第一步:在templates目录下创建admin目录

第二步:找到Django的项目下的admin文件夹,大家的路径应该都不一样。大概流程是你安装的Python路径下的Lib中找到一个叫site-packages的文件夹下面会有你安装的Django在找到contrib下就会有admin
C:\Users\Circle\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\django\contrib\admin
第三步:将需要更改文件拷贝到自己项目下的templates里的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' %}"><h1>自定义的管理页模板</h1>{{ site_header|default:_('Django administration') }}</a></h1>
{% endblock %}
{% block nav-global %}{% endblock %}
刷新网页

多熟悉一下你想更改的模板的源码。你就可以创建一个属于你的模板,想更改什么就去寻找什么
上传图片
在Python中进行图片操作,需要安装PIL,可以使用pip安装
在Django中上传图片包括两种方式:
-
管理页面admin中上传
-
自定义form表单中上传图片
上传图片后,将图片存储在服务器上,然后将图片的路径存储在表中。
创建包含图片的模型类
将模板类的属性定义成models.ImageField类型。
在应用下的models.py文件中定义PicTest模型类
from django.db import models
class PicTest(models.Model):
pic = models.ImageField(upload_to='school')
生成迁移文件,执行迁移
python manage.py makemigrations python manage.py migrate
打开项目下的settings.py文件设置图片保存到static目录下
MEDIA_ROOT=os.path.join(BASE_DIR,"static/media")
在static目录下创建media目录,在创建应用名的目录

在管理页面admin中上传图片
在应用下的admin.py文件,注册PicTest
admin.site.register(PicTest)
刷新页面,点击PicTest的增加,选择文件,然后找一张图片保存


查看数据库是否添加成功

数据保存的位置:static\media\school\

自定义form表单中上传图片
在应用下的views.py文件创建视图pic_upload
from django.shortcuts import render
...
def pic_upload(request):
return render(request, 'school/pic_upload.html')
在应用下的urls.py文件中配置url
re_path(r'^pic_upload$', views.pic_upload),
在templates下的school目录中创建pic_upload.html
在模板中定义上传表单要求:
- form的属性enctype="multipart/form-data"
- form的method为post
- input类型为file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传图片</title>
</head>
<body>
<form method="POST" action="/pic_handle" enctype="multipart/form-data">
{%csrf_token%}
<input type="file" name="pic"><br>
<input type="submit" value="上传">
</form>
</body>
</html>
在应用views.py文件中创建视图pic_handle,用于接收表单保存图片,request对象的FILES属性用于接收请求的文件、图片
from django.http import HttpResponse
from django.conf import settings
def pic_handle(request):
p = request.FILES.get('pic')
p_name = '%s/school/%s' % (settings.MEDIA_ROOT, p.name)
with open(p_name, 'wb') as pic:
for c in p.chunks():
pic.write(c)
return HttpResponse('OK')
在应用下的urls.py 中配置url
re_path(r'^pic_handle$', views.pic_handle),
访问127.0.0.1:8000/pic_upload

会获得pic_handle返回的ok,查看static/media/school下是否的到文件


修改视图pic_handle,添加写入数据库的功能
from django.http import HttpResponse
from django.conf import settings
from school.models import PicTest
def pic_handle(request):
p = request.FILES.get('pic')
p_name = '%s/school/%s' % (settings.MEDIA_ROOT, p.name)
with open(p_name, 'wb') as pic:
for c in p.chunks():
pic.write(c)
pic_db = PicTest() # 创建PicTest模型类对象
pic_db.pic = 'school/' + p.name
pic_db.save()
return HttpResponse('OK')
查看数据库中是否写入

显示图片
在应用的views.py文件中创建pic_show视图
from django.shortcuts import render
from school.models import PicTest
def pic_show(request):
pt = PicTest.objects.all()
context = {'pt': pt}
return render(request, 'school/pic_show.html', context)
在应用下的urls.py配置url
re_path(r'^pic_show$', views.pic_show),
在templates在school中创建pic_show.html文件
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>显示图片</title>
</head>
<body>
{%for pic in pt%}
<img src="{%static '' %}media/{{pic.pic}}">
{%endfor%}
</body>
</html>
访问127.0.0.1:8000/pic_show

分页
Django提供了数据分页的类,这些类被定义在django/core/paginator.py中,类Paginator用于对列进行一页n条数据的分页运算,类Page用于表示第m页的数据
Paginator类实例对象
-
方法_init_(列表,int):返回分页对象,第一个参数为列表数据,第二个参数为每页数据的条数。
-
属性count:返回对象总数。
-
属性num_pages:返回页面总数。
-
属性page_range:返回页码列表,从1开始,例如[1, 2, 3, 4]。
-
方法page(m):返回Page类实例对象,表示第m页的数据,下标以1开始。
Page类实例对象
-
调用Paginator对象的page()方法返回Page对象,不需要手动构造。
-
属性object_list:返回当前页对象的列表。
-
属性number:返回当前是第几页,从1开始。
-
属性paginator:当前页对应的Paginator对象。
-
方法has_next():如果有下一页返回True。
-
方法next_page_number(): 返回下一页的页码
-
方法has_previous():如果有上一页返回True。
-
方法previous_page_number():返回前一页的页码
-
方法len():返回当前页面对象的个数。
演示:
在应用下的views.py文件中创建视图page_test
from django.shortcuts import render
from school.models import AreaInfo
from django.core.paginator import Paginator
def page_test(request, pIndex):
# 查询所有的省
area_list = AreaInfo.objects.filter(area_parent__isnull = True)
# 将地区信息按一页10条显示
paginator = Paginator(area_list, 10)
# 默认获取第一页
if pIndex == '':
pIndex = '1'
# 将字符串转换成int类型
pIndex = int(pIndex)
# 获取第pIndex页的数据
page = paginator.page(pIndex)
return render(request, 'school/page_test.html', {'page': page})
在应用下的urls.py文件中配置url
re_path(r'^page(?P<pIndex>\d*)$', views.page_test),
在templates文件夹下的school中创建page_test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分页展示</title>
</head>
<body>
显示当前页的地区信息:<br>
<ul>
{%for area in page%}
<li>{{ area.id }} -- {{ area.name }}</li>
{%endfor%}
</ul>
<hr>
{%if page.has_previous%}
<a href='/page{{ page.previous_page_number }}'>上一页</a>
{%else%}
上一页
{%endif%}
{%for pindex in page.paginator.page_range%}
{%if page.number == pindex%}
{{pindex}}
{%else%}
<a href='/page{{ pindex }}'>{{pindex}}</a>
{%endif%}
{%endfor%}
{%if page.has_next%}
<a href='/page{{ page.next_page_number }}'>下一页</a>
{%else%}
下一页
{%endif%}
</html>
访问127.0.0.1:8000/page

既然省地区信息的已经可以在页面显示,那么我们做一个关于地区的三级联动案例,这个案例需要在Django中使用到jquery的ajax进行数据交互,jquery框架提供了三种方法用于异步交互,$.ajax、$.get、$.post,由于CSRF约束,我们这里也只是查询信息,所以使用$.get演示:先观看一下最终实现效果:

第一步:将jquery文件拷贝到static/js目录下

第二步:在应用下的views.py文件中,添加areas视图
def areas(request): return render(request, 'school/areas.html')
第三步:在应用下的urls.py中配置url
re_path(r'^areas$', views.areas),
第四步:在templates中的school下创建areas.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>地区三级联动</title>
{%load static%}
<script type="text/javascript" src='{% static "js/jquery-1.12.4.min.js"%}'></script>
<script type="text/javascript">
$(function(){
// ajax post请求方式的简写 第一个参数是访问的url,第二个参数是传入的数据, 第三个参数回调函数
// $.post('/prov', {'val':'value'}, function(data){
// })
//ajax get请求的简写,第一个参数是请求的url,第二个参数是回调函数
$.get('/prov', function(data){
// 回调函数
// 获取省列表
pro_data = data.area_data
pro = $('#pro')
// 遍历省的列表
for (var i = 0; i < pro_data.length; i++) {
//将id和name分别获取
id = pro_data[i][0]
name = pro_data[i][1]
// 格式化option标签
option_str = '<option value=' + id + '>' + name + '</option>'
// 将每一个标签添加到页面
pro.append(option_str)
}
})
//绑定pro下拉列表框的change事件,获得省下面的市的信息
$('#pro').change(function(){
//发起一个ajax请求,/city 获取省下面的市
//获取点击省的id
pro_id = $(this).val()
$.get('/prov'+pro_id, function(data){
//回调函数
city_data = data.area_data
// append默认是追加模式,如果不清空,每次求情都会在最后面追加
city = $('#city')
city.empty().append("<option>----请选择市区----</option>")
dis = $('#dis')
dis.empty().append("<option>----请选择市区----</option>")
// 遍历的第二种方式
$.each(city_data, function(index, item){
id = item[0]
name = item[1]
// console.log(id) //测试
// console.log(name) //测试
option_city = '<option value=' + id + '>' + name + '</option>'
city.append(option_city)
})
})
})
//绑定pro下拉列表框的change事件,获得省下面的市的信息
$('#city').change(function(){
//发起一个ajax请求,/city 获取省下面的市
//获取点击省的id
city_id = $(this).val()
$.get('/prov'+city_id, function(data){
//回调函数
dis_data = data.area_data
dis = $('#dis')
dis.empty().append("<option>----请选择市区----</option>")
$.each(dis_data, function(index, item){
id = item[0]
name = item[1]
// console.log(id)
// console.log(name)
option_city = '<option value=' + id + '>' + name + '</option>'
dis.append(option_city)
})
})
})
})
</script>
</head>
<body>
<h1 style='text-align: center'>省市区列表</h1>
<div>
<select id="pro">
<option >----请选择省----</option>
</select>
<select id='city'>
<option>----请选择市区----</option>
</select>
<select id='dis'>
<option>----请选择区县----</option>
</select>
</div>
</body>
</html>
第五步:在应用下views.py添加prov视图
from django.http import JsonResponse
def prov(request, parent_id):
# 如果为空说明请求的是省的下拉列表
if parent_id == '':
areas = AreaInfo.objects.filter(area_parent__isnull = True)
else:
# 否则就是请求parent_id所对应的下拉列表
areas = AreaInfo.objects.filter(area_parent = parent_id)
# 用于添加json数据
area_list = []
# 遍历获得的区域列表,添加到json数据中
for area in areas:
area_list.append((area.id,area.name))
# 返回json数据给页面
return JsonResponse({'area_data':area_list})
第六步:在应用下的urls.py文件中配置url
re_path(r'^prov(?P<parent_id>\d*)$', views.prov),
第七步:访问127.0.0.1:8000/areas

-The End-
来源:https://www.cnblogs.com/Hannibal-2018/p/11105251.html
