Django后端笔记
其他技术
静态文件
使用
在 网页使用的css文件,js文件和图片叫做静态文件。
- 新建静态文件夹 static。
- 配置静态文件所在的物理目录。Settings.py
STATIC_URL设置访问静态文件对应的url。
STATICFILES_DIRS设置静态文件所在的物理目录。
动态生成静态文件的路径。
加载目录
STATICFILES_FINDERS=(‘django.contrib.staticfiles.finders.FileSystemFinder’, ‘django.contrib.staticfiles.finders.AppDirectoriesFinder’)
中间件
中间件函数是django框架给我们预留的函数接口,让我们可以干预请求和应答的过程。
获取浏览器端的ip地址
使用request对象的META属性:request.META[‘REMOTE_ADDR’]
使用中间件
-
新建middleware.py文件。
-
定义中间件类。
在类中定义中间件预留函数。
init:服务器响应第一个请求的时候调用。
process_request:是在产生request对象,进行url匹配之前调用。
process_view:是url匹配之后,调用视图函数之前。
process_response:视图函数调用之后,内容返回给浏览器之前。
process_exception:视图函数出现异常,会调用这个函数。
middleware.py
from django.http import HttpResponse
class BlockedIPSMiddleware(object):
'''中间件类'''
EXCLUDE_IPS = ['172.16.179.152']
def process_view(self, request, view_func, *view_args, **view_kwargs):
'''视图函数调用之前会调用'''
user_ip = request.META['REMOTE_ADDR']
if user_ip in BlockedIPSMiddleware.EXCLUDE_IPS:
return HttpResponse('<h1>Forbidden</h1>')
class TestMiddleware(object):
'''中间件类'''
def __init__(self):
'''服务器重启之后,接收第一个请求时调用'''
print('----init----')
def process_request(self, request):
'''产生request对象之后,url匹配之前调用'''
print('----process_request----')
# return HttpResponse('process_request')
def process_view(self, request, view_func, *view_args, **view_kwargs):
'''url匹配之后,视图函数调用之前调用'''
print('----process_view----')
return HttpResponse('process_view')
def process_response(self, request, response):
'''视图函数调用之后,内容返回浏览器之前'''
print('----process_response----')
return response
class ExceptionTest1Middleware(object):
def process_exception(self, request, exception):
'''视图函数发生异常时调用'''
print('----process_exception1----')
print(exception)
class ExceptionTest2Middleware(object):
def process_exception(self, request, exception):
'''视图函数发生异常时调用'''
print('----process_exception2----')
如果注册的多个中间件类中包含process_exception函数的时候,调用的顺序跟注册的顺序是相反的。
3)在setting.py 注册中间件类。
Admin后台管理 (了解)
使用
-
本地化。语言和时区本地化。
-
创建超级管理员。
python mange.py createsuperuser
-
注册模型类。
-
自定义管理页面。
自定义模型管理类。
注册模型类的时候给register函数添加第二个参数,就是自定义模型管理类的名字。
3.2 模型管理类相关属性
- 列表页相关的选项。
admin.py
from django.contrib import admin
from booktest.models import AreaInfo,PicTest
# Register your models here.
class AreaInfoAdmin(admin.ModelAdmin):
'''地区模型管理类'''
list_per_page = 10 # 指定每页显示10条数据
list_display = ['id', 'atitle', 'title', 'parent'] # 列表展示模型类属性和方法
actions_on_bottom = True # 打开下方动作选框
actions_on_top = False
list_filter = ['atitle'] # 列表页右侧过滤栏
search_fields = ['atitle'] # 列表页上方的搜索框
models.py
from django.db import models
# Create your models here.
class AreaInfo(models.Model):
'''地址模型类'''
# 地区名称
atitle = models.CharField(verbose_name='标题', max_length=20) #verbose_name 指定标题名称
# 自关联属性
aParent = models.ForeignKey('self', null=True, blank=True)
def __str__(self):
return self.atitle
def title(self):
return self.atitle
title.admin_order_field = 'atitle' # 根据atitle进行排序
title.short_description = '地区名称' # 指定标题名称
# 判断父级属性是否为空
def parent(self):
if self.aParent is None:
return ''
return self.aParent.atitle
parent.short_description = '父级地区名称'
- 编辑页相关的选项。
显示字段顺序
属性如下:
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 = AreaInfo#关联子对象
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 = AreaInfo#关联子对象
extra = 2#额外编辑2个子对象
2)打开booktest/admin.py文件,修改AreaAdmin类如下:
class AreaAdmin(admin.ModelAdmin):
...
inlines = [AreaTabularInline]
3)刷新浏览器效果如下图:
上传图片
在python中进行图片操作,需要安装包PIL。
pip install Pillow==3.4.1
在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
3)打开booktest/migrations/0001_initial.py文件,删除AreaInfo部分,因为这个表已经存在。
4)回到命令行中,执行迁移。
python manage.py migrate
5)因为当前没有定义图书、英雄模型类,会提示“是否删除”,输入“no”后回车,表示不删除。
6)打开test5/settings.py文件,设置图片保存路径。
因为图片也属于静态文件,所以保存到static目录下。
MEDIA_ROOT=os.path.join(BASE_DIR,"static/media")
7)在static目录下创建media目录,再创建应用名称的目录,此例为booktest。
在管理页面admin中上传图片
1)打开booktest/admin.py文件,注册PicTest。
from django.contrib import admin
from booktest.models import *
admin.site.register(PicTest)
2)运行服务器,输入如下网址。
http://127.0.0.1:8000/admin/
3)点击“Add”添加数据,打开新页面。
4)选择图片,点击“save”按钮完成图片上传。 5)回到数据库命令行,查询表pictest中的数据如下图:
6)图片被保存到目录static/media/booktest/下,如下图:
用户自定义页面上传图片
-
定义用户上传图片的页面并显示,是一个自定义的表单。
-
定义接收上传文件的视图函数。
request对象有一个FILES的属性,类似于字典,通过request.FILES可以获取上传文件的处理对象。
在django中,上传文件不大于2.5M,文件放在内存中。上传文件大于2.5M,文件内容写到一个临时文件中。
Django处理上传文件的两个类:
FILE_UPLOAD_HANDLERS= (“django.core.files.uploadhandler.MemoryFileUploadHandler”,
“django.core.files.uploadhandler.TemporaryFileUploadHandler”)
upload_file.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传图片</title>
</head>
<body>
<form method="post" enctype="multipart/form-data" action="/upload_handle">
{% csrf_token %}
<input type="file" name="pic"><br/>
<input type="submit" value="上传">
</form>
</body>
</html>
view.py
from django.shortcuts import render
from django.conf import settings
from django.http import HttpResponse,JsonResponse
from booktest.models import PicTest,AreaInfo
# <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
# <class 'django.core.files.uploadedfile.TemporaryUploadedFile'>
def upload_handle(request):
'''上传图片处理'''
# 1.获取上传文件的处理对象
pic = request.FILES['pic']
# 2.创建一个文件
save_path = '%s/booktest/%s'%(settings.MEDIA_ROOT,pic.name)
with open(save_path, 'wb') as f:
# 3.获取上传文件的内容并写到创建的文件中
for content in pic.chunks():
f.write(content)
# 4.在数据库中保存上传记录
PicTest.objects.create(goods_pic='booktest/%s'%pic.name)
# 5.返回
return HttpResponse('ok')
上传图片参考资料:
- http://python.usyiyi.cn/documents/django_182/topics/http/file-uploads.html
- http://python.usyiyi.cn/documents/django_182/ref/files/uploads.html#django.core.files.uploadedfile.UploadedFile
分页
查询出所有省级地区的信息,显示在页面上。
AeroInfo.objects.filter(aParent__isnull = True)
- 查询出所有省级地区的信息。
- 按每页显示10条信息进行分页,默认显示第一页的信息,下面并显示出页码。
- 点击i页链接的时候,就显示第i页的省级地区信息。
from django.core.paginator import Paginator
paginator = Paginator(areas, 10) #按每页10条数据进行分页
Paginator类对象的属性:
属性名 | 说明 |
---|---|
num_pages | 返回分页之后的总页数 |
page_range | 返回分页后页码的列表 |
Paginator类对象的方法:
方法名 | 说明 |
---|---|
page(self, number) | 返回第number页的Page类实例对象 |
Page类对象的属性:
属性名 | 说明 |
---|---|
number | 返回当前页的页码 |
object_list | 返回包含当前页的数据的查询集 |
paginator | 返回对应的Paginator类对象 |
Page类对象的方法:
属性名 | 说明 |
---|---|
has_previous | 判断当前页是否有前一页 |
has_next | 判断当前页是否有下一页 |
previous_page_number | 返回前一页的页码 |
next_page_number | 返回下一页的页码 |
view.py
# /show_area页码
# 前端访问的时候,需要传递页码
from django.core.paginator import Paginator
def show_area(request, pindex):
'''分页'''
# 1.查询出所有省级地区的信息
areas = AreaInfo.objects.filter(aParent__isnull=True)
# 2. 分页,每页显示10条
paginator = Paginator(areas, 10)
# 3. 获取第pindex页的内容
if pindex == '':
# 默认取第一页的内容
pindex = 1
else:
pindex = int(pindex)
# page是Page类的实例对象
page = paginator.page(pindex)
# 4.使用模板
return render(request, 'booktest/show_area.html', {
'page':page})
show_area.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分页</title>
</head>
<body>
<ul>
{# 遍历获取每一条数据 #}
{# {% for area in page.object_list %}#}
{% for area in page %}
<li>{
{ area.atitle }}</li>
{% endfor %}
</ul>
{# 判断是否有上一页 #}
{% if page.has_previous %}
<a href="/show_area{
{ page.previous_page_number }}"><上一页</a>
{% endif %}
{# 遍历显示页码的链接 #}
{% for pindex in page.paginator.page_range %}
{# 判断是否是当前页 #}
{% if pindex == page.number %}
{
{ pindex }}
{% else %}
<a href="/show_area{
{ pindex }}">{
{ pindex }}</a>
{% endif %}
{% endfor %}
{# 判断是否有下一页 #}
{% if page.has_next %}
<a href="/show_area{
{ page.next_page_number }}">下一页></a>
{% endif %}
</body>
</html>
分页参考资料:
http://python.usyiyi.cn/translate/django_182/topics/pagination.html
省市县选择案例
- 显示省地区信息。
$.get('/prov', function(data){
})
- 省改变时在对应的下拉列表框中显示下级市的信息。
$.get('/city?pid='+pid, function(data){
})
request.GET.get(‘pid’)
或者:
$.get('/city'+$(this).val(), function(data){
})
- 市改变时在对应的下拉列表框中显示下级县的信息。
$.get('/dis?pid='+pid, function(data){
})
或者:
$.get('/dis'+$(this).val(), function(data){
})
area.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>省市县选择案例</title>
<script src="/static/js/jquery-1.12.4.min.js"></script>
<script>
$(function () {
// 发起一个ajax请求 /prov,获取所有省级地区的信息
// 获取信息,使用get
// 涉及到信息修改,使用post
$.get('/prov', function (data) {
// 回调函数
// 获取返回的json数据
res = data.data
// 获取prov下拉列表框
prov = $('#prov')
// 变量res数组,获取每一个元素:[地区id, 地区标题]
/*
for(i=0; i<res.length; i++){
id = res[i][0]
atitle = res[i][1]
option_str = '<option value="'+id + '">'+ atitle+ '</option>'
// 向prov下拉列表框中追加元素
prov.append(option_str)
}*/
$.each(res, function (index, item) {
id = item[0]
atitle = item[1]
option_str = '<option value="'+id + '">'+ atitle+ '</option>'
// 向prov下拉列表框中追加元素
prov.append(option_str)
})
})
// 绑定prov下拉列表框的change事件,获取省下面的市的信息
$('#prov').change(function () {
// 发起一个ajax请求 /city,获取省下面市级地区的信息
// 获取点击省的id
prov_id=$(this).val()
$.get('/city'+prov_id, function (data) {
// 获取返回的json数据
res = data.data
// 获取city下拉列表框
city = $('#city')
// 清空city下拉列表框
city.empty().append('<option>---请选择市---</option>')
// 获取dis下拉列表框
dis = $('#dis')
// 清空dis下拉列表框
dis.empty().append('<option>---请选择县---</option>')
// 变量res数组,获取每一个元素:[地区id, 地区标题]
// 遍历取值添加到city下拉列表框中
$.each(res, function (index, item) {
id = item[0]
atitle = item[1]
option_str = '<option value="'+id + '">'+ atitle+ '</option>'
// 向city下拉列表框中追加元素
city.append(option_str)
})
})
})
// 绑定city下拉列表框的change事件,获取市下面的县的信息
$('#city').change(function () {
// 发起一个ajax请求 /dis,获取市下面县级地区的信息
// 获取点击市的id
city_id=$(this).val()
$.get('/dis'+city_id, function (data) {
// 获取返回的json数据
res = data.data
// 获取dis下拉列表框
dis = $('#dis')
// 清空dis下拉列表框
dis.empty().append('<option>---请选择县---</option>')
// 变量res数组,获取每一个元素:[地区id, 地区标题]
// 遍历取值添加到dis下拉列表框中
$.each(res, function (index, item) {
id = item[0]
atitle = item[1]
option_str = '<option value="'+id + '">'+ atitle+ '</option>'
// 向dis下拉列表框中追加元素
dis.append(option_str)
})
})
})
})
</script>
</head>
<body>
<select id="prov">
<option>---请选择省---</option>
</select>
<select id="city">
<option>---请选择市---</option>
</select>
<select id="dis">
<option>---请选择县---</option>
</select>
</body>
</html>
view.py
# /areas
def areas(request):
'''省市县选中案例'''
return render(request, 'booktest/areas.html')
# /prov
def prov(request):
'''获取所有省级地区的信息'''
# 1.获取所有省级地区的信息
areas = AreaInfo.objects.filter(aParent__isnull=True)
# 2.变量areas并拼接出json数据:atitle id
areas_list = []
for area in areas:
areas_list.append((area.id, area.atitle))
# 3.返回数据
return JsonResponse({
'data':areas_list})
def city(request, pid):
'''获取pid的下级地区的信息'''
# 1.获取pid对应地区的下级地区
# area = AreaInfo.objects.get(id=pid)
# areas = area.areainfo_set.all()
areas = AreaInfo.objects.filter(aParent__id=pid)
# 2.变量areas并拼接出json数据:atitle id
areas_list = []
for area in areas:
areas_list.append((area.id, area.atitle))
# 3.返回数据
return JsonResponse({
'data': areas_list})