Django save files from FileField in docker container

Viewed 1043

I have a model with a FileField. My application is running in a docker container created with a dockerfile. When I save a file it is saved on my local path instead of the containers base root. But when I try to retrieve the file and send it as attachment in an email I get a file not found error. The media root directory is based on the Dockerfile's base root. The email is built and sent in another container as an async task. The Dockerfile looks like this:

# pull official base image
FROM python:3.9.1-alpine

# set work directory
#ENV APP_HOME=/code
#RUN mkdir $APP_HOME
#RUN mkdir $APP_HOME/static
#RUN mkdir $APP_HOME/media
#WORKDIR $APP_HOME
WORKDIR /code

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

COPY requirements.txt /code/

RUN apk --update --upgrade --no-cache add \
    postgresql-libs make gdal-dev geos-dev g++ python3-dev cairo-dev pango-dev gdk-pixbuf-dev ttf-ubuntu-font-family

RUN \
    apk add --virtual .build-deps gcc musl-dev postgresql-dev jpeg-dev zlib-dev libffi-dev && \
    pip3 install --no-cache-dir -r /code/requirements.txt && \
    apk --purge del .build-deps

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

# copy project
COPY . /code/

I have a docker-compose file to build the image and run the container. It looks like that:

version: "3.9"

services:
  db:
    image: postgis/postgis:13-3.0-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    # TODO remove ports for production
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_DB=DB
      - POSTGRES_USER=USER
      - POSTGRES_PASSWORD=PW
  api:
    build: .
    image: app/api
    command: python manage.py runserver 0.0.0.0:8000 --settings=api.settings.production
    volumes:
      - .:/code
      - static:/code/static
      - media:/code/media
    ports:
      - "8000:8000"
    env_file:
      - ./.env.dev
    depends_on:
      - db
    entrypoint: /entrypoint.sh
  mail:
    image: app/api
    command: python manage.py qcluster --settings=api.settings.production
    depends_on:
      - api

volumes:
  postgres_data:
  static:
  media:

An example model looks like this:

def order_nested_item_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/orders/user_<id>/<filename>
    user_id = instance.order.user.id if instance.order.user else 'anonymous'
    return f'orders/user_{user_id}/order_{instance.order.id}/{filename}'

class Invoice(TimeStampedModel):
    order = models.ForeignKey(Order, related_name='invoices', on_delete=models.CASCADE)
    number = models.CharField(max_length=100)
    pdf = models.FileField(upload_to=order_nested_item_directory_path)

with media root settings:

BASE_DIR = Path(__file__).resolve().parent.parent.parent
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Anyone can help how to save the files in the Dockerfile's base root?

1 Answers

I'm not sure it will solve your problem but if you want to share the data from a named volume between containers using docker-compose, you need to add these volumes to all these containers or use extension fields.

As the first thing is easier, just add

    volumes:
      - media:/code/media

to your mail service in docker-compose.

After that rebuild your container.

Then, when it's running, run docker exec -it <image id> sh and check if /code/media is there.

Related