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.
Related
I am new to flask and am very confused regarding including libraries from external folders into the html template. I know how to include from the static folder, but I want to keep my code and the library files in separate folders. So, my directory structure is something like:
./main_directory
|
|_./Code
| |_./flask_script.py
| |_./static
|
|_./Libraries
I know about the send_from_directory() function but I don't understand exactly how to use it. The most common thing I have seen is:
#app.route('/cdn/<path:filename>')
def custom_static(filename):
return send_from_directory(app.config['CUSTOM_STATIC_PATH'], filename)
Which is from another StackOverflow answer. But here what is cdn in this case? Also, what is the value of app.confg['CUSTOM_STATIC_PATH'] set to? And what all should be done in the HTML template to make this work?
In general serving static files with flask is only good for development. Realistically, when going live it is advised to serve static files otherwise, for example having a web server in front of your app and delegating file serving to it, or putting the files in a file store, like AWS S3.
Regarding your custom_static route, cdn in this case is simply a url prefix (it can be anything), you can then address your static assets from templates like <img src="/cdn/test.jpg">. (The cdn prefix can give you a hint that these files are later moved to a CDN and aren't served by flask in production).
app.config['CUSTOM_STATIC_PATH'] should be set to the absolute path to the directory containing your custom static files.
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.
I'm using Django 1.8 and I want to add a parameter to my static files to cache bust.
This is what I'm doing right now, setting a manual parameter:
<link href="{% static 'css/openprescribing.css' %}?q=0.1.1" rel="stylesheet">
But I feel there must be a better way to update the parameter.
I guess it would be a bit neater to have a setting passed through the template (and that would save having to update it in multiple places).
But what would be really nice is if Django could update it automatically for me.
The notes on django-cachebuster suggest that it's now possible to do this automatically in staticfiles, but I can't find anything in the staticfiles docs about it.
Anyone know a way to do this?
Yes this can be done automatically with contrib.staticfiles. There are two additional provided storage classes which will rename files using a hash. These
are documented here: ManifestStaticFilesStorage and CachedStaticFilesStorage
From the docs:
A subclass of the StaticFilesStorage storage backend which stores the file names it handles by appending the MD5 hash of the file’s content to the filename. For example, the file css/styles.css would also be saved as css/styles.55e7cbb9ba48.css.
The purpose of this storage is to keep serving the old files in case some pages still refer to those files, e.g. because they are cached by you or a 3rd party proxy server. Additionally, it’s very helpful if you want to apply far future Expires headers to the deployed files to speed up the load time for subsequent page visits.
The main difference is
CachedStaticFilesStorage is a similar class like the ManifestStaticFilesStorage class but uses Django’s caching framework for storing the hashed names of processed files instead of a static manifest file called staticfiles.json. This is mostly useful for situations in which you don’t have access to the file system.
To enable them you need to change your STATICFILES_STORAGE setting is set to 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' or 'django.contrib.staticfiles.storage.CachedStaticFilesStorage'. The file names are only changed when DEBUG=False as it would be in production.
I'm no expert in Caching, but I think letting nginx handle caching might be better than using Django. Django has a lot to handle, so you can let the lightweight static server do that nasty job.
I do not use cloudflare, but I use this line to cache my statics, however, immediately the file changes, Nginx propagates the most recent file (thats the same file):
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
}
which is a snippet from this gist I'm currently using that conf in production, so I know it works.
One thing I'll point out is, make sure MemCached isn't working and connected to your django as a Caching Backend. I say this, because I have spent many hours in time past hitting my head against the wall, simply because MemCached was caching my page with every content in it for up to 10 minutes.
with this nginx location conf, Whenever I change my .css, or upload a new file (static), the new file immediately takes over, as long as I've python manage.py collectstatic'ed them into the appropriate directory
I stand to be corrected though, if that's not actually the part doing the trick.
Proof that above works with Cache-busting (as you call it)
I went into server
Deleted my static folder (nginx still running) sudo rm -rf static/
Accessed my site
No static loaded
Went back, and python manage.py collectstatic
Accessed my site again. Statics loaded
No browser hard refresh was used. No nginx reload|restart whatever was used.
Nginx is smart enough to cache your statics, but reload the static when the file is new and serve it.
If you use a CDN, cache busting becomes more complex. If you just want to bust the cache for a specific image, just create a simple tag that does the trick....
import time
from django import template
register = template.Library()
#register.simple_tag()
def cache_bust():
return int(time.time())
Then in your template just do something like this...
{% load cache_app %}
<img src="/captcha/?cache_bust={% cache_bust %}" class="captcha"/>
And you have cache busting the simple way.
I have a django web app on A2hosting, where I am using .htaccess files and passenger_wsgi.py. It was working fine the last time I touched it, but then someone who had a look at it later informed me that it was broken.
I created a test situation to find the problem and here's the gist of it.
When I do a GET (to www.geo4ce.com/quiz/test_weird/), it goes to a page with a simple form that just has one input and a submit and an action that has "/quiz/test_weird/" and method="post". When I submit the form, the server expects the "quiz" part of the url to be referring to a path on the file server, can't find it and then logs an error that it can't find it. But, then it checks the test_weird part of the url against my django urls.py file, finds a different view for that and displays it.
A scenario that almost works properly is with www.geo4ce.com/quiz/test_hacked/, that has the same set up, except the form has action = "/anythinghere/quiz/test_hacked/". In this case, the "anythinghere" part of the url gets an error logged, since it doesn't exist on the file server, and then the /quiz/test_hacked/ part of the url works normally to get back to the original web page.
Anyone have any idea how I might be able to fix or debug this?
[EDIT]
I don't think it's the .htaccess file that's the cause. It looks something like this.
PassengerEnabled On
PassengerAppRoot /path/to/app/folder/
# Prevent Apache from serving .htaccess files:
<FilesMatch "^\.htaccess">
Order allow,deny
Deny from all
</FilesMatch>
deny from xxx.yyy.zzz
Apparently the issue is caused by a certain version of Passenger (with RoR). I've been told to switch to using FCGI.
I am writing a web-app in web.py (a rewrite/extension of mongs) which I want to function both as a standalone application, and as a sub-app that requests can be forwarded to. The issue that I am having is that when it is used as a sub-app, static files cannot easily be served from its own static directory. Since I intend to distribute this (and not require users to combine the files into their project's static directory) I want the directory structure to be:
app_that_is_using_mongs (not mine)
static (which holds the app's static files - also not mine)
mongs (my subapp)
main.py (the code for mongs)
view (holds templates)
static (the static folder for mongs)
main.py (the code for the app that is using mongs)
...so that the entire mongs directory is separated from whatever app is using it.
I have considered a few possibilities for getting this to work:
Using a request handler that reads and outputs the files from the static directory, like:
cwd = os.path.dirname(__file__) + '/' # get current working directory
class Static:
def GET(self, filename):
"""searches for and returns a requested static file or 404s out"""
try:
return open(cwd + 'static/' + filename, 'r').read()
except:
web.application.notfound(app) # file not found
I am not sure about the performance of this solution for large files, and it seems like this should be something web.py can do on its own.
Adding another static directory by accessing the cherry.py staticdir tool through web.py... I'm not sure how to do something like this (interacting directly with the server that web.py is running on), and I don't think it would still work if I switched to a Gunicorn server (or any server but cherry.py).
Fixing the way that web.py handles static files to make it more extendable... If there is no other way, then rewriting this portion of web.py and maybe getting it pushed into the main repo is probably the best way.
So, what is the best way to do this?
In web.py the static assets aren't served through the application router. Instead the http server has a check weather the request url starts with /static. This means that weather you have a sub-application or not, the /static/... maps directly to the static directory in the root application.
Your first idea of building a static class will definitely work, but you are right that there is a definite performance implication - though, you'd have to benchmark it to really know how bad it is.
Another option, which is operationally worse, but is a temporary fix is to create a soft-link from the static directory of the parent app, into the sub-application's static directory. i.e.
parent_app/
static/
sub_app/ -> parent_app/sub_app/static/sub_app
...
sub_app/
static/
sub_app/
...
Then, when you want to access a static asset from sub_app, you would hit a url like: /static/sub_app/asset. Since this url starts with /static, it will get caught by the http server and redirect to the static directory, following the soft link, and resolves to the actual asset. Because of the sub_app directory, this solution will work when running the sub_app directly, or running the parent_app. You will have to setup this soft link on every server you deploy to, and for every development environment, which makes this less than ideal.