Django - admin_media_prefix access from javascript - django

In the admin, I would like the admin media url to be accessible from any javascript.
I already have it included in the request context.
But in order to be able to access it from an included javascript,
<script type="text/javascript">
window.__admin_media_prefix__ = "{{ ADMIN_MEDIA_URL }}";
</script>
Do I have to put something like that in a base template or is there a cleaner way to do that ?

Media (static) files are not interpreted by django in any way. On a production site python code might not even have an access to that files, as they are probably served by the frontend webserever. So you have a crazy option: use something like server side includes (SSI) to embed the variable content into choosen media files by somehow parsing the config file.
Better idea would be to have same admin media prefix scheme for every site, flexible per-site config file for your webserver of choice, where admin media files would be served from some known location:
location ^~ /media/ {
root /.../django-$django_ver/contrib/admin/;
}

I can provide a somewhat "evil" solution: since Django 1.4 django.contrib.admin is using django.contrib.staticfiles for everything static. Since Admin's Javascript is using missing-admin-media-prefix if it's not found, we can do a redirect, e.g. in Apache:
RewriteRule ^/missing-admin-media-prefix/(.*)$ /static/admin/$1 [L,R=301]
(if your STATIC_URL is set to /static, of course)
This method is especially nice if you hack around stuff in Admin a lot and also don't want to clutter your templates with global Javascript variable declarations.

Related

How to rename "admin" static files in Django?

When loading up the Django admin pages, you load in the default Django admin css / js files. For example, example.com/staticfiles/admin/css/base.css.
I've enabled a default Amazon WAF (firewall) rule that blocks all access to pages that have the word "admin" in their URL. This has been great so far -- as most bots love looking for /admin domains.
However, this ends up blocking the Django admin. I'm able to rename the Django admin url to /manager using one line of code in urls.py, however I can't figure out how to to change the admin static files' filepath. I'm currently getting raw, unstyled HTML in my admin as all the static files are blocked.
Ideally, in settings.py I could write:
ADMIN_FOLDER_NAME = 'manager'
and then
example.com/staticfiles/admin/css/base.css would get rewritten to example.com/staticfiles/manager/css/base.css.
How can I tell Django to look in a different folder for admin files?
Note
I am not asking how to rename the url for accessing the admin page. I am talking about the admin static files. The below code does change the admin page url, but does not change the url for the staticfiles.
urlpatterns += [url(r'^newadminurl', admin.site.urls)]
As you may have noticed in example.com/staticfiles/admin/css/base.css, the url is based off of the folder structure.
Also, it should be noted that in django admin templates, static file paths are said to look into a folder named admin like so: {% static "admin/css/base.css" %} (admin static file paths are hard-coded).
# a line from admin/base.html
<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}">
This means that you cannot change the path after example.com/staticfiles/ for admin static files.
But, can be done by copying the templates directory
Copy templates directory from venv/Lib/site-packages/django/contrib/admin/ to project base folder
Find all the occurrences of {% static "admin/ and replace {% static "admin/ with {% static "manager/ in the templates. Need to be careful not to change any other thing in the template files.
Update TEMPLATES settings > 'DIRS': [os.path.join(BASE_DIR, 'templates')]
Overriding Admin Templates
Another possible way to achieve this could be rewriting the URL to remove or replace admin from the URL. Simply rewriting the URL would result in 404. Furthermore, server configuration for serving the admin static files has to be additionally added.
You're solving the wrong problem. Firewalls have standard rules to get you started, but must be customized to match the environment they're protecting, not the other way around.
Renaming files and routes each time you install or upgrade something is time consuming and error prone. Make exceptions for routes that exist - they are valid routes, so teach the WAF about them.
Just think about it this way: you would never post a question like this:
"I've just bought a firewall. What application framework can I use that can be put behind it, without needing to change the framework?"

How to serve static files with django that has hardcoded relative paths on Heroku/OpenShift/etc?

I have HTML/JS/CSS files provided by a third party (whom I have no control over) that serves as a single page app that communicates with a backend built with Django and django-rest-framework.
I'm wanting to host this on Heroku and thus these static assets are being served by Django. These files contains relative paths to each other. For example, the index.html contains:
<link rel="stylesheet" type="text/css" media="screen" href="styles/css/bootstrap.min.css">
Which leads to a 404 because styles/css/bootstrap.min.css is not routed by django.
The only way I know of to serve the index.html from my domain root www.domain.com is with an url config like:
url(r'^$', TemplateView.as_view(template_name='index.html'), name='home'),
...even though it's not really a template, it's just plain HTML.
The problem arises from the fact that all the urls in the other assets are relative to this index.html and of course Django doesn't work like that. If I was developing this front-end application I'd be using the static template tag and one of the various ways to get urls to javascript.
I don't mind switching from Heroku to another PaaS if they offer a solution to this problem, but manually editing all these files does not sound like a fun job...especially considering the fact that I'll be receiving updates to these files going forward.
I think the way to solve this on a regular old server would be to configure the web server to resolve these urls correctly, but that option doesn't seem to be available on Heroku.
Here's how to set up Django to serve your static files and index.html on / while still having the possibility to use Django views for the admin dashboard, registration etc.
from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.staticfiles.views import serve
from django.views.generic import RedirectView
admin.autodiscover()
urlpatterns = [
# / routes to index.html
url(r'^$', serve,
kwargs={'path': 'index.html'}),
# static files (*.css, *.js, *.jpg etc.) served on /
# (assuming Django uses /static/ and /media/ for static/media urls)
url(r'^(?!/?static/)(?!/?media/)(?P<path>.*\..*)$',
RedirectView.as_view(url='/static/%(path)s', permanent=False)),
# other views still work too
url(r'^admin/', include(admin.site.urls)),
]
I specify urlpatterns as a list, as Django 1.10 requires. The redirects are not permanent by default since 1.9, so you need to explicitly set permanent=True if you want browsers to cache this, though when debugging I find it better to start with False.
This approach allows you to use something like create-react-app or Yeoman frontend generators that package a built, minified frontend app into a single folder (like dist/). You then e.g. use a script to move that into Django's static files folder (e.g. myproject/static/) and serve it from Heroku.
What Ian wrote about wanting to use something like S3 for your static files stands, but sometimes you just want to start simple with one repository, one Heroku dyno and still be able to use Django + a SPA. Also, using something like WhiteNoise makes serving static files from Python pretty OK and allows you to
later on easily put a CDN in front of your static files.
Note: for user-uploaded files you should still use an outside service like Amazon S3 or Backblaze B2 (which is 4 lines of code to integrate).
Serving Media Files
WhiteNoise is not suitable for serving user-uploaded “media” files. For one thing, as described above, it only checks for static files at startup and so files added after the app starts won’t be seen. More importantly though, serving user-uploaded files from the same domain as your main application is a security risk (this blog post from Google security describes the problem well). And in addition to that, using local disk to store and serve your user media makes it harder to scale your application across multiple machines.
For all these reasons, it’s much better to store files on a separate dedicated storage service and serve them to users from there. The django-storages library provides many options e.g. Amazon S3, Azure Storage, and Rackspace CloudFiles.
Note 2: on production with DEBUG=False there are issues, so check out this WhiteNoise issue for a solution.
Note 3: Turned this answer into a blog post here.
Note 4: since writing this, I've been tweaking the solution more and more for frontend routing, so I ended up releasing a new django-spa package suited for simple serving of single-page apps from Django.

Django's admin pages are missing their typical formatting/style, have I set it up wrong?

I finally got my django install working, however I'm noticing that the typical look and feel of the admin pages are missing, and it's as if there are no styles applied to the structure of the pages. Do I have some kind of setup issue or pathing issue that's preventing the style sheets from being found? Where are they stored? My pages look like they are from 1994.
Sounds like your admin media isn't being served correctly. In your settings.py, there's a variable called ADMIN_MEDIA_PREFIX, which specifies the URL where Django should look for them. The actual media files are in "[path to your Python site-packages]/django/contrib/admin/media". When using manage.py runserver, the files are served "automagically". However, when using Apache/nginx/etc it's your responsibility to make sure that your server makes the files available at that URL (using rewrite rules, symlinks, etc). More info can be found here.
I've solved this issue simply with the alias on apache:
Alias /static/admin/ /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media/
Alias admin/media/ /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media/
You need to provide more info for use to help you properly. However, this is most probably because didn't set up your Web server to serve static file, and therefor, the admin CSS is not loaded.
To solve this, got the the admin and look at the HTML source. You'll css the path to the admind css. Make your web server service this file on this path.

Django and Serving Static Files

I'm hosting a site on WebFaction using Django/mod_python/Python2.5. I've recently run into the concept of static files (when setting up my Django admin).
From what I understand, serving static files is simply the idea of telling the server to serve files directly from a specific directory, rather than first routing the request through apache, then mod_python, then django, and finally back to the user. In the case of WebFaction this helps especially since there are two Apache servers that your request must go through (your app's server and the main public server).
Why is it that when I setup Django's static files, it only needs the /media folder in /contrib/admin? Is it just that all of Django's static content is related to the admin panel?
When I want to serve my own static content (images, css, etc.) should I include it in the same /media folder or set up another alias for my own content (/my_media)?
Yes, the static files used by Django are pretty much related to images, javascript and css for the admin. All other static content comes from your application. You can keep both sets (yours and the admin) under the same server. Just set the appropriate folders in the settings file.
http://docs.djangoproject.com/en/dev/ref/settings/#admin-media-prefix
http://docs.djangoproject.com/en/dev/ref/settings/#media-root
http://docs.djangoproject.com/en/dev/ref/settings/#media-url
See this post for a little more information:
Django and Static Files
Django's static files (e.g. js, css, images, etc.) are all in the media folder, and are related to the admin panel.
On WebFaction to save processing power, and more importantly memory, it is better to serve these from your secondary apache server (or even better from nginx or lighttpd) without having to go through mod_python and Django.
I use the following folder setup for my files:
media
css
js
img
etc
admin
css
js
img
See http://forum.webfaction.com/viewtopic.php?id=1981 for how to setup nginx as your secondary server on WebFaction if you are interested.

How do you serve vanilla/custom pages in an MVC based site?

Let's say you've setup your site using Pylons, Django and most of the site runs fine and according to the framework used. However, what if you had a custom section that was entirely say, composed of flat html files and its own set of images, which you didn't have time to actually incorporate using the framework and were forced to basically support, under the same domain? Should there be some sort of default controller/view that's super bare minimalistic or do frameworks such as these somehow offer support in some smart way?
I realize also that potentially one could setup a new subdomain and reroute it to an entirely different directory, but I'm just curious as to how one would solve this when forced to deal with a framework.
When serving static pages I'd rather avoid having Django or Pylons handle the request, and handle it with the web server only. Using Nginx, you'd use a directive like:
location / {
root /whatever/the/path/is/;
# if the file exists, return it immediately
if (-f $request_filename) {
break;
}
# pass requests to MVC framework
# i.e. proxy to another server on localhost:
proxy_pass http://127.0.0.1:80;
}
For pylons you should be able to drop your static html files in the public directory. If there isn't a controller for a url then I think pylons looks in the public folder next.
For Django, I would serve these in exactly the same way as you serve your static assets - in your site_media directory, along with subdirs for js, css and img, you could have an html directory. Then the URL would just be /site_media/html/whatever.html.
In Django take a look at flatpages. It's part of the django.contrib package and uses flatpages middleware to serve up flat HTML controlled through the admin interface. For basic purposes, serving up additional about pages or the like this should do the trick.
You could also just create an HTML folder and - using mod_python, at least - set no handler for that path in the Apache configuration file (e.g. vhost.conf).