Django: difference between MEDIA_ROOT and STATIC_ROOT
Tags: django
Published: 2024-07-10 09:10:47

## Introduction 
### `STATIC_ROOT` vs. `MEDIA_ROOT`

In web development, particularly when working with Django, managing assets such as images, videos, CSS, and JavaScript files is a critical component of building a robust and efficient web application. Two key settings in Django, `MEDIA_ROOT` and `STATIC_ROOT`, that often cause confusion among developers, especially those new to the framework. Despite their seemingly similar roles in handling files, these settings serve distinct purposes and are configured differently within a Django project. Understanding the differences between `MEDIA_ROOT` and `STATIC_ROOT` is essential for organizing and deploying your web application's static and media content effectively. In this discussion, we will delve into the definitions, uses, and distinctions of `MEDIA_ROOT` and `STATIC_ROOT`, providing clarity on how each contributes to the file management strategy of a Django project.


- **`STATIC_ROOT`**:
  - **Purpose**: It is the directory where all static files are collected (using the `collectstatic` management command: `python manage.py collectstatic`) for deployment.
  - **Use Case**: Static files are typically assets like CSS, JavaScript, and images that are part of your application's front-end and do not change frequently.
  - **Settings**:
    ```python
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
    ```

- **`MEDIA_ROOT`**:
  - **Purpose**: It is the directory where user-uploaded files are stored. This includes files uploaded via forms, such as profile pictures or documents.
  - **Use Case**: Media files are usually content that can change, such as user uploads or files generated by the application.
  - **Settings**:
    ```python
    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    ```

### Key Differences

1. **Nature of Files**:
   - `STATIC_ROOT`: Contains static assets that are part of your codebase and are typically versioned with your application.
   - `MEDIA_ROOT`: Contains media files uploaded by users or dynamically generated by the application.

2. **File Management**:
   - `STATIC_ROOT`: Files are collected here during deployment using `collectstatic`.
   - `MEDIA_ROOT`: Files are stored directly when uploaded, without needing a collection step.

3. **URL Serving**:
   - `STATIC_URL`: URL prefix for accessing static files.
   - `MEDIA_URL`: URL prefix for accessing media files.

4. **Access Control**:
   - `STATIC_ROOT`: Usually public and accessible to all users.
   - `MEDIA_ROOT`: Access can be restricted and managed more granularly depending on the application needs.

### Using `upload_to` with `MEDIA_ROOT`

When you specify `upload_to` in a model field, it creates a subdirectory in the `MEDIA_ROOT` where the file will be stored.

#### Example with Model

```python
#models.py

from django.db import models

class Picture(models.Model):
    photo = models.ImageField(upload_to='images/')
```

### Settings Example

Here’s an example of how you might set up `MEDIA_ROOT` in your `settings.py`:

```python
# settings.py

import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
```

In this setup:
- `MEDIA_ROOT` points to the `media` directory in your project’s base directory.
- `upload_to='images/'` means the file will be saved in `media/images/`.

### How it Works with File Uploads

1. **Saving a File**:
   When a user uploads a file, Django will save it in the `MEDIA_ROOT` directory, under the subdirectory specified by `upload_to`.

2. **Path Example**:
   If a file named `example.jpg` is uploaded through a model field with `upload_to='images/'`, the file path would be:
   ```
   media/images/example.jpg
   ```

### Serving Media Files

In **development**, Django can serve media files using the following settings in your `urls.py`:

```python
# urls.py

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

urlpatterns = [
    # Your URL patterns...
]

if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```

In **production**, you typically configure your web server (e.g., Nginx or Apache) to serve files from `MEDIA_ROOT`.


### Recommended Structure

It’s typically recommended to separate media files from static files:

- **Static Files**:
  - Directory: `static/`
  - Path: `STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')`

- **Media Files**:
  - Directory: `media/`
  - Path: `MEDIA_ROOT = os.path.join(BASE_DIR, 'media')`

This helps maintain a clean and manageable structure for your project’s file assets.


### Directory Structure Example

```
# project tree

myproject/
│
├── static/
│   └── staticfiles/  # Collected static files (after running `collectstatic`)
│
├── media/
│   └── images/  # Uploaded media files (via `MEDIA_ROOT`)
│
├── manage.py
├── myproject/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
|
├── app/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── views.py
│   └── templates/
│       └── projects.html
```


### Example Model with Media Files

Assuming you have the following models:

```python
# models.py

from django.db import models

class Project(models.Model):
    title = models.CharField(max_length=200)
    category = models.CharField(max_length=100)
    # Other fields...

class Picture(models.Model):
    project = models.ForeignKey(Project, related_name='pictures', on_delete=models.CASCADE)
    photo = models.ImageField(upload_to='images/')
    description = models.TextField()
    # Other fields...
```


### Create a Class-Based-View (CBV) for the template

```python
#views.py

from django.views.generic import ListView

from app.models import Project


class PortfolioView(ListView):
    model = Project
    template_name = "app/projects.html"  # default Django name: project_list.html
    context_object_name = "projects"
```


### Using the Template to Display Media Files

Here’s how you can update your template to display the media files:

```html
# projects.html

{% for project in projects %}
  <div>
    <h4>{{ project.title }}</h4>
    <p>{{ project.category }}</p>
    {% for picture in project.pictures.all %}
      <img src="{{ picture.photo.url }}" alt="{{ project.title }}">
    {% endfor %}
  </div>
{% endfor %}
```

In this setup:
- You access the media file URL in the template using `{{ picture.photo.url }}`, which Django handles based on the `MEDIA_URL`.


### Final Considerations

1. **Development vs. Production**:
   - During development, Django can serve static and media files directly.
   - In production, it’s recommended to use a web server (like Nginx or Apache) to serve static and media files.

2. **Security**:
   - Ensure that media files are served securely, especially if they contain sensitive information.
   - You may need to set appropriate permissions and access controls based on your application’s requirements.

By following these guidelines, you can efficiently manage and serve static and media files in your Django project.