Pugetworks
Seattle Software Developers

blog-archive

Pugetworks Blog Archive!

Django Tips: FileField and uploadto

The following tip, should help in understanding how the FileField handles the path and url for a file referenced in the database.

The Model Object

The FileField model in Django is used to store files in Django. The file itself is not stored in the database but on the filesystem, with a reference to the file location saved in the database. Once saved, the file can be easily accessed with 2 functions:

  • document.path - returns the absolute location to the file in relation to the filesystem
  • document.url - returns the absolute location to the file to be used in a web browser

Settings

The Django docs reference 2 settings variables that should be set:

  • MEDIA_ROOT - Absolute path to the directory that holds media...
  • MEDIA_URL - URL that handles the media served from MEDIA_ROOT

Now, I don't like to expose my filesystem structure to the internet, so I set MEDIA_URL to a specific directory. Then use apache (or a dedicated url configuration) to serve the files. For example: [python]MEDIA_URL='/media'[/python]

I also like to isolate my media so that there can be no conflicts, so I use 2 settings variable to store the media root: [python]MEDIA_ROOT = os.path.join( os.path.dirname(__file__), 'media/').replace('\\','/') MODEL_DOC_ROOT = os.path.join( 'model', 'documents' ).replace('\\','/')[/python]

The url configuration is: [python]( r'^model/documents/(?P<path>.*)$', 'django.views.static.serve', { 'document_root': '%s/%s' % ( settings.MEDIA_ROOT, settings.MODEL_DOC_ROOT ) } ),[/python]

uploadto

Default

The Django docs for FileField say:

A local filesystem path that will be appended to your MEDIA_ROOT setting to determine the value of the url attribute.

This is important, because if you include the complete path, then document.path will be correct, but document.url will have the complete filesystem path in the url...ack!

As a function

The Django docs also say:

This may also be a callable, such as a function, which will be called to obtain the upload path, including the filename. This callable must be able to accept two arguments, and return a Unix-style path (with forward slashes) to be passed along to the storage system.

If I expect many files to be uploaded, it is cleaner to split them into subdirectories. My function turned out like the following: [python] def doc_location( instance, filename ): root_path = path.join( settings.MODEL_DOC_ROOT, instance.name.slug ).replace('\\','/') full_path = settings.MEDIA_ROOT + path.join( '/', root_path ).replace('\\','/')

if not path.exists( full_path ): mkdir( full_path )

return path.join( root_path, filename ).replace('\\','/') [/python]

Obscure Pitfalls

The above works quite well.... but what the docs don't say is:

  • If MODEL_DOC_ROOT starts with '/' then MEDIA_ROOT is NOT appended as indicated the docs!
  • In the doc_location function, if settings.MEDIA_ROOT is included in path.join it doesn't get added!?
DjangoBill