Django-静态文件
Django - 静态文件
before
django提供了两种静态文件:
- static,这类静态文件用于django项目所需要的静态文件,如js、css、img等等这些静态文件。
- media,这类静态文件就是用户相关的静态文件了,比如存放用户上传的图像、音视频文件等。
static
来看怎么引入的。
首先,项目结构:
D:\TMP\DEMO\
├─app01 # app,内部结构略
| └─static
| └─index.css
├─demo
| └─settings.py
├─static
| ├─jquery-2.2.3.min.js
| ├─a.jpg
│ └─bootstrap
│ ├─css
| └─bootstrap.css
│ └─js
| └─bootstrap.js
└─templates
├─books.html
└─form.html
然后在settings.py
中配置相关路径:
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_URL = '/static/' # 通过别名指向STATICFILES_DIRS目录,当然,别名也可以修改
STATICFILES_DIRS = [ # 列表或者元组都行
os.path.join(BASE_DIR, 'static'),
os.path.join(BASE_DIR, 'app01', 'static') # 你也可以配置多个静态文件目录,只需拼上路径就好了
]
我们把静态文件CSS
、JS
、img
等都可以存放在static
目录。当然,这里面可以有多个静态文件目录。但每个目录必须存在,才能后续引用。
引入方式1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/jquery-2.2.3.min.js">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.js">
</head>
<body>
</body>
<script src="/static/bootstrap/js/bootstrap.js"></script>
</html>
但是,我不推荐上面这种写死了的形式,如果静态文件位置改变,那就所有的页面都无法引用静态文件了。
所以,推荐下面这种形式。通过settings
中的static
别名,我们如果更改了具体的静态文件路径,只需要在setttings
中更改别名的路径就可以了,相当灵活。
引入方式2:
<!DOCTYPE html>
<html lang="en">
<head>
{% load static %} <!-- 使用load声明引用静态文件 -->
{% static 'a.jpg' as a_obj %} <!-- 某个文件被多次使用可以赋值给变量 -->
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>看中医,学医术,尽在中医智库</title>
<link rel="stylesheet" href="{% static 'index.css' %}"> <!-- 这个引入是app01下的static中的静态文件 -->
<link rel="stylesheet" href="{% static 'base.css' %}"> <!-- 使用static别名进行引用静态文件 -->
<link rel="stylesheet" href="{% static 'base.css' %}">
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
</head>
<body>
<img src="{% static 'a.jpg' %}" alt="">
<img src="{{ a_obj }}"></img>
</body>
<script src="{% static 'jquery-2.2.3.min.js' %}"></script>
<script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
</html>
推荐使用方式2。
get_static_prefix
get_static_prefix
用的较少,它是获取settings中STATIC_URL
指向的别名,在模板中渲染时是这样的:
<!DOCTYPE html>
<html lang="en">
<head>
{% load static %} <!-- 必须load static -->
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.js' %}">
<link rel="stylesheet" href="{% get_static_prefix %}bootstrap/css/bootstrap.js">
</head>
<body>
{% get_static_prefix %} <!--渲染成: /static/ -->
</body>
<script src="{% static 'bootstrap/js/bootstrap.js' %}"></script>
<script src="{% get_static_prefix %}bootstrap/js/bootstrap.js"></script>
</html>
get_static_prefix
的用法如上示例所示,它跟static
的区别是,get_static_prefix
需要手动拼接路径;而static
是Django会自动拼接路径,推荐使用static
。
media
media通常就是跟用户打交道了,所以涉及到表中的两个字段:
-
FileField
:一个专门用于文件上传的字段。它的常用属性有:
- FieldFile.name,上传文件的文件名。
- FieldFile.size,获取文件大小。
- FieldFile.path,只读属性,通过调用底层的 path() 方法,访问文件的本地文件系统路径,用的较少。
-
ImageField
:继承 FileField 的所有属性和方法,但也验证上传的对象是有效的图像。除了 FileField 的特殊属性外, ImageField 也有两个常用属性:
- ImageField.height_field,模型字段的名称,每次保存模型实例时将自动填充图像的高度。
- ImageField.width_field,模型字段的名称,每次保存模型实例时将自动填充图像的宽度。
除此之外,我们还可以在settings.py中,配置两个media参数:
- MEDIA_ROOT:默认值为
''
(空字符串)。用于保存用户上传的文件的绝对路径。另外,MEDIA_ROOT 和 STATIC_ROOT 必须有不同的值。 - MEDIA_URL: 默认值为
''
(空字符串),如果设置为非空值,则必须以斜线结束。结合路由中的配置,可以对外提供访问。
来看看简单的用法,如果你的settings.py中,没有配置MEDIA_ROOT和MEDIA_URL,即两个参数默认为空字符串,然后models.py中的字段设置如下:
from django.db import models
class Asset(models.Model):
user = models.CharField(max_length=32, verbose_name='用户名')
avatar = models.FileField(upload_to='avatars/', verbose_name='用户头像', default='avatars/default.png')
def __str__(self):
return self.user
那么,如果生成一条用户记录时,图片会被django默认保存到项目根目录下的avatars
目录下,如果avatars
目录不存在,会先创建。而Asset表的avatar字段存的数据长这样avatars/20190701134630.jpg
,很明显存的是路径而不是文件内容。
但一切都是默认的,肯定不符合我们的需求,我们需要自己定义这些用户文件应该保存到项目的哪个目录下,当然通常这些文件将会保存到对应的文件服务器中。
来看看怎么自定义吧。
首先先来看配置文件settings.py
:
# 上传的文件将会保存到项目根目录下的media目录下
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = "/media/" # 必须反斜杠结尾。
然后models.py
不变:
from django.db import models
class Asset(models.Model):
user = models.CharField(max_length=32, verbose_name='用户名')
avatar = models.FileField(upload_to='avatars/', verbose_name='用户头像', default='avatars/default.png')
def __str__(self):
return self.user
另外,urls.py
还要进行特殊的配置:
from django.contrib import admin
from django.urls import path, re_path
from django.views.static import serve
from django.conf import settings
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 关于media配置的固定写法
re_path('media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT})
]
经过这么一配置啊,我们浏览器就可以通过访问media
url,访问到项目根目录下的静态文件了,如http://127.0.0.1:8000/media/avatars/20190701134630.jpg
。
来通过一个示例进行演示,包括文件上传、删除、下载的示例。
settings.py
:
# ----------------- media ------------
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = "/media/"
urls.py
:
from django.contrib import admin
from django.urls import path, re_path
from django.views.static import serve
from django.conf import settings
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 关于media配置的固定写法
re_path('media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
path('add/', views.add, name='add'),
path('user_list/', views.user_list, name='user_list'),
re_path('del_user/(?P<pk>.*?)/', views.del_user, name='del_user'),
re_path('download/(?P<pk>.*?)/', views.download, name='download'),
]
views.py
:
import os
from django.shortcuts import render, HttpResponse, redirect
from django.http import FileResponse
# 解决图片名称不能是中文的问题,导入这个家伙
from django.utils.encoding import escape_uri_path
from app01 import models
def add(request):
""" 添加用户信息 """
if request.method == "GET":
return render(request, 'add_user.html')
user = request.POST.get('user')
avatar = request.FILES.get('avatar')
models.Asset.objects.create(user=user, avatar=avatar)
return redirect('/user_list/')
def user_list(request):
""" 用户列表页 """
if request.method == "GET":
users = models.Asset.objects.all()
return render(request, 'user_list.html', {"users": users})
def del_user(request, pk):
# 如果直接使用delete删除记录,那么表中的记录是会被删除,但是avatar对应的文件不会被删除
# models.Asset.objects.filter(pk=pk).delete()
# 所以,想要连文件一块删除,则需要手动删除
obj = models.Asset.objects.filter(pk=pk).first()
obj.avatar.delete(save=True) # 删除对应的文件
obj.delete() # 删除表中记录
return redirect('/user_list/')
def download(request, pk):
# 下面示例演示从django项目的本地下载图片
# 如果是文件存储在远程,则可以通过requests模块获取文件内容,在封装到FileResponse中
file_obj = models.Asset.objects.filter(pk=pk).first()
file_path = file_obj.avatar.path
# print(file_path)
f = open(file_path, 'rb')
response = FileResponse(f)
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename="{}"'.format(escape_uri_path(file_path.rsplit(os.sep, 1)[-1]))
# 这里不能关闭文件
# f.close()
return response
models.py
:
from django.db import models
class Asset(models.Model):
user = models.CharField(max_length=32, verbose_name='用户名')
avatar = models.FileField(upload_to='avatars/', verbose_name='用户头像', default='avatars/')
def __str__(self):
return self.user
demo\templates\add_user.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>添加用户信息</h3>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="text" name="user" value="zhangkai">
<input type="file" name="avatar">
<input type="submit" value="提交">
</form>
</body>
</html>
demo\templates\user_list.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-10">
<h3>用户列表页</h3>
<a href="/add/"><button>添加用户</button></a>
<table class="table table-hover table-striped">
<thead>
<tr>
<th>用户名</th>
<th>图片</th>
<th>大小(kb)</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for row in users %}
<tr>
<td>{{ row.user }}</td>
<td>{{ row.avatar.name }}</td>
<td>{{ row.avatar.size }}</td>
<td>
<a href="{% url 'del_user' row.pk %}" class="btn btn-default">删除</a>
<a href="{% url 'download' row.pk %}" class="btn btn-primary">下载</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>