Flask static file Cache-Control - python

I'm trying to set a reasonable cache expiry for my JS files while in development. I have the standard setup, where HTML, CSS and JS are living under the static directory.
The docs do mention this, but for the life of me I cannot get this to work. I've tried both methods implied, first
class MyFlask(flask.Flask):
def get_send_file_max_age(self, name):
if name.lower().endswith('.js'):
return 60
return flask.Flask.get_send_file_max_age(self, name)
app = MyFlask(__name__)
and
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 60
Both have had no effect, my JS files under /static are still coming back with the default cache timeout,
Cache-Control: public, max-age=43200
Any pointers appreciated.

You may want to look at webassets to manage the cache expiry. It works in both development and production environment.

I had this problem and couldn't find an answer online that worked for me.
Then I realised that my static files are not being served from Flask at all! Flask only generates my HTML. The static files are served directly by my web server (Apache in my case, yours might be Nginx or something else).
Here are the instructions for Apache.
First install the mod_expires module:
sudo a2enmod expires
Then add something like this to your .htaccess file:
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/* "access plus 1 year"
More details on how to configure it in the Apache manual.

Related

Django 1.10 media (FileField) 404

My first question ever, so go easy. I'll give as much detail as possible.
My setup
django 1.10 (I know, I need to upgrade)
python 3.5.[something]
postgres DB
gunicorn
nginx
The problem and what I've tried
The problem is that I pulled a recent commit that was working fine locally and suddenly none of a model's images, which previously rendered fine, are working - I'm getting 404s for all of them. I've tried the following:
Checking out previous commits
Restarting gunicorn (sudo systemctl restart gunicorn)
Restarting nginx
Restarting postgresql
Using python manage.py shell to access objects and check that they're still associating with a URL to the file
My code works when run locally - none of the uploaded images/files are causing 404s. As with my production environment, the logos folder sits in the main directory of my project, rather than being appended to a media folder (which other answers and the django docs suggested would be the case - this could be a red herring though).
My browser console shows the 404s and that it's trying to fetch from <domain.com>/media/logos/<filename>, even though they're stored (I've checked the file structure) in <project_folder>/logos/<filename> (i.e. without the media folder), but this was never previously a problem. Again, locally this is also the case but is completely fine.
My code and stuff
In my models.py I have this field:
class Thing(models.Model):
...
logo = models.FileField(upload_to='logos/', blank=True)
...which I then render in my HTML file with:
<img class="..." src="{{ thing.logo.url }}>
...which, again, is how django docs says to do it (rather than hard-coding a URL). I read in the docs that the file is stored as part of the object in the database, so referring to a filepath with filename wouldn't necessarily work (or something similar), therefore that this is the best option.
As far as I can see my urls are set up fine:
urlpatterns = [
...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
...as per this answer (and probably others).
In settings.py no filepaths or URLs are hard-coded: they're all relative to the os.path....
MEDIA_URL = '/media/' and MEDIA_ROOT is unset (default is '')
I can't think of any other information that would be helpful, but if there is then let me know. Please help! I currently have a live website with ugly alt-text :(
The static(...) only works in development. In production you should configure your server (e.g. Apache or Nginx) to serve the files. See the example for Apache and modwsgi. – Alasdair Jan 9 at 22:12
Thanks #Alasdair for your above comment - unfortunately I can't mark it as the answer, but it led to it.
I hadn't setup Nginx to handle media files - only static files. So my Nginx conf file now looks like this:
location /static/ {
root /home/<path>/<to>/<folder>;
}
location /media/ {
root /home/<path>/<to>/<folder>;
}
My Django app was then uploading files to a slightly unexpected location, so I had to copy those files across to the correct media file location and set that right.

Pyramid returning wrong Content-Type HTTP header for static assets

Up till now, I have been using Pyramid to serve static assets from a folder inside the python package, as specified in their documentation:
config.add_static_view('static', 'myapp:static')
and loading them from the templates as follows:
<script type="text/javascript" src="{{ request.application_url }}/static/js/jquery-1.7.1.min.js"></script>
However, I noticed that Chrome spits out warnings like this:
Resource interpreted as Stylesheet but transferred with MIME type apache/2.2.14: "http://mydomain.com/static/js/jquery-1.7.1.min.js"
or
Resource interpreted as Stylesheet but transferred with MIME type text/plain: "http://mydomain.com/static/js/jquery-1.7.1.min.js"
This happens on hard refresh, and it seems that 3-4 random resources which are loaded in the <head> are served with the wrong Content-Type header (according to Pyramid docs, the header is determined by the file extension).
There is no pattern I was able to deduce as to how the wrong header is set. Sometimes, it is text/plain for javascript/CSS files, sometimes it is a path like /static/js/something.js (and this path is in no way related to the request URL), and sometimes it is the value of the Server header, as with apache/2.2.14 above.
This is a big problem since when CSS is returned with a bad Content-Type, it is not rendered, and this breaks the entire page. I have solved this by catching requests to /static with Apache, and using it to serve the static assets, while letting all other requests go through to Pyramid. I no longer see the bad MIME type warnings in Chrome. However, I was wondering if anyone has run into this issue, and whether it is a Pyramid bug, or whether I am doing something else wrong.
EDIT: I forgot to provide the specs of how I deploy my app. The production server runs Apache 2.2, and the app runs under mod_wsgi. The process I followed is almost verbatim described in this tutorial: http://docs.pylonsproject.org/projects/pyramid/en/1.0-branch/tutorials/modwsgi/index.html. IMPORTANT: the issue only occurs when running on Apache via mod_wsgi. When I run the app locally on waitress, the Content-Type headers are always correct.

Django can't find my non-python files!

I can't, for the life of me, get Django to find my JavaScript files! I am trying to plug in a custom widget to the admin page, like so:
class MarkItUpWidget(forms.Textarea):
class Media:
js = (
'js/jquery.js',
'js/markitup/jquery.markitup.js',
'js/markitup/sets/markdown/set.js',
'js/markitup.init.js',
)
css = {
'screen': (
'js/markitup/skins/simple/style.css',
'js/markitup/sets/markdown/style.css',
)
}
However, when this code is inserted on my admin page, all of the links are broken, I can't get to any of my JavaScript files. My settings file looks like this: http://pastebin.com/qFVqUXyG
Is there something I am doing wrong? I'm somewhat new to Django.
I guess you're using django-admin runserver to test your website. In that case, have a look at "How to serve static files" (but don't ignore the big fat disclaimer).
Once you're ready to deploy, this chapter contains all the information (provided you go the standard route of Apache/mod_wsgi)
I don't know what the "django way" to include js files is, but I just use a simple regular include in the template, its great since you can dynamically generate the location / filename for these.. oh and if you are using django's test server I don't know how to get it to recognize these or if it even can but all you need is to run an apache server on your local machine and then put the files in the localhost directory and you can include them through localhost by including their full path in the template (i.e., http://localhost/myfiledirectory/myfile.js), also something I do is use amazon s3 to host files since you then get a url for them on there (not that you asked about this but its a quick way to host files if you don't have apache running locally)
Basically, my understanding of Django is that it works differently than a PHP framework, for example, as the files for Django don't sit (or don't need to at least) in the web path (i.e. they do not need to be accessible through the host path but can be anywhere on the machine) this is good for providing extra security, etc but it also means, that to give files a url to download them they need to be in the web hosting path, others may know how to make django do this directly but my quick and dirty method of putting them on the localhost path will work if thats all you're after.

Making Django development server faster at serving static media

I'm using the Django manage.py runserver for developing my application (obviously), but it takes 10 seconds to completely load a page because the development server is very, very slow at serving static media.
Is there any way to speed it up or some kind of workaround? I'm using Windows 7.
Consider using mod_wsgi instead, and having httpd handle the static media.
Development server is simple unsafe single-threaded application, so you cannot do much.
One trick you could try is to redirect /site_media to second development server, but this is ugly and probably wouldn't help that much. So you could try bundling/compressing multiple assets into one css/js (e.g. using YUI Compressor).
And in any case, you should have separate static media server, that can serve multiple assets at once.
Install Firefox (if you haven't already), and install the Firebug Add-On. Restart your browser. In the lower-right corner click the "bug" icon and make sure that in the "Network" tab (it's a dropdown) of the Firebug panel that opens at the bottom of the browser, the network monitor is active.
Now with the network tab of Firebug open, open your Django-generated page that you observed to load slowly. Take a look at the timeline bars. You'll notice that the colored fragment(s) of each bar indicate(s) the reason for each request's total "load" time. Violet, for instance, means that actually the browser is waiting for the server to generate the response. Gray means it's receiving content. And so on. Hovering over the bars will display a color legend.
With Firebug's network monitor you should be able to pinpoint how exactly your browser and/or server are spending their 10 seconds.
Run lighttpd to serve the static content and use the MEDIA_URL to point the pages at the lighttpd server that servers the static stuff.
You can try using django-extensions runserver_plus command with the --threaded option as a replacement for Django's runserver command. Under the hood, it uses Werkzeug as the threaded WSGI server. You may notice a huge improvement in loading times for static files.
Lightning fast ans easy on resources when using NGINX for serving static and media files. Here's how it goes. However, you'll need to adapt some paths according to your use case and system. But I think this will get you started:
1) Download NGINX for your system, in your case Windows:
http://nginx.org/
2) Unpack the zip file. Here's how your NGINX config file may look like. This file lives inside nginx/conf/:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# root path to your project
# use "..." if spaces are in the path
# you may add a drive letter if required, e.g. root c:/foo
# use / instead of \. It's simpler and works
root /example/path/django/project/;
server {
# any free port number will do; Django dev server usually runs on port 80
listen 8000 default;
server_name localhost; # or 127.0.0.1
}
}
3) Start NGINX by calling the nginx.exe - no options needed.
4) Tweak your Djnago project's settings.py file:
if DEBUG:
STATIC_URL = 'http://localhost:8000/static/'
# against Django recommendation, I often still use the static
# directory for user uploads; old-style Django ;-)
MEDIA_URL = 'http://localhost:8000/static/uploads/'
Now, the static URLs in Django should look something like this:
http://localhost:8000/static/js/base.js
... 5) Stop the server by calling:
taskkill /f /IM nginx.exe
Well, that's it. I typed this quickly, so just let me know if anything's unclear or not working for you. I may improve this answer accordingly.

How to deploy Django application at sub-url?

I need to set up a django development environment that is publicly viewable on the internet (I am doing this for school, and my projects need to be viewable by my professor, this isn't a setup that needs much security). I have a virtual server running Ubuntu 8.04 LTS.
I need to have multiple django applications running in subdirectories of my main site. That is, I need mysite.com to be a static page, mysite.com/wordpress to be my wordpress blog, and mysite.com/django1 mysite.com/django2 etc. to be django projects.
I am using apache, and I will either be using sqlite or mysql.
There seem to be as many different ways to install and configure django as there are websites offering advice, and all of them assume a single project is going to be the root of the website. I'd really appreciate some help, thank you.
You can use
WSGIScriptAlias /django1 /home/keratacon/www/django1/wsgi.py
WSGIScriptAlias /django2 /home/keratacon/www/django2/wsgi.py
in your apache+mod_wsgi config, assuming wsgi.py is the name of your wsgi script.
This blog explains the solution (assuming that mod_wsgi is used, with nginx/uwsgi the solution is similar apparently in nginx/uwsgi this is not necessary).
The first parameter of WSGIScriptAlias - the /sub-url will be stripped from the request url and the rest will go to your django app. If your Django app urls all start with /sub-url (which are stripped by mod_wsgi), then you will not be able to show the views at those urls, unless you "re-insert" the /sub-url to the request path part.
import django.core.handlers.wsgi
_application = django.core.handlers.wsgi.WSGIHandler()
def application(environ, start_response):
#the line below will re-append the sub-url to all urls
environ['PATH_INFO'] = environ['SCRIPT_NAME'] + environ['PATH_INFO']
#this one will make django think that it's the only thing running on the server
environ['SCRIPT_NAME'] = '' # my little addition to make it work
return _application(environ, start_response)
Also in your urls.py all urls must be prefixed with the sub-url of your interest.
Finally, the WSGIScriptAlias must be the same as your sub-url:
#the below line will "take-out" the sub-url and pass the rest
#to your wsgi script
WSGIScriptAlias /sub-url /path/to/wsgi_script
Where file /path/to/wsgi_script must contain the definition of application as shown in the first code snippet.
To make the "sub-url" setup explicit in Django, the equivalent request path patching would have to occur within the Django framework.

Categories

Resources