Django upload_to outside of MEDIA_ROOT - python

My deployment script overwrites the media and source directories which means I have to move the uploads directory out of the media directory, and replace it after the upload has been extracted.
How can I instruct django to upload to /uploads/ instead of /media/?
So far I keep getting django Suspicious Operation errors! :(
I suppose another solution might be a symlink?
Many thanks,
Toby.

I did the following:
from django.core.files.storage import FileSystemStorage
upload_storage = FileSystemStorage(location=UPLOAD_ROOT, base_url='/uploads')
image = models.ImageField(upload_to='/images', storage=upload_storage)
UPLOAD_ROOT is defined in my settings.py file: /foo/bar/webfolder/uploads

While the accepted answer is probably what you want, we now have the option with django 3.1 that we can decide which storage to use at runtime by passing a function to the storage argument of an ImageField or FileField.
def select_storage():
return MyLocalStorage() if settings.DEBUG else MyRemoteStorage()
class MyModel(models.Model):
my_file = models.FileField(storage=select_storage)
Have a look at the official docs.
Please note that the current accepted answer writes the actual value of the variable UPLOAD_ROOT to the migration file. This doesn't produce any SQL when you apply it to the database but may be confusing if you change that setting frequently.

Related

Django FileField Model that doesn't upload but returns path of the file

I am using Django to build an app where I ask the user to choose some files from his/her computer and the app will process them and write an output file in the same directory.
If I use the FileField model in Django, it uploads the file to the MEDIA_ROOT directory. That's not what I want. I would like the file to remain where it is and I need the path to the file where it is instead. I have been looking at the FileField code:
https://docs.djangoproject.com/en/1.11/_modules/django/db/models/fields/files/#FieldFile
Does anyone know of a hack around this where the upload_to is disabled and the storage method that is being used returns the path instead? From what I could make out, the storage method reads the file and writes it into the upload_to directory. The FileField class uses the FieldFile class and the FileDescriptor class. Does anyone know which method actually performs the read and therefore has the actual path of the file? Been trying to read the code but I am not at an advanced stage yet and I can't understand all of it.
Thanks in advance.

Deliver images in Django from a different path than from its ImageField.url property

I'm porting a web app from PHP (Codeigniter) to Python (powered by Django.) I already migrated the database and am now using Django models. I populated the database with images using Django's ImageField field and the images are in its proper folders under MEDIA_ROOT.
I'm in development so I'm using Django's server to test the web app as I go and I can serve the images through image.url.
image.url serves the image from:
MEDIA_ROOT/folder/subfolder/numberImage_folder_subfolder.jpg
But I need it to be served from MEDIA_ROOT/numberImage_folder_subfolder.jpg.
In production I know I can serve the image using nginx's XSendFile or something like that. Django's server (runserver command) is very easy to use and gives me what I need at the moment. While in development, how can I deliver the image from the URL I require? Preferably using Django's server...
Any help will be so much appreciated.
I'm not entirely clear on what your URLs are and how they map to file paths (and don't have the reputation to ask for clarification yet). So correct me if I've understood it wrong.
But in the meantime, to rephrase the question slightly, I think you're saying that you have the files stored on disk in those subfolders like MEDIA_ROOT/folder/subfolder/numberImage_folder_subfolder.jpg, but when you actually want the URLs served, you don't want the folders in the URL, so the URL will have MEDIA_ROOT/numberImage_folder_subfolder.jpg? And only with the ./manage.py runserver?
Going ahead on those assumptions, we have two steps: map the URLs we want back to a file path, and then also make sure we're generating the URLs we want.
For the first, normally urls.py will have something like this to serve media files in development:
from django.conf.urls.static import static
if settings.DEBUG is True:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
which is basically adding url(r'^media/(?P<path>.*)$', django.views.static.serve) to your urlconf, where serve takes all paths below settings.MEDIA_URL and serves up the equivalent file under MEDIA_ROOT.
The simplest way to do this would be to create a custom function modelled on that same django.conf.urls.static which takes the path and adds the folders back in before passing it on to django.views.static.serve.
For the second, if you want the URLs generated from the ImageField.url to differ from the underlying file storage path, you'll need to have a custom ImageField subclass overriding the FileField.url property (or a custom Storage class that does a similar thing).
Like so:
class CustomURLImageField(django.forms.fields.ImageField):
def _get_url(self):
"""
Override the standard FileField.url property to mangle the URL from
MEDIA_ROOT/folder/subfolder/numberImage.jpg
to
MEDIA_ROOT/numberImage_folder_subfolder.jpg
"""
self._require_file()
#return self.storage.url(self.name)
path_components = self.name.split('/')
folder = path_components[-3]
subfolder = path_components[-2]
numberImage, ext = os.path.splitext(path_components[-1])
return '/{numberImage}_{folder}_{subfolder}{ext}'.format(**locals())
url = property(_get_url)
This will be likely true in production too... how are you generating your URLs?)

django-storages with Amazon S3 - prevent overwriting

I noticed that django-storages (or perhaps it's Django's storage API itself) overwrites files with the same name. This is a problem for me as my site allows user uploads, so I need to ensure that files are never overwritten.
Ideally I'd like to be able to pass a file name to the storage backend from the view level, but I'm struggling to find an elegant way to do this. I'd be equally happy if there's a switch somewhere where I can just do something like overwrite=False and have the backend come up with its own alternative name.
If you are using the s3boto backend not the old s3 backend in django-storages then you can change this using the AWS_S3_FILE_OVERWRITE setting: https://bitbucket.org/david/django-storages/src/83fa2f0ba20c/storages/backends/s3boto.py#cl-43
#Mark Lavin's answer aptly points out that setting AWS_S3_FILE_OVERWRITE to False avoids this problem.
You may additionally want to improve your file name-spacing a little bit. You can save files under whatever name on S3 you want (it doesn't have to be the name of the file the user uploaded). So you could save your file with the name "user_uploads/[user_id]/[user_generated_file_name]". You can also set the file name to be whatever you want as part of a download. If you save the user's uploaded file name as a field on your model, you can then specify that as the file name in the view that downloads a file.

Django - How to share configuration constants within an app?

It is sometimes beneficial to share certain constants between various code files in a django application.
Examples:
- Name or location of dump file used in various modules\commands etc
- Debug mode on\off for the entire app
- Site specific configuration
What would be the elegant\pythonic way of doing this?
There's already a project-wide settings.py file. This is the perfect place to put your own custom setttings.
you can provide settings in your settings.py like
MY_SETTING = 'value'
and in any module you can fetch it like
from django.conf import settings
settings.MY_SETTING
Create a configuration module.
Configuration.py: (in your project/app source directory)
MYCONST1 = 1
MYCONST2 = "rabbit"
Import it from other source files:
from Configuration import MYCONST1,MYCONST2
...
Django apps are meant to be (more or less) pluggable. Therefore, you are not supposed to hack into the code of an app in order to parametrize what you want (it would be quite a mess if you had to do this ! Imagine you want to upgrade an app you downloaded on internet... you would have to re-hack into the code of the new version ?!?).
For this reason you shouldn't add app-level settings at the app level, but rather put them together somewhere in your project-wide settings.py.

What does Django do with `MEDIA_ROOT`?

What does Django do with MEDIA_ROOT exactly? I never understood it. Since Django itself doesn't serve static media, and you have to set up apache or something similar for it, why does it care in which directory it sits?
You're not the only one who wonders; check out Django ticket #10650. Based on the comments by Django developers there, I think this pretty much sums up what MEDIA_ROOT is used for:
Unfortunately, Django is also at fault for being far too vague in its docs about what
MEDIA_ROOT and MEDIA_URL are used for. Searching through Django's code confirms that
MEDIA_ROOT & MEDIA_URL are used for file upload, and that MEDIA_URL is provided as a
convenience to templates via the default context processors (available when using
RequestContext??).
It appears to be used by some classes in Django as a default -- for instance, FileSystemStorage uses it as the default place to store files.
When you creates a model with ImageField or FileField attributes, you should pass the upload_to argument. That is a relative path will be appended to your MEDIA_ROOT path and there will be save and retrieve that files.

Categories

Resources