Background:
I'm starting to use Django for the first time, which is also my first foray into web development. I just got stuck on the whole "serving static media" problem. After spending a while looking at all the documentation and StackOverflow questions, I think I understand how it's supposed to work (i.e. MEDIA_ROOT, MEDIA_URL, updating the urls file, etc).
My Question:
Ok, so here's the part I'm not sure about. Django applications are supposed to be "pluggable", i.e. I can move an application from one project to another. So, how should these applications bundle static media?
For example, let's say I have a "foo" application, which has templates that load some css/image files. Where am I supposed to put these files, so that they'll automatically get served once I include the application?
The only solution I see, is that installing an application has to include the extra step of copying its static media to some place on your own server that serves that media.
Is this the accepted way to do it? It includes an extra step, but maybe that's standard when dealing with web-dev (I'm new so I don't really know).
Also, if this is the way, is there a standard way to collect all my static media to make it easy to know what I need to serve? (I.e., is it standard to have a folder named "media" or something inside the app?).
Thanks,
Convention is to put static media in either media/appname/ or static/appname/ within the app (similar to templates).
For using apps in your project that come with media, I strongly recommend using django-staticfiles. It will automatically serve media (including media within apps) in development through a view that replaces django.views.static.serve, and it comes with a build_static management command that will copy media from all apps into a single directory for serving in production.
Update: django-staticfiles has become part of Django 1.3. It now expects app media to live in a "static/" subdirectory of the app, not "media/". And the management command is now "collectstatic."
The only app I know of that deals with this without any intervention is the rather wonderful django-debug-toolbar, though it's arguable that this isn't a great example, since it's an app specifically designed for debug mode only.
The way it deals with it is that it serves its media through Django itself - see the source for urls.py:
url(r'^%s/m/(.*)$' % _PREFIX, 'debug_toolbar.views.debug_media'),
In general, this is a bad idea (you don't want to serve static files through Django), per this comment from the documentation:
[Serving static files through Django] is inefficient and
insecure. Do not use this in a
production setting. Use this only for
development.
Obviously, the django-debug-toolbar is only used for development, so I think its method of deployment makes sense, but this is very much an exception.
In general, the best way I know to do it is to create symbolic links wherever your media is stored to the media inside your app code. For example, create a folder called media within your app, and then require users installing your app to either add a symbolic link from their media directory, or copy the whole thing.
i usually put apps media in ./apps/appname/static (my apps resides in an apps subfolder)
then i have something similar in the vhost in apache :
AliasMatch ^/apps/([^/]+)/static/(.*) /home/django/projectname/apps/$1/static/$2
<DirectoryMatch "^/home/django/projectname/apps/([^/]+)/static/*">
Order deny,allow
Options -Indexes
deny from all
Options +FollowSymLinks
<FilesMatch "\.(flv|gif|jpg|jpeg|png|ico|swf|js|css|pdf|txt|htm|html|json)$">
allow from all
</FilesMatch>
</DirectoryMatch>
i also have this in my urls.py for dev server (use only for debug) :
def statics_wrapper(request, **dict):
from django.views import static
return static.serve(request, dict['path'], document_root = os.path.join(settings.BASE_DIR, 'apps', dict['app'], 'static'), show_indexes=True)
urlpatterns += patterns('', (r'^apps/(?P<app>[^/]+)/static/(?P<path>.+)$', statics_wrapper))
this is very handy because statics url are simply mapped to filesystem, eg :
http://wwww.ecample.com/apps/calendar/static/js/calendar.js resides in [BASE_DIR]/apps/calendar/static/js/calendar.js
hope this helps
Related
Django currently has a complete system for routing urls.
But I have a very specific situation where I am using django but actually need to use urls like in classic PHP language.
For example:
The url - localhost/reader/theeffort should take me to a folder called theeffort where I have my files index.html, 1.html, 2.html, 3.html
and so on!
Now all these files should be accessible by localhost/reader/theeffort/*.html and not by Django's default url system. Is this possible to achieve that? If yes, how?
This isn't a thing you would do with Django's URLs. If you just want to serve HTML files within a folder, they are static files; they should therefore be served by the web server itself, eg Apache. You just need to configure an alias in the Apache conf to point to the folder where the static files are.
I deployed a Django app using Apache, and I check for authentication in most views using a decorator.
#custom_decorator
def myView(request):
bla bla bla...
It's not the #login_required decorator that comes with Django, but it's almost the same thing, except that allows access only to users from certain groups. This works as intended.
Also, I'm serving media (user uploaded) files with Apache, like this:
Alias /media /path/to/media
<Directory /path/to/media>
Require all granted
</Directory
I can access the media files just fine, but the problem is that I can access them even if I'm not logged in, simply by typing the url manually, like:
mySite/media/myFile.png
Is there a way to limit access to the media files, hopefully using the custom decorator?
I stumbled across a similar question: How do you Require Login for Media Files in Django, but unfortunately the answer went way over my head.
Thanks in advance!
When you mention media path to the apache, those files are served directly by Apache (or Nginx or any other web server). Those requests do not even goes through your Django application. Hence you do not have a control over those requests or the data served by them.
One way is to create your separate API to serve the static/media files. In that API, use the same validation that you do for other content.
Even better, if you have AWS (Amazon Web Services) or GCP (Google Cloud Platform) account, store the static files on the S3 or Cloud Storage respectively and serve their URL of files via your API.
PS: Do not forget to remove the media path from the Apache configuration. Else, Apache will keeps on serving those file.
Alternatively, as mentioned in Sarafeim's answer to Restricting access to private file downloads in Django which requires modification in both sever and application side. You need a way for your HTTP server to ask the application server if it is ok to serve a file to a specific user requesting it. You may achieve this using django-sendfile which uses the X-SendFile mechanism. As per the django-sendfile's README:
This is a wrapper around web-server specific methods for sending files to web clients. This is useful when Django needs to check permissions associated files, but does not want to serve the actual bytes of the file itself. i.e. as serving large files is not what Django is made for.
To understand more about the sendfile mechanism, read: Django - Understanding X-Sendfile
Okay, so based on #MoinuddinQuadri answer and links, it seems that the easiest solution is to serve the files using a regular Django view, and apply the desired decorator, like this:
#custom_decorator
viewFile(request, objectID):
object = MyModel.object.get(id = objectID)
return HttpResponse(object.file, content_type = "image/png")
(In my case, I wanted to serve a FileField related to a Model, so in the view I pass the ID of the object instead of the file name).
Also, I commented out the corresponding code in the Apache conf:
### Alias /media /path/to/media
### <Directory /path/to/media>
### Require all granted
###</Directory
I had to change some templates to use the new view instead of the URL of the media file, but now it works as intended, locking out non-logged users.
However, this no longer uses Apache to serve the files, it uses Django itself, which according to the docs, is inneficient and not recommended.
Ideally you want to still serve the files using Apache and just use the view to protect its access, and for that you can use mod_xsendfile for Apache, or simply use Django Sendfile, which is a wrapper for the module just mentioned.
I tried the latter, but unfortunately it has problems with file names that have non-ascii characters. As my target are spanish-speaking users, I had to resort of just serving the files with Django, at least for now.
I used solution #1 in the post "Django protected media files". There are two other solutions described here as well: "Unpredictable Urls" and "X-Sendfile", but the one I'm describing was my choice.
As #Sauvent mentioned, this causes the files to be served by Django and not by a web server (e.g. Apache). But it's quick and easy if you're not dealing with a lot of traffic or large files.
Basically, add the following to your urls.py:
#login_required
def protected_serve(request, path, document_root=None, show_indexes=False):
return serve(request, path, document_root, show_indexes)
urlpatterns = patterns('',
url(r'^{}(?P<path>.*)$'.format(settings.MEDIA_URL[1:]), protected_serve, {'document_root': settings.MEDIA_ROOT}),
)
In my case I edited it to the following because my directories are set up differently and I use Login Required Middleware to ensure login is required everywhere (Django: How can I apply the login_required decorator to my entire site (excluding static media)?:
urlpatterns = patterns('',
url(r'^media/(?P<path>.*)$', "django.views.static.serve", {'document_root': settings.MEDIA_ROOT}),
)
I use django and my application works, when I change the style.css the app changes etc.
However, I recently found out that I didn't define STATICFILES_DIR, I only defined
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
Which is only for collectstatic.
But now I'm wondering, how does django know where to look for the static files?
Specifically in the polls/static/polls directory?
Django will look for a directory named static in each of your apps by default. More on this later.
From the documentation:
Now we might be able to get away with putting our static files
directly in my_app/static/ (rather than creating another my_app
subdirectory), but it would actually be a bad idea. Django will use
the first static file it finds whose name matches, and if you had a
static file with the same name in a different application, Django
would be unable to distinguish between them.
The key part being
...Django will use the first static file it finds whose name matches...
There are Django libraries called "static file finders" that help with this process. You can configure them by modifying the installed apps in your Django configuration file.
According to the documentation, they're actually undocumented, because they are considered private. However, you can look at the source code for those "finders" on GitHub.
Django projects are composed of modules called apps. Each app contains some default files when you generate it. Although there is configuration, there's plenty of convention about Django too. For example, the views.py file. Another such convention is your app's static directories folder.
Your STATICFILES_DIR array allows you to add additional places for you to put your static files. This is useful, as you'll see when you prepare to deploy, because of the way Django handles static files in a real-world environment.
It's totally optional, but it's there to make your life easier later.
I'm building an application in Pyramid and utilizing Jinja2 templates and traversal routing. In order to wire my view-callables with the templates I am using, I want to be able to reference my templates using the webapp:templates prefix. As an example:
#view_config(name='about-us', renderer='webapp:templates/pages/about-us.html', context=Root)
def static_pages(context, request):
... //more code
This decouples where the templates live from whats using them. In order to make the above functional, though, I had to put this inside the __init__.py in my webapp root folder:
config.add_static_view(name='templates', path='webapp:templates', cache_max_age=3600)
The add_static_view() causes the webapp/templates folder to be referenced as webapp:template in other configurations. However, it also makes it viewable from a url such as http://0.0.0.0:6543/templates/<some template file>. Is there a way to achieve the former goal without allowing the latter visibility as a static page?
add_static_view() is not supposed to cause the webapp/templates folder to be referenced as webapp:template in other configurations, if it does that it's just due to a weird side-effect.
The package:path syntax works because Pyramid uses pkg_resources API to resolve the paths. Here are some details.
This means that, in your example, webapp should be a python package located somewhere your app can find it.
I've recently have taken a new server under my wing that has an interesting installation of django on it. The previous developer mixed in the media uploads with the static content and in other modules created it's own directory on the root file level of the project. My first reaction to this was general annoyance. ( I'm a huge fan of modular development. ) However after working to 'correct,' it's raised a question.
Even though this question is tagged with django, feel free to post response according to java and asp.net.
How do you set up your static files? Do you stack everything inside a static directory or do you take the time link each modular independently?
One of my tricks for every django app I start is, in the init.py of said app I put the following.
import os
from django.conf import settings as djsettings
TEMPLATES_DIR = (os.path.join(os.path.dirname(__file__),'./templates'),)
djsettings.TEMPLATES_DIR += TEMPLATES_DIR
I don't think your trick is really needed (anymore).
If you use django.template.loaders.app_directories.Loader (docs)
you can put a template dir in your app dir and Django will check it for your app-specific templates
And starting with Django 1.3 you can use the staticfiles app to do something similar with all your static media files. Check the docs for the staticfiles-finders
So finally, thanks to the collectstatic management command, you're now able to keep your static media files modularized (for easier development and distribution), yet you still can bundle them at a centralized place once it is time to deploy and serve your project.
Right now I'm in the habit of putting a static folder in each app directory containing its static files. I keep templates in each app directory under templates. I alias the static path when putting the app behind nginx or apache.
One thing that I'm starting to do more of is putting static files such as javascript, css, or images behind a CDN like S3.