Django Get absolute url for static files - python

In Django, when I use:
{{ request.build_absolute_uri }}{% static "img/myimage.jpg" %}
It produces: 'http://myurl.com//static/img/myimage.jpg'. This produces an error.
How can I remove the double slashes?
The STATIC URL is:
STATIC_URL = '/static/'
But I don't think removing the first '/' would be a good idea.

The request object is available within your templates and you can easily access attributes such as request.scheme or request.META.HTTP_HOST to construct your base URL that you can prepend ahead of your static URL to get the full URL.
Final example would look something like this:
<img src="{{request.scheme}}://{{request.META.HTTP_HOST}}{% static 'img/myimage.jpg' %}">

The build_absolute_uri method builds an absolute uri for the current page. That means that if you're on e.g. 'http://myurl.com/login/', the resulted full url would be 'http://myurl.com/login//static/img/myimage.jpg'.
Instead, use request.get_host() (optionally together with request.scheme for the url scheme), or preferably, use the sites framework to set a template variable to the current site domain. The get_host() method has some issues regarding proxies.
The get_host() method will return the current domain without a path appended.

I just made a quick template tag for doing this. Create files /myapp/templatetags/__init__.py and /myapp/templatetags/my_tag_library.py, if you don't have them already, and add the following to my_tag_library.py:
from django import template
from django.templatetags import static
register = template.Library()
class FullStaticNode(static.StaticNode):
def url(self, context):
request = context['request']
return request.build_absolute_uri(super().url(context))
#register.tag('fullstatic')
def do_static(parser, token):
return FullStaticNode.handle_token(parser, token)
Then in your templates, just {% load my_tag_library %} and use e.g. {% fullstatic my_image.jpg %}.
In response to earlier comments wondering why someone would need to do this, my particular use case was that I wanted to put a link to a static file inside of an open graph protocol meta tag, and those links need to be absolute. In development the static files get served locally, but in production they get served remotely, so I couldn't just prepend the host to get the full url.

Is this worth an update (Django 2+)?
This helped me specifically because I was trying to put a query in the link, i.e. the myimage.jpg was actually pulling from the DB. I needed a way to put it in the src, which was to replace 'myimage.jpg' with {{ img_link_field_in_model }}.
<img src="{% get_static_prefix %}img/myimage.jpg">
will produce:
<img src="/static/img/myimage.jpg">
The example of the query is:
<img src="{% get_static_prefix %}img/{{img_link_from_model}}">

Use this for apps:
{{ request.build_absolute_uri|slice:":-1" }}{% static "img/myimage.jpg" %}
Use this generally:
{{ request.scheme }}://{{ request.META.HTTP_HOST }}{% static "img/myimage.jpg" %}

Not entirely sure what you're asking, but since the {% static .. %} is only adding /static/ to the front of your path you specify, you could just do that yourself:
{{ request.build_absolute_uri }}static/img/myimage.jpg
Not very modular, but then again most times you don't need direct access to the full url since it will just append it onto whatever url you're at if you use it as a src for some html object.

build_absolute_uri takes the location as an argument which handles the double slash problem.
Unfortunately you cannot pass arguments via the django template language.
You will need to build a custom template tag or filter which accepts an argument to the build_absolute_uri function.
One of the many reasons I prefer Jinja as I can just do this:
{{ request.build_absolute_uri(static('img/foo.png')) }}

Related

How to use autoescape off AND static variables inside django template

I'm using auto generated HTML which has been saved to a file and then read in again to use as part of a page in a django template.
In order to do this I have used:
{% autoescape off %}
{{ my_html }}
{% endautoescape %}
However, in the my_html variable, I have some static content. This comes in the form of something like this:
<img src="{% static "img/tree_report.png" %}" width="12px" height="12px"/>
My issue is that the static content is not displayed. I get:
http://127.0.0.1:8000/%7B%%20static 404 (in the browser error report)
I read something about get_static_prefix in another question but that doesn't solve my problem because I just get this instead:
GET http://127.0.0.1:8000/%7B%%20get_static_prefix%20%%7Dimg/tree_report.png 404 (Not Found)
I also tried endautoscape turning on and off periodically in my_html in the saved HTML variable. That also didn't work.
Should I be autogenerating the development and production static files paths for my_html or is there a more elegant solution to this problem?
Any suggestions are most welcome.
Thanks.

How can I get the base url with Flask / Jinja2?

I have a base.html template which I would like to use for all pages. This base.html contains a navigation
<nav>
<li>Home</li>
<li>bar</li>
</nav>
This is no problem when I'm on the same level (e.g. localhost:5000/whatever), but when I'm in a subfolder (e.g. localhost:5000/whatever/insert) the links break.
This can be fixed by making the relative links absolute, e.g.
<nav>
<li>Home</li>
<li>bar</li>
</nav>
However, I don't know how to get the base_url. If possible, I would like to avoid adding base_url to each render_template call. And, if possible, I would also like to avoid to set base_url manually.
How is this problem solved with Flask / Jinja2?
Don't worry about a base url; if home and foo are routes in your Flask app, use the url_for() function to build your URLs instead:
<nav>
<li>Home</li>
<li>bar</li>
</nav>
Also see the URL Building section of the Flask Quickstart documentation:
Why would you want to build URLs using the URL reversing function url_for() instead of hard-coding them into your templates?
Reversing is often more descriptive than hard-coding the URLs.
You can change your URLs in one go instead of needing to remember to
manually change hard-coded URLs.
You can change your URLs in one go instead of needing to remember to manually change hard-coded URLs
URL building handles escaping of special characters and Unicode data
transparently.
The generated paths are always absolute, avoiding unexpected behavior of relative paths in browsers.
If your application is placed outside the URL root, for example, in
/myapplication instead of /, url_for() properly handles that for you.
If you have elements like,meta, a navbar, etc in your base.html that you would like to display across all pages in your site. You can type this at the top of each new page.
{% extends "base.html" %}
{% block content %}
<!-- write page specific html between here -->
{% endblock %}
its important to place
{% block content %}
{% endblock %}
within you base.html file.

Django models - html/render attribute?

I use the same models in many different templates and tables. I'm looking for a way to tell Django how to render those objects in templates so I don't have to write the same html again and again.
For example model Url - when I want to display it inside a template or table, I have to write (sometimes much more):
{{ url.site.name }}
which renders:
Stackoverflow.com
It would be better if I could just do something like:
{{ url }} # url is Url model object
I think that I can add methods like def render_object(self) like this:
def render_object(self):
return mark_safe("""{}""".format(self.url,self.site.name))
and in templates:
{{ url.render_object }}
but I'm curious if it is there some built in function. As far as I know __unicode__(self) would do the work but it would mess admin and shell display names.
Add custom template tag my_tags.py like render_url and then in your template load that template tag ({% load my_tags %}) and send url object to it: {% render_url url %}. You can use html template in your tag or just make result as a string and return it. Check the documentation for details.

Why use Flask's url_for?

I'd like to know the reasoning behind using url_for to generate links in templates and the application code.
What do I gain by doing this:
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
and this:
<ul>
<li>Home</li>
<li>About Us</li>
<li>Contact</li>
</ul>
Instead of hard coding the paths?
From Flask's documentation,
flask.url_for(endpoint, **values)
Generates a URL to the given endpoint with the method provided.
Variable arguments that are unknown to the target endpoint are
appended to the generated URL as query arguments. If the value of a
query argument is None, the whole pair is skipped. In case blueprints
are active you can shortcut references to the same blueprint by
prefixing the local endpoint with a dot (.).
Now, instead of specifying static urls to reach an endpoint, you can use url_for which does a reverse match for the endpoint. It is particularly useful when you have arguments which you might want to specify at runtime.
{{ url_for('events', user_id=user.id, year=2013) }}
/events/1388224/2013
The paths generated are always absolute (start with a "/") so that they work regardless of the current URL. They also take into account if the application is mounted at a prefix rather than the root (like "/myapp/events/1/2013").
The _external=True argument can be used to generate URLs with the server name so that they can be used externally, such as in email messages.
View your events: {{ url_for("events", user_id=user.id, year=2013, _external=True) }}

Using Django template tag "with" with the result of another template tag

I have a comment_form.html template, which is used in multiple places in my app, and I'd like to be able to pass the endpoint's url into that template from a parent template. Normally I would do this using the with tag:
{% with endpoint='/comments' %}
{% include 'comment_form.html' %}
{% endwith %}
The problem is that I can't use a string literal '/comments' here, but instead I need a url tag, like so: {% url 'blog:comments:comments' username=post.user.username object_id=post.id %}. The with template tag seems to expects a literal or a context variable and doesn't seem to be able to comprehend "use the result of another template tag".
One solution would be to pass the strings 'blog:comments:comments', post.user.username, post.id all separately. But this is a problem because different uses of the comment form may require different arguments to uniquely define the endpoint.
How can I use with with the result of another template tag?
You can't, but you don't need to. The url tag has an alternative syntax that injects is result into the context:
{% url 'blog:comments:comments' username=post.user.username object_id=post.id as endpoint %}

Categories

Resources