django admin 上传文件并显示文件图标
1.效果预览
2.自定义文件widget
2.1 定义django模板过滤器
为了显示已经上传的文件的文件名,需要从django模板上下文变量widget.value
中将文件名提取出来。在应用目录下新建templatetags文件夹,在里面新建两个python文件customfilter.py和_init_.py(__是两个下划线),编写自定义过滤器
# 自定义模板过滤器
# 从文件url中获取文件名
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
@register.filter
def get_file_name(value):
# 从文件URL中获取文件名
return str(value).split('/')[-1]
2.2 定义widget模板文件
自定义widget的模板文件来替换django admin的默认模板。
upload_file.html
{% load static %}
{% load customfilter %}
{#如果原来有上传的文件#}
{% if widget.value.url %}
{# 原来有上传的文件,显示原来上传的文件图标#}
<div class="selected-file"
style="background-image: url('{% static 'myapp/file.png' %}');
background-repeat: no-repeat;background-position: center;background-size: 48px;">
<i class="iconfont icon-delete" οnclick="delete_selected_file(this)"
style="z-index: 999;background-color:rgba(255,255,255,.8);position: absolute;right: 0;top: 0;"
title="删除文件"></i>
</div>
{# 原来有上传的文件,先隐藏图片上传组件#}
<div class="upload-file-container" id="upload-file-container"
style="background-image: url('{% static 'myapp/uploadfile.png' %}');background-repeat: no-repeat;background-size:48px;
background-position: center;display: none;">
<input type="{{ widget.type }}" name="{{ widget.name }}"{% if widget.value != None %}
value="{{ widget.value|stringformat:'s' }}"{% endif %}
id="id_uploadGoodsDetail"
accept="text/html,application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
οnchange="show_file(this)">
</div>
<div class="help" id="upload_file_help" hidden>支持上传小于2M的pdf、word和html文档</div>
<div class="help" id="selected_file_name"><a href="{{ widget.value.url }}">{{ widget.value|get_file_name }}</a>
</div>
{% else %}
<div class="upload-file-container" id="upload-file-container"
style="background-image: url('{% static 'myapp/uploadfile.png' %}');background-repeat: no-repeat;background-size:48px;
background-position: center;">
<input type="{{ widget.type }}" name="{{ widget.name }}"{% if widget.value != None %}
value="{{ widget.value|stringformat:'s' }}"{% endif %}
id="id_uploadGoodsDetail"
accept="text/html,application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
οnchange="show_file(this)">
</div>
<div class="help" id="upload_file_help">支持上传小于2M的pdf、word和html文档</div>
<div class="help" id="selected_file_name" hidden></div>
{% endif %}
{#引入jquery#}
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript">
var upload_file_container = $("#upload-file-container");//选择文件上传
var upload_help = $("#upload_file_help");//上传文件帮助文本
var selected_file_name = $("#selected_file_name");//选中的文件名
//将想要上传的文件显示为图标+文件名
function show_file(e) {
var file = e.files[0];
//console.log("file", file);
//检查文件拓展名
var ext = file.name.slice(file.name.lastIndexOf(".") + 1).toLowerCase();
var file_name = file.name;//选中的文件名
//console.log("%%%%%%%", ext);
if ("pdf" == ext || "html" == ext || "doc" == ext || "docx" == ext) {
//添加文件图标
upload_file_container.before("<div class=\"selected-file\"\n" +
" style=\"background-image: url('{% static 'myapp/file.png' %}');\n"+
" background-repeat: no-repeat;background-position: center;background-size: 48px;\">\n" +
" <i class=\"iconfont icon-delete\"\n" +
" style=\"z-index: 999;background-color:rgba(255,255,255,.8);position: absolute;right: 0px;top: 0px;\"\n" +
" title=\"删除文件\" οnclick=\"delete_selected_file(this)\"></i>\n" +
"</div>");
selected_file_name.html(file_name);//设置选中的文件名
selected_file_name.show();
upload_file_container.hide();//隐藏上传文件的组件
upload_help.hide();//隐藏帮助文本
} else {
alert("选择的文件类型不正确,只能上传pdf、html或word文档");
e.value = '';//清空input的值
}
}
//删除选中的文件
function delete_selected_file(e) {
$(e).closest(".selected-file").remove();
selected_file_name.hide();//隐藏显示文件名的组件
upload_file_container.show();//显示文件上传组件
upload_help.show();//显示文件上传帮助文本
}
</script>
<style>
.upload-file-container {
width: 48px;
height: 48px;
position: relative;
display: inline-block;
overflow: hidden;
}
.upload-file-container input {
position: absolute;
top: 0;
height: 100px;
opacity: 0;
}
.upload-file-container:hover {
border: solid #25adc6 1px;
}
/*已经选中的文件显示*/
.selected-file {
width: 64px;
height: 64px;
position: relative;
display: inline-block;
overflow: hidden;
}
//删除符号使用阿里云字体图标
@font-face {
font-family: 'iconfont'; /* project id 1361777 */
src: url('//at.alicdn.com/t/font_1361777_ufufbwqmfpa.eot');
src: url('//at.alicdn.com/t/font_1361777_ufufbwqmfpa.eot?#iefix') format('embedded-opentype'),
url('//at.alicdn.com/t/font_1361777_ufufbwqmfpa.woff2') format('woff2'),
url('//at.alicdn.com/t/font_1361777_ufufbwqmfpa.woff') format('woff'),
url('//at.alicdn.com/t/font_1361777_ufufbwqmfpa.ttf') format('truetype'),
url('//at.alicdn.com/t/font_1361777_ufufbwqmfpa.svg#iconfont') format('svg');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 20px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-delete:before {
content: "\e63c";
}
</style>
2.3 定义python类
# 上传文件的组件
class UploadFileInput(FileInput):
template_name = "myapp/upload_file.html"
def render(self, name, value, attrs=None, renderer=None):
context = self.get_context(name, value, attrs)
template = loader.get_template(self.template_name).render(context)
return mark_safe(template)
3.定义Model和ModelFrom
在应用的models.py中定义一个实验用的模型和模型表单
class UploadFileModel(models.Model):
file = models.FileField("上传文件", upload_to="files/")
class UploadFileModelForm(ModelForm):
file = forms.FileField(label="上传文件", widget=UploadFileInput,help_text="",
required=False)
class Meta:
model = UploadFileModel
fields = [ 'file', ]
4.结束语
上述定制的文件上传组件在本地选择文件时并没有上传到服务器,只有在admin表单提交时才会提交至服务器,这样能减少ajax请求,方便修改,同时也省略了垃圾文件的处理逻辑,维护更简单。