Django get the static files URL in view - django

I'm using reportlab pdfgen to create a PDF. In the PDF there is an image created by drawImage. For this I either need the URL to an image or the path to an image in the view. I managed to build the URL but how would I get the local path to the image?
How I get the URL:
prefix = 'https://' if request.is_secure() else 'http://'
image_url = prefix + request.get_host() + STATIC_URL + "images/logo_80.png"

# Older Django <3.0 (also deprecated in 2.0):
from django.contrib.staticfiles.templatetags.staticfiles import static
# Django 3.0+
from django.templatetags.static import static
url = static('x.jpg')
url now contains '/static/x.jpg', assuming a static path of '/static/'

EDIT: If you're on Django >=3.0, refer to Django get the static files URL in view instead. This was answered with Django 1.X version.
dyve's answer is good one, however, if you're using "cached storage" on your django project and final url paths of the static files should get "hashed"(such as style.aaddd9d8d8d7.css from style.css), then you can't get a precise url with django.templatetags.static.static(). Instead, you must use template tag from django.contrib.staticfiles to get hashed url.
Additionally, in case of using development server, this template tag method returns non-hashed url, so you can use this code regardless of that the host it is development or production! :)
from django.contrib.staticfiles.templatetags.staticfiles import static
# 'css/style.css' file should exist in static path. otherwise, error will occur
url = static('css/style.css')

From Django 3.0 you should use from django.templatetags.static import static:
from django.templatetags.static import static
...
img_url = static('images/logo_80.png')

here's another way! (tested on Django 1.6)
from django.contrib.staticfiles.storage import staticfiles_storage
staticfiles_storage.url(path)

Use the default static tag:
from django.templatetags.static import static
static('favicon.ico')
There is another tag in django.contrib.staticfiles.templatetags.staticfiles (as in the accepted answer), but it is deprecated in Django 2.0+.

#dyve's answer didn't work for me in the development server. Instead I solved it with find. Here is the function:
from django.conf import settings
from django.contrib.staticfiles.finders import find
from django.templatetags.static import static
def get_static(path):
if settings.DEBUG:
return find(path)
else:
return static(path)

If you want to get absolute url(including protocol,host and port), you can use request.build_absolute_uri function shown as below:
from django.contrib.staticfiles.storage import staticfiles_storage
self.request.build_absolute_uri(staticfiles_storage.url('my-static-image.png'))
# 'http://localhost:8000/static/my-static-image.png'

In short words you need to get
STATIC_URL
STATIC_ROOT
urlpatterns
staticfiles
templatetags
url parameters
All in the right place, to get this working. In addition, in real time deployment, circumstances vary, which it is very possible that the current setting you spent 3 hours worked out, works on your local machine but the server.
So I adopted the traditional way!!
app
├── static
│ └── json
│ └── data.json
└── views.py
views.py
import os
with open(os.path.abspath(os.getcwd()) + '/app/static/json/data.json', 'r') as f:
pass

Related

How to redefine url_for in flask by automatically adding a string '/foo' as a prefix of the url generated from url_for?

It may look very odd, but this is what I want to achieve exactly, it is not related with blueprints or add prefix to all routers. thanks!
You can define your own:
# url_for_custom.py
import flask
def url_for(endpoint, **values):
url = flask.url_for(endpoint, **values)
if not values.get('_external', False):
url = '/foo' + url
return url
If you wish to change it globally, you can monkey-patch some time early on:
# __init__.py
import flask
from url_for_custom import url_for
flask.url_for = url_for
Although it's perhaps better to simply import and use your own implementation.

How to serve another static root in Django for html pages?

The typical configuration serves two static roots:
http://www.example.org/static/
http://www.example.org/media/
This is STATIC_URL and MEDIA_URL.
I would like to add a third one to host static files build with Sphinx:
http://www.example.org/docs/
I know I could configure this on the level of the web server. Is it also possible to configure this on the level of Django?
Here is my python package, that implements Sphinx with Django templates and renders it to static pages. A kind lightweight read-the-docs. Still in an early state, yet working.
https://github.com/elmar-hinz/Django.SphinxCMS
How about building a url just like you do for the static files? The DOCS_ROOT setting should be a string in your settings.
from django.urls import re_path
from django.views.static import serve
from django.conf import settings
urlpatterns += [
re_path(r'^docs/(?P<path>.*)', serve, {'document_root': settings.DOCS_ROOT})
]

Django settings: How to access variables from the settings folder in an app

I have a Django project with the following structure:
--|src
--project
--|settings
--__init__.py
--production.py
--local.py
--|app1
In my app I import the settings (from django.conf import settings) and then as I was following a tutorial they said to do this getattr(settings, VARIABLE). That doesn't work for me. Instead I can do this: settings.VARIABLE. What's the difference?
Oh and I ran type(settings) and it outputted <class 'django.conf.LazySettings'>.
in order to access variables in settings.py file, you can do like this:
for example, I define STATIC_ROOT variable in settings.py file like this:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'static', 'static_root')
and I can access to this variable like this:
from django.conf import settings
document_root=settings.STATIC_ROOT
The difference is that for various reasons (see the documentation for details), the settings object is not loaded unless an object is referenced from it.
The LazySettings object is special and you have to access it with settings.SOMETHING.
The reason its called "Lazy" is because the entire object is not loaded and made available to you when you import it. This LazySettings object acts like a proxy to the actual settings object.
project DIR
--|app DIR
--|settings.py <<< your variable API_KEY = '28234-jns-23-23n'
from app.settings import API_KEY

Read static file in view

To integrate Django and Ember, I have decided to serve my Ember SPA in a Django view (avoids CORS issues, only one server for frontend and API, etc). I do it like this:
# urls.py
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^api/', include(api_urls, namespace='api')),
...
url(r'^$', views.emberapp, name='emberapp'),
...
]
# views.py
from django.http import HttpResponse
def emberapp(request):
# The Ember frontend SPA index file
# This only works in development, and is anyway hacky
EMBER_FE_INDEX_HTML = '/absolute/path/to/my/frontend/static/fe-dist/index.html'
template_file = open(EMBER_FE_INDEX_HTML)
html_content = index_file.read()
index_file.close()
return HttpResponse(html_content)
The index.html is part of the static assets. In development this is very easy:
The index.html is directly accessible to the Django application in the file system
I know the absolute path to the index file
But in production things are more complex, because the static assets are not local to the django application, but accessible on Amazon S3. I use django-storages for that.
How can I read the contents of a static file from a view, in a generic way, no matter what backend is used to store/serve the static files?
First, I don't think the way you do it is a good idea.
But, to answer your question: In your settings.py, you likely have defined the directory where Django will collect all static files.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
So in your view, you just need to fetch the file os.path.join(settings.STATIC_ROOT, 'index.html')
That said, you should serve index.html via the webserver, same as your static/ files, robots.txt, favicon.ico, etc. Not through Django. The webserver is much faster, uses proper caching, and its just one line in your Nginx or Apache settings, instead of an entire view function in Django.
This is my current solution. Works in development, not sure about production yet (it is a pain that you need to commit untested code to verify production-related code in Heroku)
from django.conf import settings
from django.http import HttpResponse
from django.core.files.storage import get_storage_class
FE_INDEX_HTML = 'fe/index.html' # relative to the collectstatic directory
def emberapp(request):
# The Ember frontend SPA index file
# By getting the storage_class like this, we guarantee that this will work
# no matter what backend is used for serving static files
# Which means, this will work both in development and production
# Make sure to run collectstatic (even in development)
# TODO: how to use this in development without being forced to run collectstatic?
storage_class = get_storage_class(settings.STATICFILES_STORAGE)
# TODO: reading from a storage backend can be slow if assets are in a third-party server (like Amazon S3)
# Maybe streaming the static file from the server would be faster?
# No redirect to the Amazon S3 asset, please, since the Ember App needs to
# run from the same URL as the API, otherwise you get CORS issues
with storage_class().open(FE_VWORKS_INDEX_HTML) as index_file:
html_content = index_file.read()
return HttpResponse(html_content)
Or, to reply with an StreamingHttpResponse, which does not force Django to read the whole file in memory (and wait for it to be read):
def emberapp(request):
# The Ember frontend SPA index file
# By getting the storage_class like this, we guarantee that this will work
# no matter what backend is used for serving static files
# Which means, this will work both in development and production
# Make sure to run collectstatic (even in development)
# TODO: how to use this in development without being forced to run collectstatic?
storage_class = get_storage_class(settings.STATICFILES_STORAGE)
index_file = storage_class().open(FE_INDEX_HTML)
return StreamingHttpResponse(index_file)

Django favicon.ico in development?

How can I serve favicon.ico in development? I could add a route in my urlconf, but I don't want that route to carry over to the production environment. Is there a way to do this in local_settings.py?
The easiest way would be to just put it in your static directory with your other static media, then specify its location in your html:
<link rel="shortcut icon" type="image/png" href="{% static 'images/favicon.ico' %}"/>
My old answer was:
You can set up an entry in your urls.py and just check if debug is true. This would keep it from being served in production. I think you can just do similar to static media.
if settings.DEBUG:
urlpatterns += patterns('',
(r'^favicon.ico$', 'django.views.static.serve', {'document_root': '/path/to/favicon'}),
)
You also could just serve the favicon from your view.:
from django.http import HttpResponse
def my_image(request):
image_data = open("/home/moneyman/public_html/media/img/favicon.ico", "rb").read()
return HttpResponse(image_data, content_type="image/png")
This worked for me:
from django.conf.urls.static import static
...
if settings.DEBUG:
urlpatterns += static(r'/favicon.ico', document_root='static/favicon.ico')
From the docs:
from django.conf.urls.static import static
urlpatterns = patterns("",
# Your stuff goes here
) + static('/', document_root='static/')
There doesn't appear to be a way to serve a single static file, but at least this helper function is a wrapper which only works when DEBUG = True.
I use this:
from django import conf
from django.conf.urls import static
...
if conf.settings.DEBUG:
urlpatterns += static.static(
r"/favicon.ico", document_root=conf.settings.STATIC_ROOT / "favicon.ico"
)
Well, you can create your own loader.py file, which loads settings you want to override.
Loading this file should look like this:
try:
execfile(os.path.join(SETTINGS_DIR, 'loader.py'))
except:
pass
and be added at the end of settings.py.
This settings should not be commited into production server, only should appear on development machines. If you are using git, add loader.py into .gitignore.