写在前面
有时候想把资料存在云端,我们可以选择百度云,但是它给的实在太多了,而且下载的时候还限速,我就想要简单的上传下载功能,所以尝试用Django做了一个,在此记录一下。
本文主要讲models和admin的写法,前端只有很少一部分内容。
准备工作
#创建项目
django-admin startproject myproject
#创建app
python manage.py startapp uploader
修改配置
settings.py
添加以下路径:
#修改
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'uploader',#添加app
]
#修改
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',
],
},
},
]
#以下为添加的部分
#静态文件路径
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
)
#上传文件的保存路径
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media/')
定义models
uploader/models.py:
from django.db import models
from django.utils import timezone
# Create your models here.
class Uploader(models.Model):
#文件id,作为主键
id = models.AutoField(primary_key=True)
#文件标题,可以理解为文件的描述
title = models.CharField(verbose_name = '标题',max_length = 100)
#文件主体,在后面定义保存的路径,通过日期来区分
file = models.FileField(verbose_name='文件', upload_to='uploader/files/%Y%m%d/', blank=True)
#上传时间
created = models.DateTimeField(verbose_name = '创建时间',default = timezone.now)
#文件大小
size = models.CharField(verbose_name='大小',max_length=50,blank=True)
#上传者
author = models.ForeignKey(
'auth.User',
verbose_name='用户',
on_delete=models.CASCADE,
default=1)
#为了后台上传时也能计算文件大小,重写save方法
def save(self,*args,**kwargs):
size = round(self.file.size / 1024 ** 2, 2)
self.size = str(size) + "MB" if size < 1024 else str(round(size / 1024, 2)) + "GB"
#大小计算完毕后重新将文件保存
super(Uploader, self).save(*args,**kwargs)
class Meta:
#文件排序方式,上传时间倒序
ordering = ('-created',)
#自定义model名称
verbose_name = '附件'
verbose_name_plural = verbose_name
def __str__(self):
#显示文件标题
return self.title
定义admin
uploader/admin.py:
from django.contrib import admin
from .models import Uploader
from django.utils.translation import ugettext_lazy as _
from django.utils.html import format_html
import os
from django.db.models.signals import post_delete
from django.dispatch import receiver
from django.conf import settings
# Register your models here.
#以下为自定义过滤器,通过上传者进行过滤
#写法参考本站的后端代码,这里没有注释
class ImageFilter(admin.SimpleListFilter):
title = _("用户")
parameter_name = 'author'
def lookups(self, request, model_admin):
authors = list(set(map(lambda x: x.author, Uploader.objects.all())))
for author in authors:
yield author.id, _(author.username)
def queryset(self, request, queryset):
id_ = self.value()
if id_:
return queryset.filter(author__id__exact=id_)
else:
return queryset
#自定义admin后台
class UploaderAdmin(admin.ModelAdmin):
#显示的信息,download_link为下载链接
list_display = ('id','title','author','created','size','download_link')
#可以作为超链接的选项(转向model的更改页面)
list_display_links = ('id','title')
#过滤器,通过创建时间过滤
list_filter = [ImageFilter,'created']
#修改页面显示的内容
fields = ['title','author','created','file']
#下载链接,将其渲染为HTML
def download_link(self, obj):
return format_html('<a href="%s">%s</a>' % (obj.file.url, '下载'))
#据说allow_tags在django2.x以上被停用了,但是我这里用的还可以
#如果遇到问题可以使用mark_safe
download_link.allow_tags = True
download_link.short_description = '下载'
#定义一个方法,当文件在后台被删除时一并删除服务器中的资源
@receiver(post_delete, sender=Uploader)
def delete_upload_files(sender, instance, **kwargs):
files = getattr(instance, 'file')
if not files:
return
fname = os.path.join(settings.MEDIA_ROOT, str(files))
if os.path.isfile(fname):
os.remove(fname)
#将model注册到后台
admin.site.register(Uploader,UploaderAdmin)
使用
首先迁移数据库:
python manage.py makemigrations
python manage.py migrate
创建超级用户:
python manage.py createsuperuser
启动服务:
python manage.py runserver
访问:
127.0.0.1:8000/admin/
前端使用
myproject/urls.py:
from django.contrib import admin
from django.urls import path,include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('upload/',include('uploader.urls',namespace ='upload')),
]
urlpatterns += static(settings.MEDIA_URL,document_root = settings.MEDIA_ROOT)
uploader/forms.py:
from django import forms
from .models import Uploader
class UploadForm(forms.ModelForm):
class Meta:
model = Uploader
fields = ('title','file')
uploader/views.py:
from django.shortcuts import render,redirect
from django.contrib.auth.decorators import login_required
from .forms import UploadForm
from django.contrib.auth.models import User
from django.http import HttpResponse
@login_required(login_url='/admin/')
def uploadview(request):
if request.method == 'POST':
upload_form = UploadForm(request.POST,request.FILES)
if upload_form.is_valid():
new_upload = upload_form.save(commit = False)
new_upload.author = User.objects.get(id = request.user.id)
new_upload.save()
return HttpResponse("上传成功")
else:
return HttpResponse("表单内容有误,请重新填写")
else:
upload_form = UploadForm()
context = {'upload_form':upload_form}
return render(request,'uploader/file.html',context = context)
uploader/urls.py:
from django.urls import path,include
from . import views
app_name = 'uploader'
urlpatterns = [
path('files/',views.uploadview,name='files'),
]
编写模版:
根目录下新建templates文件夹,其中新建uploader文件夹:
templates/base.html:
<!DOCTYEP html>
{% load static %}
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
{% block item %}{% endblock item %}
</div>
</nav>
</head>
<body>
<script src="{% static 'jquery/jquery-3.5.1.js' %}"></script>
<script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
<div id="wrapper">
{% block content %}{% endblock content %}
<div od="push"></div>
</div>
{% block script %}{% endblock script %}
</body>
templates/uploader/file.html:
{% extends "base.html" %}
{% load static %}
{% block title %}
上传文件
{% endblock title %}
{% block item %}
<p class="navbar-brand">上传文件</p>
<div>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{% url 'upload:files' %}">上传</a>
</li>
</ul>
</div>
{% endblock item %}
{% block content %}
<style type="text/css">
.upload{
position:absolute;
left:30px;
top:220px;
}
</style>
<div class="container">
<div class="row">
<div class="col-4"></div>
<div class="col-8">
<br>
<form method="post" action="." enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group col-md-4">
<label for="title">描述</label>
<input type="text" class="form-control" id="title" name="title">
</div>
<div class="form-group col-md-4">
<label for="file">文件</label>
<input type="file" class="form-control-file" name="file" id="file">
</div>
<br>
<div class="upload">
<button type="submit" class="btn btn-primary" style="width:220px;">上传</button>
</div>
</form>
</div>
</div>
</div>
{% block script %}
{% endblock script %}
{% endblock content%}
bootstrap和jquery等文件可以去官网下载。。真的需要的话,也可以给我发邮件,我通过邮箱发给你。
写在后面
以上内容,后端应该是OK的,至于前端,我没有仔细检索,我那个项目里有两个app,我只选择这一部分粘贴了过来,前端可能有跟另一个app交叉的部分。
如果功能再次优化,我会将其传到github,到时候会附上地址。