How to handle media uploads in Django
Tags: django, python, webdev, files
Published: 2024-05-12 21:47:37

It is a common feature in most web apps to prompt users to upload files or documents. These files include image files (**.jpeg, .jpg, .png**) or other document files like PDFs, audio files (.mp3), etc.

[Django](https://docs.djangoproject.com/en/5.0/), a very popular and powerful web framework in Python provides built-in methods and utilities that enables seamless upload and storage of files in web applications.

In Django, there are 3 ways to accept media uploads from users. They include the following:

- Django ModelForms

- Django Forms

- Pure HTML forms

In this tutorial, you would learn how to handle media uploads within a local storage using Django ModelForms. [ModelForms](https://docs.djangoproject.com/en/5.0/topics/forms/modelforms/) provides a convenient and faster way to create forms in Django. They are also very customizable.

By the end of this tutorial, you would have learned the following:

- How to receive/accept media files via a ModelForm 
- How to save and handle the received file object
- How to render/display saved image files on a web page
- How to make saved files available for download by users. 


## Project requirements

- **Python**: You need python installed on your computer. Preferably python3.8 or higher

- **Django**: The latest version of Django is required. 

Create a new virtual environment using the `virtualenv` package in Python:

```
virtualenv env
```
Use the command below to activate the virtual environment:

Windows:

```
env\Scripts\activate
```

Linux/macOS:

```
env/bin/activate
```

Install Django in the new virtual environment:

```
pip install Django
```
_**Note**_: To confirm your virtual environment (VE) is activated, check to see the name of your VE in a bracket like the one below:

```
(env) >>>
```


## Getting started with Django file upload tutorial

You need to create a new Django project and app to display how Django handles media uploads. 


### Create a Django project

Follow the steps below to create a Django project:

a. Open your terminal
b. Run the command below to create a Django project

```
django-admin startproject my_project
```
c. Enter the command below to move into the new _my_project_ folder

```
cd my_project
```
Next step is to create an app.


### Create a Django app

Enter the command below to create an app named **file_upload**

```
python manage.py startapp file_upload

```

Open the settings.py file in your project folder and add the app to the INSTALLED_APPS list as seen below:

```
#my_project/settings.py

INSTALLED_APPS = [
...
"file_upload"
]
```


## Django MEDIA ROOT and MEDIA URL

MEDIA_ROOT and MEDIA_URL variables are crucial in handling media uploads in a local filesystem storage. They provide a way of accessing and storing saved files in a Django app.


### Django MEDIA ROOT 

MEDIA ROOT refers to the root folder where all uploaded files are stored in a Django web app. It refers to the folder where all files can be accessed from.


#### How to set up a MEDIA ROOT

To set up a MEDIA ROOT, do the following:

a. Create a new folder named _uploads_ in your project folder

```

#file_arrangement
my_project
   \__ my_project
   \__ file_upload
   \__ uploads
```

b. Open settings.py file and include the following line of code in it:

```
MEDIA_ROOT = BASE_DIR / "uploads"
```

This setting instructs Django to save uploaded files to the uploads folder within your project, that is **BASE_DIR**.

BASE_DIR is a variable automatically set by Django. It contains the file path to your current project, which is also known as the base directory.


### Django MEDIA URL

MEDIA URL refers to the root URL that renders user-uploaded files present in the MEDIA ROOT of a web app. 

For example, if you uploaded an image file titled _example.jpg_, it is rendered on the web via a url like the one below:

https://mysite/[MEDIA_URL]/[MEDIA_ROOT]/images/example.jpg

The URL above is analyzed as follows:

- mysite is the domain name of your website.
- media_url is the base/root url for uploaded media.
- media_root is the root folder for uploaded media.
- images is another folder inside the MEDIA ROOT that stores image files only
- example.jpg is the file name.


#### How to set up a MEDIA URL

a. Open your project's settings.py file and add the code below:

```
MEDIA_URL = "files/"
```

b. Open the urls.py file in your project folder
c. Add the following import statements:

```
from django.conf import settings
from django.conf.urls.static import static
```

d. Write the following code below the urlpatterns list in the urls.py file:

```
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```

In the steps above, a MEDIA URL, named _files_ is created. This root url for media uploads is finally added to the list of URLs. 


## Create a Database Model

The next step is to create a database model named Uploads. This model will have three field types which includes: A Charfield,  [FileField](https://docs.djangoproject.com/en/5.0/ref/models/fields/#django.db.models.FileField) and an [ImageField](https://docs.djangoproject.com/en/5.0/ref/models/fields/#django.db.models.ImageField). Follow the steps below to create the `Uploads` model:

a. Go to the models.py file in your app
b. Create a simple database model titled `Uploads`

```
from django.db import models

# Create your models here.
class Uploads(models.Model):
   caption = models.CharField(unique=False, null=False, blank=False, max_length=20, default="An image")
   file_field = models.FileField(upload_to="files", null=False, blank=False)
   image_field = models.ImageField(upload_to="images", null=False, blank=False)
```
The functions of each field are listed below:

`CharField` stores textual data.
`FileField` stores files such as PDFs, docx, etc.
`ImagField` stores image files.

The `FileField` and `ImageField` have an `upload_to` argument. This refers to a new folder inside your project's MEDIA ROOT where the content of each file field will be stored. It is created automatically when a file is uploaded to each field. 


### Run migrations for the database

Run database migrations via the steps below:

a. Open the terminal and run this command

```
python manage.py makemigrations

```
b. Run the command below to add the migrations to a file

```
python manage.py migrate
```
Migrations help to track changes made to the database.


## Build a ModelForm

Follow the steps below to build a ModelForm:

a. Create a `forms.py` file in your app folder

```
my_project
   \___ file_upload
          \___ ...
          \___ forms.py
```

b. Add the code below to the file:

```
from django import forms
from .models import Uploads

class FileUploadForm(forms.ModelForm):
   class Meta:
       model = Uploads
       fields = "__all__"
```
The code above creates a `ModelForm `based on the structure of the Uploads model. The `__all__ ` method means that the form should contain all the fields in the database model.


## Create a view for media uploads

Follow the steps below to create a view function:

a. Open the views.py file
b. Add the code below

```
from django.shortcuts import render, redirect
from .forms import FileUploadForm
from .models import Uploads


# Create your views here.

def handle_upload(request):
   form = FileUploadForm()
   if request.method == "POST":
       form = FileUploadForm(request.POST, request.FILES)
       if form.is_valid():
           form.save()

   return render(request, "form.html", context={"form": form})
```

The view function renders the ModelForm through a `form.html` file that will be created in the next step. 

This view also accepts POST requests from the ModelForm when the user submits their uploads. It verifies that there are no form errors before saving the user input. 

It is necessary to include the `request.FILES` and `request.POST `dictionary in the form class for each POST request. 

POST requests are usually sent with a `requests.POST` dictionary. When files are involved, a `request.FILES` dictionary is added. 

Uploaded files are stored temporarily in a `request.FILES` dictionary. Each key in this dictionary holds the data of a particular file field in the form submitted.

For example, if you created a form with a FileField named "images", you can access the file data in your views function via the code below:

```
request.FILES['images']
```


## Create the homepage file

You need to create an HTML file named `form.html` that'll render the form. Follow the steps below to do create a web page:

a. Create a new folder named templates in your project

```
my_project
   \___ my_project
   \___ ...
   \___ templates
   \___ ...

```

b. Create a new HTML file named `form.html` inside this folder
c. Add the code below inside this file

```
<head>
   <meta charset="UTF-8">
   <title>Forms practise</title>
</head>


<body>

<form method="post" action="" enctype="multipart/form-data">
   {% csrf_token %}

   {{ form.as_p }}
   <input type="submit" name="submit">

</form>

</body> 
```

The HTML code above displays the form to the user through `form.as_p`, while the `{% csrf_token %}` enables secured transfer of form data

The `enctype="multipart/form-data"` variable enables the form to send file data.

Finally, you need to register the templates folder with Django so it can access it. 

To do this, open the `settings.py` file in your project folder and add the code below to the `TEMPLATES `list in this file:

```
TEMPLATES = [
    {
        '...',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        '...'
    }
]
```


## Configure app urls.

The next step is to bind the view function to a url. Follow the steps below to do this:

a. Open the urls.py file in your app folder

```
my_project
   \___ file_upload
      \___ ...
      \___ urls.py
      \___ ...
```
b. Add the code below to this file

```
from django.urls import path
from file_upload import views

urlpatterns = [path("", views.handle_upload, name="home")]
```
c. Open the urls.py file in your project folder

```
my_project
  \___ my_project
     \___ ...
     \___ urls.py
     \___ ...

```
d. Register your app URLs to the urlpatterns list in your project.

```
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("", include("file_upload.urls")),
]
```


## Test the app

a. Run the command below in your terminal

```
python manage.py runserver
```
b. Click on the link to open the web page
c. Click on the **_Choose file _**button to upload your files.


![Web page showing how to upload files in Django](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ccqlj81cn2x8fwsb379u.png)

c. Enter a caption of your choice.
d. Submit the form.
e. Proceed to the `upload `folder, you would see two new folders named `files `and `images`.

In the files folder, you would see the document you uploaded.
In the images folder, you would see the image file you uploaded.


## Display user uploads

In most cases, users would need access to their files. You can display media uploads to users using a simple `.url` attribute. The steps below show you how to display uploaded files to users:

a. Open the views.py file
b. Update the view function to contain the `Uploads` model and pass it to the render function.

```
from django.shortcuts import render, redirect
from .forms import FileUploadForm
from .models import Uploads

# Create your views here.
def handle_upload(request):
   form = FileUploadForm()
   uploads = Uploads.objects.all()
   if request.method == "POST":
       form = FileUploadForm(request.POST, request.FILES)
       if form.is_valid():
           form.save()
           return redirect("home")

   return render(request, "form.html", context={"form": form, "uploads": uploads})
```

c. Proceed to the `form.html` file
d. Add this code below the form in the HTML file

```
{% for media in uploads %}
{% if media %}

<div style="margin: 20px 0px;">

   <img src="{{ media.image_field.url }}" height="200px;" width="200px;">
   <p>{{ media.caption }}</p>
   <p>Download your file <a href="{{ media.file_field.url }}">here</a></p>

</div>

{% endif %}
{% endfor %}
```
e. Wait for the server to reload
f. Open the browser and refresh the web page

This process simply queries all records in the Uploads model, adds it to the context dictionary so it can be accessed and used in the HTML file.

The image you uploaded will be displayed on the web page and your document will be available for download. Here is an example below:


![An image displaying user uploads in Django](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4enm63sl7y3cb7wruz6k.png)


## Conclusion

Leveraging Django's ModelForms for file uploads simplifies the process of handling user-submitted files. Whether it's for pictures, documents or any other type of file, ModelForms provides an efficient and structured way to manage file uploads in Django.


## Frequently asked questions (FAQs)

#### What type of media files can be uploaded in Django?
You can upload files of different sizes and formats in Django. However, if you are using a local file-storage, you will get to a point where the memory gets full and you will need to use a cloud service like AWS, Google clouds, etc.

An overloaded memory will affect site performance.


#### How can I secure media uploads in Django?
There are different security measures you can apply to media uploads in Django. They include:

1. Verifying the file formats before saving them. That is, by checking the file extensions, such as .png, .jpg, .pdf, etc. to be sure they are safe
2.  Sometimes, reading file metadata before saving is also important. However, you may need to use third-party Python libraries to like Pillow to implement this. 


#### How to handle media uploads in a production environment?
It is not advisable to use a local file storage in a production environment. This is because such implementation may have security implications on stored files.

Using cloud services enhance application performance and provide more flexibility and security to uploaded files.


#### Can I resize or manipulate uploaded images in Django?
Yes, you can. 

Before saving an uploaded file, you can get the file object from the `request.FILES` dictionary and use it directly with a file manipulation library like Pillow, OpenCV, etc. in Python.

After editing the image, you can then save it to a file of your choice. 


#### How does the Django Filesystem work?
When a user uploads a file in Django, the Django [filesystem ](https://docs.djangoproject.com/en/5.0/ref/files/)has two handlers that handles file uploads. For small files, they are saved to memory using the (handler) handler while the (handler) handler handles large files by saving them to a temporary file. 


#### What happens when I delete saved files in Django?
Deleted files are immediately removed from the MEDIA ROOT of your app. Therefore, trying to access a deleted file through the `.url` attribute will throw an error.