How do you control version of your static files in Django? I wrote custom templatetag which adds the modification date as GET parameter of file URL, but would like to know - whether I'm doing.
Tag code:
import os
from django import template
from django.conf import settings
register = template.Library()
#register.simple_tag
def sstatic(path):
'''
Returns absolute URL to static file with versioning.
'''
full_path = os.path.join(settings.STATIC_ROOT, path)
try:
# Get file modification time.
mtime = os.path.getmtime(full_path)
return '%s%s?%s' % (settings.STATIC_URL, path, mtime)
except OSError:
# Returns normal url if this file was not found in filesystem.
return '%s%s' % (settings.STATIC_URL, path)
Applications such as django-compressor, and django-pipeline are good for these sort of things.
Django 1.7 added ManifestStaticFilesStorage which is designed to do this as part of the collectstatic cycle by appending hashes to filenames. By default it applies to all filetypes.
If you want to use it but want to specify which filetypes are versioned, then I wrote an extension that allows you to include / exclude files by path pattern.
As of Django 2.2 this ManifestStaticFilesStorage is still the recommended way to do this.
Related
I have made a Django app that takes arguments from the form and makes an image using Pillow.
views.py:
file_name = "{}.png".format(random.randint(1, 255))
image1.save(file_name)
pretty simple stuff, right? Now when I try to render that image with HttpResponse as:
return HttpResponse("<img src='" +file_name + "' alt='image here'>")
apparently, it will throw an error. Can you please tell me what to do in order to save it properly and show in HttpResponse?
Django==1.11.8
Pillow==5.0.0
Python 3.6.2
Thank you!
Firstly, you should be saving to MEDIA_ROOT. Secondly, you need to put an actual URL - ie relative to MEDIA_URL - in your img src, not just a filename. Third, you need to have something serving files at that URL.
I have an application that allows for users to upload CSV files with data which is then graphed and displayed to the user. These files are saved as media within my media folder. In my graph view however I need to open the file and process it. My problem is that I can only open files that are within my project's current working directory and any time that I attempt to upload a file from somewhere outside of that directory I get this error:
File b'TEST.CSV' does not exist
I have attempted this, but without success:
file_upload_dir = os.path.join(settings.MEDIA_ROOT, 'Data_Files')
data_file = open(os.path.join(file_upload_dir, new_file), 'rb')
The variable new_file is only the name of the file saved from in a session and not a path to that file. Data_Files is a directory within the media directory that contains the uploaded files.
My media settings for Django are
SETTINGS_DIR = os.path.dirname(__file__)
PROJECT_PATH = os.path.join(SETTINGS_DIR, os.pardir)
PROJECT_PATH = os.path.abspath(PROJECT_PATH)
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(PROJECT_PATH, 'media')
Is there a way to reference the media files properly from a view?
Here is the output of file_upload_dir and the location of the new file.
>>> print(file_upload_dir)
C:\\Users\\vut46744\\Desktop\\graphite_project\\media\\Data_Files
>>> print(os.path.join(file_upload_dir, new_file))
C:\\Users\\vut46744\\Desktop\\graphite_project\\media\\Data_Files\\TEST.CSV
Normally, you should not access files using open() in a Django app. You should use the storage API. This allows your code to play well with Django settings, and potential third party apps that augment this API.
https://docs.djangoproject.com/en/1.7/topics/files/#file-storage
So here you should be doing something like
from django.core.files.storage import default_storage
f = default_storage.open(os.path.join('Data_Files', new_file), 'r')
data = f.read()
f.close()
print(data)
By the way, if you want it to be modular, it would be a good idea to have a custom storage class, allowing easy configuration and use of your app, should your requirements change. Also, that allows putting files outside of MEDIA_ROOT. This sample storage will put them in settings.UPLOADS_ROOT (and default to MEDIA_ROOT if the setting is not found).
# Put this in a storage.py files in your app
from django.conf import settings
from django.core.files.storage import FileSystemStorage, get_storage_class
from django.utils.functional import LazyObject
class UploadsStorage(FileSystemStorage):
def __init__(self, location=None, base_url=None, *args, **kwargs):
if location is None:
location = getattr(settings, 'UPLOADS_ROOT', None)
super(UploadsStorage, self).__init__(location, base_url, *args, **kwargs)
self.base_url = None # forbid any URL generation for uploads
class ConfiguredStorage(LazyObject):
def _setup(self):
storage = getattr(settings, 'UPLOADS_STORAGE', None)
klass = UploadsStorage if storage is None else get_storage_class(storage)
self._wrapped = klass()
uploads_storage = ConfiguredStorage()
We create a very simple storage here. It's just the regular one, but that reads its files from another directory. Then we set up a lazy object that will allow overriding that storage from settings.
So now your code becomes:
from myapp.storage import uploads_storage
f = uploads_storage.open(new_files, 'r')
And in your settings, you set UPLOADS_ROOT to whatever you like. Probably something outside your media directory. And if someday you decide to store uploads in a database instead, you can set UPLOADS_STORAGE to a database-backed storage, your code will happily use it.
I am writing a Google App Engine webapp that renders some html to a Django template. I want to either render the template using either a file or just some json thats very similar to that in file. Is it possible to use Django to render this to a file that is read in and stored in database?
The oldAPI.HTML is just an old version of api.html but with some small changes. Rendering Django to the api-html file works fine.
I understand that you can't store files on GAE, how can i dynamically use Django to render to HTML stored in memory?
path = ""
oldAPI = APIVersion().get_by_key_name(version)
if oldAPI is None:
path = os.path.join(os.path.dirname(__file__), "api.html")
template_values = {
'responseDict': responseDict,
}
if path:
self.response.out.write(template.render(path, template_values))
else:
self.response.out.write(template.render(oldAPI.html,template_values))
In order to render a template 'in memory', there are a few things you'll need to do:
App Engine Setup
First of all, you'll need to ensure that everything is set up correctly for Django. There's a lot of information on the Third-party libraries page, but I'll include it here for your benefit.
In main.py, or (whatever your script handler is), you'll need to add the following lines:
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from google.appengine.dist import use_library
use_library('django', '1.2') # Change to a different version as you like
Don't forget to include django in your app.yaml:
libraries:
- name: django
version: "1.2"
Code Setup
Second of all, you'll need to create a Template object, as denoted in the Google App Engine template documentation. For example:
from google.appengine.ext.webapp import template
# Your code...
template_string = "Hello World"
my_template = template.Template(template_string)
# `context` is optional, but will be useful!
# `context` is what will contain any variables, etc. you use in the template
rendered_output = template.render(context)
# Now, do what you like with `rendered_output`!
You can instantiate a template from text in Django with just template.Template(my_text).
Unfortunately, there's no (builtin) way to do so, but you can get inspired from the function google.appengine.ext.webapp.template._load_user_django (GAE with Python 2.5) or google.appengine.ext.webapp.template._load_internal_django (GAE with Python 2.7) and write your very own wrapper overriding settings and rendering like GAE source does.
In my pyramid app, I have several static html files under tutorial/tutorial/pages/name.html (for example). How can I write a view callable for this? Would this work?
#view_config(renderer='view_page')
def view_page(request):
return {} # no values have to be passed to the template
then in the init.py file
config.add_route('view_page', 'tutorial:pages/{name}.html')
What do I need to put in the def view_page(request) function to call that name.html file specifically and then display its content?
Pyramid's static_view is a view capable of serving files from a directory. The part you really haven't explained is what the URLs are like for these static pages. For example, if they are all under a common prefix, you could use static_view (option 1). If they are not, then you have to create a view per page and serve it up directly (option 2).
option 1
url:
/foo/bar.html
/foo/baz/boo.html
static view:
config.add_static_view('/foo', 'tutorial:pages')
tutorial/pages hierarchy:
tutorial/pages/bar.html
tutorial/pages/baz/boo.html
add_static_view is effectively like calling add_route('foo', '/foo/*subpath'), and it serves up the subpath relative to tutorial:pages.
option 2
config.add_route('foo', '/foo')
config.add_route('bar', '/foo/bar')
#view_config(route_name='foo', renderer='tutorial:pages/foo.html.mako')
#view_config(route_name='bar', renderer='tutorial:pages/bar.html.mako')
def static_view(request):
return {}
Notice the .mako suffix to invoke the mako renderer. There is no .html renderer by default, but you could make one.
Does Jinja2 support template-relative paths e.g. %(here)s/other/template.html, to include other templates relative to the current template's place in the filesystem?
I do not believe so. Typically you include or extend other templates by specifying their paths relative to the root of whatever template loader and environment you're using.
So let's say your templates are all in /path/to/templates and you've set up Jinja like so:
import jinja2
template_dir = '/path/to/templates'
loader = jinja2.FileSystemLoader(template_dir)
environment = jinja2.Environment(loader=loader)
Now, if you'd like to include /path/to/templates/includes/sidebar.html in the /path/to/templates/index.html template, you'd write the following in your index.html:
{% include 'includes/sidebar.html' %}
and Jinja would figure out how to find it.
Just to add to Will McCutchen's answer,
You can have multiple directories in your loader. It then searches in each of the directories (in order) until it finds the template.
for example, if you wanted to have "sidebar.html" instead of "/includes/sidebar.html" then have:
loader=jinja2.FileSystemLoader(
[os.path.join(os.path.dirname(__file__),"templates/includes"),
os.path.join(os.path.dirname(__file__),"templates")])
instead of
loader=jinja2.FileSystemLoader(os.path.join(os.path.dirname(__file__),"templates"))
According to the documentation for jinja2.Environment.join_path(), support for relative template paths is possible by overriding join_path() to implement "template path joining".
class RelEnvironment(jinja2.Environment):
"""Override join_path() to enable relative template paths."""
def join_path(self, template, parent):
return os.path.join(os.path.dirname(parent), template)
The cleanest way to overcome this limitation, would be with a jinja2 extension that will allow to import relative template names
Something in the likes of:
from jinja2.ext import Extension
import re
class RelativeInclude(Extension):
"""Allows to import relative template names"""
tags = set(['include2'])
def __init__(self, environment):
super(RelativeInclude, self).__init__(environment)
self.matcher = re.compile("\.*")
def parse(self, parser):
node = parser.parse_include()
template = node.template.as_const()
if template.startswith("."):
# determine the number of go ups
up = len(self.matcher.match(template).group())
# split the current template name into path elements
# take elements minus the number of go ups
seq = parser.name.split("/")[:-up]
# extend elements with the relative path elements
seq.extend(template.split("/")[1:])
template = "/".join(seq)
node.template.value = template
return node