In this blog, the goal is to show the capabilities of Django’s built-in Fields, specifically Django FileField, for storing files. Every experienced web developer had to work with files at one point. Files can be stored in a database in many ways, either locally or on a server.
So what actually is FileField in Django?
Django FileField inherits an abstract Field class representing the database table column, and it is used for simple file uploads.
Before we go into a more detailed explanation of the FileField parameters, we will define one model with a pdf_file
column.
class Book(models.Model): title = models.CharField(max_length=255) published_date = models.DateTimeField(null=True) pdf_file = models.FileField()
Let’s look at how FileField behaves in the database and what we use it for.
Django FileField in Database
For the specified model after python manage.py makemigrations
the following migration file is obtained.
class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='Book', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=255)), ('published_date', models.DateTimeField(null=True)), ('pdf_file', models.FileField(upload_to='')), ], ), ]
And after migrating python manage.py migrate
we get the following data structure in the database:
Name | Data Type | Database Example |
id | Big int | 2 |
title | Character Varying (255) | Django FileField Book |
published_date | Timestamp with time zone | 2022-01-19 16:56:03+01 |
pdf_file | Character Varying (100) | django_filefield_book.pdf |
We see one interesting thing, FileField is stored in the Postgres database as Character Varying.
What would that mean for our files? Where do they end up?
Our files are stored locally on the computer. So in this example, the FileField or pdf_file
column in the database would save the path of our file.
Let’s look at the FileField parameters.
Django FileField parameters
class FileField(upload_to=None, max_length=100, **options)
FileField has 2 optional parameters. Let’s see in the following example what these parameters actually mean.
pdf_file = models.FileField(upload_to='uploads/', max_length=255, **options)
upload_to
specifies the route to which a particular file will be saved. For example, book.pdf will be saved in theBASE_DIR/uploads
directory, the file name and folder in which the file is located will be saved in the database.max_length
determines the maximum length of the saved path. The default is 100. If you have files and paths with long names, you must not forget to change this parameter. Otherwise, you might getDatabaseError: value too long for type character varying (100)
.
Finally, let’s look at an example of a saved book in the database:
id | title | published_date | pdf_file |
1 | Book | 2022-01-19 19:49:22+01 | uploads/book.pdf |
Although we have defined the directory in which we save the files, it is still not the best practice.
According to the official Django documentation, the files should be saved in the MEDIA folder. The media folder should be defined in the Django settings, so let’s show what it should look like.
Define next in your settings.py.
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/'
From the moment we define MEDIA_ROOT, all subsequent files we upload will be saved in media/uploads/
because we defined upload_to = 'uploads'
. But only the file path upload_to + file_name
is saved in the database.
Now that we have successfully saved our files to the computer. How can we retrieve these files?
How to access FileField files?
We access our files via URL with the exposed path to the file.
So let’s try to access our file saved in media/uploads/book.pdf
We simply send a GET request to http://127.0.0.1:8000/media/uploads/book.pdf
, but we get: Not Found: /media/uploads/book.pdf
More precisely: "GET /media/uploads/book.pdf HTTP/1.1" 404
So why is that so?
This happens because the path is not defined in urls.py, i.e., Django tries to find a valid path to the file we are looking for, but as it does not find the right URL, it concludes that it does not exist.
So the next step is to define a valid URL, but since files can be named differently, how do we do that?
It is quite simple, we previously defined the MEDIA folder, and it now stores files. We just have to expose that file in URLs, so we can then access everything in it in the following way: host + media_root + upload_to
parameter (what is saved in the database as varchar)
Let’s see how to expose a URL. Set the following in root URLs:
from django.conf import settings from django.conf.urls.static import static urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
WARNING - use this only in Development! This method of serving static files is NOT safe in production. Read more in the official Django documentation
Now that we’ve exposed the files, we can finally retrieve the file via URL. Assuming you’re running this locally, let’s type the following:
http://127.0.0.1:8000/ + media/ + upload_to parameter
In this example, considering upload_to
parameter is uploads/book.pdf:
http://127.0.0.1:8000/media/uploads/book.pdf
The uploaded pdf is displayed in the web browser, and we get:"GET /media/uploads/book.pdf HTTP / 1.1" 200
NOTE - Occasionally we can get a response with status code 304. No need to worry, it's a redirection to a cached resource.
FileField with Django ORM
Object-relational mapping helps us create queries over the database without writing actual SQL queries. So it can be pretty handy.
In the following example, we will show what can be done with Django ORM.
# Filters books using string manipulation of pdf_file Book.objects.filter(pdf_file__exact='uploads/book.pdf') Book.objects.filter(pdf_file__iexact='uploads/BOOK.pdf') Book.objects.filter(pdf_file__contains='book') Book.objects.filter(pdf_file__endswith=".pdf") Book.objects.filter(pdf_file__startswith="uploads") # Filters books using regex Book.objects.filter(pdf_file__regex='book') # Filters books by existing pdf_file Book.objects.filter(pdf_file__isnull=False) # Filters books by given array of pdf_files Book.objects.filter(pdf_file__in=['uploads/book.pdf'])
As we can see, since pdf_column is character varying in the database, we can do all the standard operations on it, which we could do with CharField, for example. Here are just a few, mostly retrieving books using string operations.
Summary
FileField is a powerful tool for saving files. However, it is also dangerous to misuse and expose files that we should not reveal. Now you can use everything you learned and finally start uploading files to your application.
I hope you found this blog post helpful!