First, we add random UUID before the filename when uploading a new file. It will make URL "secured" which means that only someone who will know URL will be able to get the file. One another benefit here - this will prevent renaming files with the same names. Also, we group files into directories by year, month, and date to prevent lagging when we have too many files in one directory:

def file_upload_path(instance, filename):
    shortuuid.set_alphabet(ascii_lowercase + digits)  # will generate 25-char uuids
    return timezone.now().strftime('uploads/%Y/%m/%d/{0}-{1}').format(shortuuid.uuid(), filename)

class File(Model):
    file = FileField(upload_to=file_upload_path)

Note that we use shortuuid python module here which allows to generate shorter UUID then default uuid.uuid4 function (becouse default uses smaler set of chars and several hyphens).

But such a way will have some negative effect - users will download files with modified names which is not cool. To solve this in most performant way we can add some magic header to response which will cut off UUID from filename when user will request file. To do it in nginx proxy we can use:

location ~ "^/media/(?<path>uploads/\d+?/\d+?/\d+?/[\da-z]{25}-(?<name>.*\.(jpg|gif|png|svg)))$"  {
    alias /path/to/media/$path;
   add_header Content-Disposition 'inline; filename="$name"';
}

location ~ "^/media/(?<path>uploads/\d+?/\d+?/\d+?/[\da-z]{25}-(?<name>.*))$"  {
    alias /path/to/media/$path;
    add_header Content-Disposition 'attachment; filename="$name"';
}

We use two locations here because we want to give images in inline mode (if the user will click Open in new tab file will be opened in the browser tab). But other files we will give in attachment mode and when the user tries to follow anchors to these files he will see a save dialog or downloading process.