This complete guide will walk you through creating a Django application that handles file uploads, displays a list of uploaded files, allows downloading files, and provides delete functionality - all using Jinja templates.
Step 1: Set Up Your Django Project
First, create a new Django project and app:
django-admin startproject filemanager
cd filemanager
python manage.py startapp files
Step 2: Install Required Packages
Install Django and Jinja2:
pip install django jinja2
Step 3: Configure Django Settings
In filemanager/settings.py
:
- Add your app to
INSTALLED_APPS
:
INSTALLED_APPS = [
# ...
'files',
]
- Configure Jinja2 as a template backend:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'environment': 'filemanager.jinja2.environment',
'context_processors': [
'django.template.context_processors.request',
],
},
},
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'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',
],
},
},
]
- Set up media files configuration:
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
- Create a
jinja2.py
file in your project directory (filemanager/jinja2.py
):
from jinja2 import Environment
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
def environment(**options):
env = Environment(**options)
env.globals.update({
'static': staticfiles_storage.url,
'url': reverse,
})
return env
Step 4: Create the File Model
In files/models.py
:
from django.db import models
class UploadedFile(models.Model):
file = models.FileField(upload_to='uploads/')
uploaded_at = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=255, blank=True)
size = models.PositiveIntegerField(default=0)
def save(self, *args, **kwargs):
# Set the name from the filename if not provided
if not self.name:
self.name = self.file.name
# Get the file size
self.size = self.file.size
super().save(*args, **kwargs)
def __str__(self):
return self.name
def get_absolute_url(self):
return self.file.url
def delete(self, *args, **kwargs):
# Delete the file from storage first
self.file.delete()
super().delete(*args, **kwargs)
Run migrations:
python manage.py makemigrations
python manage.py migrate
Step 5: Create Forms
In files/forms.py
:
from django import forms
from .models import UploadedFile
class FileUploadForm(forms.ModelForm):
class Meta:
model = UploadedFile
fields = ['file']
def clean_file(self):
file = self.cleaned_data.get('file')
if file:
# Add any file validation here (size, type, etc.)
if file.size > 10 * 1024 * 1024: # 10MB limit
raise forms.ValidationError("File too large ( > 10MB )")
return file
Step 6: Create Views
In files/views.py
:
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse, Http404
from django.conf import settings
import os
from .models import UploadedFile
from .forms import FileUploadForm
def file_list(request):
files = UploadedFile.objects.all().order_by('-uploaded_at')
return render(request, 'files/list.html', {'files': files})
def upload_file(request):
if request.method == 'POST':
form = FileUploadForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('file_list')
else:
form = FileUploadForm()
return render(request, 'files/upload.html', {'form': form})
def download_file(request, pk):
uploaded_file = get_object_or_404(UploadedFile, pk=pk)
file_path = os.path.join(settings.MEDIA_ROOT, uploaded_file.file.name)
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/octet-stream")
response['Content-Disposition'] = f'inline; filename={os.path.basename(file_path)}'
return response
raise Http404
def delete_file(request, pk):
uploaded_file = get_object_or_404(UploadedFile, pk=pk)
if request.method == 'POST':
uploaded_file.delete()
return redirect('file_list')
return render(request, 'files/confirm_delete.html', {'file': uploaded_file})
Step 7: Set Up URLs
- InÂ
filemanager/urls.py
:
from django.contrib import admin
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
from files import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.file_list, name='file_list'),
path('upload/', views.upload_file, name='upload_file'),
path('download/<int:pk>/', views.download_file, name='download_file'),
path('delete/<int:pk>/', views.delete_file, name='delete_file'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Step 8: Create Jinja Templates
Create a templates
directory in your project root and add these files:
templates/base.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Manager</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<h1 class="mb-4">File Manager</h1>
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
templates/files/list.html
:
{% extends "base.html" %}
{% block content %}
<div class="mb-4">
<a href="{{ url('upload_file') }}" class="btn btn-primary">Upload File</a>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Size</th>
<th>Uploaded At</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for file in files %}
<tr>
<td>{{ file.name }}</td>
<td>{{ (file.size / 1024)|round(2) }} KB</td>
<td>{{ file.uploaded_at.strftime('%Y-%m-%d %H:%M') }}</td>
<td>
<a href="{{ file.get_absolute_url() }}" class="btn btn-sm btn-outline-primary" target="_blank">View</a>
<a href="{{ url('download_file', pk=file.pk) }}" class="btn btn-sm btn-outline-success">Download</a>
<a href="{{ url('delete_file', pk=file.pk) }}" class="btn btn-sm btn-outline-danger">Delete</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="4" class="text-center">No files uploaded yet.</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
templates/files/upload.html
:
{% extends "base.html" %}
{% block content %}
<h2>Upload File</h2>
<form method="post" enctype="multipart/form-data">
{{ form.csrf_token }}
<div class="mb-3">
<label for="file" class="form-label">Select file to upload:</label>
<input type="file" class="form-control" id="file" name="file" required>
{% if form.file.errors %}
<div class="text-danger">
{% for error in form.file.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<button type="submit" class="btn btn-primary">Upload</button>
<a href="{{ url('file_list') }}" class="btn btn-secondary">Cancel</a>
</form>
{% endblock %}
templates/files/confirm_delete.html
:
{% extends "base.html" %}
{% block content %}
<h2>Confirm Delete</h2>
<p>Are you sure you want to delete "{{ file.name }}"?</p>
<form method="post">
{{ form.csrf_token }}
<button type="submit" class="btn btn-danger">Yes, delete</button>
<a href="{{ url('file_list') }}" class="btn btn-secondary">Cancel</a>
</form>
{% endblock %}
Step 9: Create Media Directory
Create a media
directory in your project root:
mkdir media
Step 10: Run and Test
- Run the development server:
python manage.py runserver
- Visit
http://localhost:8000
in your browser.
Additional Features You Might Want to Add
- File Type Restrictions: Modify the form to only accept certain file types.
- User Authentication: Associate files with users and add login requirements.
- File Preview: Add preview functionality for images and PDFs.
- Pagination: Add pagination for the file list if you expect many files.
- Search: Implement search functionality to find files by name.
This complete example provides all the basic functionality for file uploads, listing, downloading, and deletion in Django using Jinja templates.