How to rename "admin" static files in Django? - 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?"

Related

django static files not served to HTML-EMail template

I am running Django on a local Vagrant arch machine on a Win7 host. I set up my environment variables from Django in a .env file. In my app all static files are served correctly and everything works as it should.
Problem: I am not able to serve my static files (images) in my html-email templates. Until now i served them as hardcoded filer URL's and i want to change that.
I am passing BASE_URL BASE_URL=http://127.0.0.1:8001, which is proved working, as context to the template and loading static as usual:
{% load static %}
and calling it in HTML tag:
<img src="{{BASE_URL}}{% static 'img/my_image.png' %}">
In the received email the URL of the image is http://127.0.0.1:8001/static/img/my_image.png which looks right but triggers a 404.
What am i missing??
(Please dont ask me if the image is in the corresponding folder, it is ;)
As already said by #dentemm: Your email service provider try to fetch images from http://127.0.0.1:8001/static/img/my_image.png but he cannot while address of your server is visible only from your local computer. Therefore images are not found. One way to solve this is to render template and take screenshot of template and send that in email body(to see if it renders properly) E.g. here.
Another way is to upload img files to some publicly accessible server.

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 - admin_media_prefix access from javascript

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.

Serving static media in django application

I notice that when I reference my java scripts and static image files from my templates, they show up in development, but not from the production server. From development, I access them as such:
<img src="/my_proj/media/css/images/collapsed.png" />
but from production, I have to remove the project directory:
<img src="/media/css/images/collapsed.png" />
I'm assuming I'm doing something wrong with regard to serving static media.
I'm caught between a number of seemingly different options for serving static media in Django. On one hand, it's been recommended that I use django-staticfiles to serve media. On the other I see reference to STATIC_ROOT and STATIC_URL in the documentation (with caveats about use in production).
I have small .png files of "plus" and "minus" symbols for use in some of my jQuery scripts. In addition, the scripts themselves need to be referenced.
1) Am I correctly categorizing scripts and site images as static media?
2) What is the best method to access this media (from production)?
You shouldn't keep the URLs hardcoded that way.
If you're running thedev version of Django, you should check this doc.
If you're not, for simplicity, you can use {{ MEDIA_URL }} and configure that variable in your settings for dev and for production.
<img src="{{ MEDIA_URL }}/media/css..." />
Keep in mind that you'll have to use RequestContext, see more here on docs here and here.
Also, you should server all your static files directly trough a proper webserver. You can configure apache or nginx to do that.
Sigh. There is only one way to serve static media in production, and that is to get the webserver - eg Apache - to do it. Django-staticfiles is for development only, and this is clearly stated throughout the documentation.

Apache | Django: How to run websites on the back of a base URL?

I've got a base url. http://baseurl.com/
I'm trying to run projects on the back of it. For example
http://baseurl.com/mongoose/
The projects run but the URL don't work properly because they all
reference the base url. So for 'About Me' page it points to
http://baseurl.com/about instead of http://baseurl.com/mongoose/about
Is this something i need to change in django or apache? Is what I'm
trying to do even possible?
Coming from an IIS .net background I know that in IIS you can "Create and application" within a site which essentially does what I'm trying to achieve now with Apache and Django.
Thanks
You shouldn't need to do anything. Apache is supposed to be setting a request header called SCRIPT_NAME, which is your base URL, and all URL reversing takes that into account.
How are you creating these URLs in your templates?
Update
So your problem is with getting the URLs of Flatpages. The issue is that the normal way of calculating URLs dynamically, so that they do take SCRIPT_NAME into account - using the reverse() function or the {% url %} tag - doesn't work with Flatpages, because they are not dispatched via urls.py but via a custom middleware which fires on a 404.
So instead of using that middleware, I would use the urls.py mechanism to dispatch to flatpages. Remove the flatpagemiddleware from your settings.py, and in urls.py at the end of your patterns add this:
url(r'^(?P<url>.*)$', 'django.contrib.flatpages.views.flatpage', name='flatpage'),
Now, in your templates, you can do:
<a href="{% url flatpage page.url %}">
and it should work correctly.
Check any urls.py in the project(s) to see if they expect to be top-level. But if the application outputs links like /something then it's going to mean the root directory. The application should be reversing a view/parameter into a URL, which would allow you to move it around. If you wrote the apps, check out reverse in django.core.urlresolvers