How to generate absolute urls in Django 2 templates - python

I have an html template, which is used to render an email, in that template, I want to attach verification links.
I am using the following code to generate the link
{% url 'verify_email' token=token email=email %}
but this one generates following URL instead of absolute URL.
I read this SO thread
How can I get the full/absolute URL (with domain) in Django?
and some initial google results but all of them seems old and not working for me.
TLDR: How do I generate absolute URLs in Django2 template files

You can use the build_absolute_uri() referenced in the other thread and register a custom template tag. Request is supplied in the context (that you enable via takes_context) as long as you have django.template.context_processors.request included in your templates context processors.
from django import template
from django.shortcuts import reverse
register = template.Library()
#register.simple_tag(takes_context=True)
def absolute_url(context, view_name, *args, **kwargs):
request = context['request']
return request.build_absolute_uri(reverse(view_name, args=args, kwargs=kwargs))
More on where and how to do that in the docs.
Then you can use the tag in your template like this:
{% absolute_url 'verify_email' token=token email=email %}

Related

How to show images in Django Template with out static?

I have an API which gives me a list of movies, and there images are in HTTP links like:
https://d2gx0xinochgze.cloudfront.net/5/public/public/system/posters/12/thumb/Tevar-Movie-Opening-Day-Box-Office-Collection-Report-1140x752_1482917123.jpg
So in django we use static file {% static 'image.jpg' %} to display images in templates, But how to display api images links in template?
{% static 'image.jpg' %} doesn't magically make the image show up in the website, django renders the template with what its been given. Static means take a look at the statics folder, search for the given parameter as a file, and get the url for that given static asset.
In your particular case, you say you get the url of the images. You can easily add those urls to context, which is then gonna be a part of the rendering process.
For a function view it would roughly look like this.
def view(request, *a, **kw):
template = loader.get_template('my_template.html')
context = {'my_image': get_image_from_api()}
return HttpResponse(template.render(context, request))
For a class based view it would roughly look like this.
class MyView(TemplateView):
template_name = 'my_template.html'
def get_extra_context(self):
_super_context = super().get_extra_context()
context = {'my_image': get_image_from_api()}
return {**_super_context, **context}
For both of these views, which are roughly the same; you now can use {{my_image}} inside the template to refer to the url. Hope it helps!
Fetch the images via python in views per say. Pass them through your context when rendering the page. Use django's template tags to put the URLs within tags.

I want to use the if statement based on the existence of a web page

So basically i have a complicated scenario. I am current using Django to build a website and i have current made two apps. Both apps have almost identical fields. The field I would want to focus on though is the Information field(which they both have and which i have auto generated with the help of the Wikipedia model)
So the case here is that I want to create an if and else statement in the html in such a way that if the page i am hyperlinking to exists it would go to the link dealing with DetailView but if it doesnt exist I would redirected to the Create View
I should also note that the two apps have their names linked with the help of the foreign key but when i try to open information links using the same name , they gave me different pks
I dont feel like i explained my problem well enough but I hope someone can understand what i mean
UPDATE
ok I create the get function using
def get(self, request, *args, **kwargs):
try:
self.object = self.get_object()
except Http404:
return redirect('/create/')
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
but i dont know how to use the CreateView fuction i created instead of the link i put
This is the Detail View Html
{%extends "home.html"%}
{%block head_title%} {{block.super}}{%endblock head_title%}
{% block content%}
<!-- verify authentication -->
{% if request.user.is_authenticated%}
<h3>{{object.title}}</h3><br/>
{% endif %}
<ul type="disc">
<div class="container">
<li><b>Artist: </b>{{object.Summary}}</li>
<li><b>Genre: </b>{{object.Genre}}</li>
<li><b>Bio: </b><br>{{object.Bio}}</li>
EDIT
</div>
</ul>
{%endif%}
{% endblock %}
This is my model
from django.db import models
from django.conf import settings
from Blog.models import MainPage
from django.urls.base import reverse
from Blog.Retrieve import retriever
from django.db.models.signals import pre_save,post_save
import InfoPedia
class InfoPedia(models.Model):
user =models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
Name =models.ForeignKey(MainPage,on_delete=models.CASCADE)
Location =models.CharField(max_length= 50,null=True,blank=True)
Information =models.TextField(null=True,blank=True)
TrackListing=models.TextField(null=True,blank=True)
Published=models.BooleanField(default=True)
Timestamp=models.DateTimeField(auto_now=True)
Updated=models.DateTimeField(auto_now=True)
def get_absolute_url(self):
# return f"/Blog/{self.slug}"
return reverse('InfoPedia:DetailView', kwargs={"pk":self.pk})
class Meta:
ordering=["-Updated","-Timestamp"] #orranges in order of updated
def get_tracklist(self):
return self.TrackListing.split(",")
def Information_create_pre_save( instance, sender, **kwargs):
instance.Information=retriever(instance.Name)
def rl_post_save_reciever(sender, instance,created,*args,**kwargs):
print("saved")
print(instance.Timestamp)
pre_save.connect(Information_create_pre_save, sender=InfoPedia)
post_save.connect(rl_post_save_reciever, sender=InfoPedia)
An alternative - rather than checking the if/else in the HTML, just make all the links to the DetailView URL.
Then, in the get() handler for the DetailView, you perform a queryset lookup for the object. If no object is found, then instead of displaying the DetailView HTML, return to the user a 302 redirect (i.e. a temporary redirect) to the CreateView for that object. So all your if/else logic is in the view function or class, instead of HTML.

How can a custom jinja2 tag interface with the context of a flask request

I'm writing a custom jinja2 extension to use in flask applications and I'm looking for a way to access the templates context data using the tag I'm implementing. That is, I want the extension tag to use context params passed into the template:
#app.route('/users/<user_id>')
def user_page(user_id):
...
return render_template('users/index.html', user_id=user_id, active=True)
The template:
<!-- I want this tag to see the value of user_id and active -->
{% my_jinja2_tag %}
I know I can render the context variable using {{ user_id }}, but what I'm looking for is a way to inspect the context of the template rendering a custom jinja2 extension. Is that doable? thanks.
ContextReference
Yes, it's possible using jinja2.nodes.ContextReference(). See the API reference here.
Relax, I'm going to guide you through it. :)
First the extension:
class ActiveCheckerExtension(jinja2.ext.Extension):
"""
This will give us a {% check_active %} tag.
"""
template = 'Active is : %s'
tags = set(['check_active'])
def _render_tag(self, context, caller):
return jinja2.Markup(self.template % unicode(context['active']))
def parse(self, parser):
ctx_ref = jinja2.nodes.ContextReference()
lineno = next(parser.stream).lineno
node = self.call_method('_render_tag', [ctx_ref], lineno=lineno)
return jinja2.nodes.CallBlock(node, [], [], [], lineno=lineno)
Then let's add it to Flask's jinja2.
app.jinja_env.add_extension(ActiveCheckerExtension)
Now in your template, you can do:
{% check_active %}
Make sure active is defined in all the templates you add the tag to, or else you'll get a KeyError because the context won't have that template variable.

How to add button next to Add User button in Django Admin Site

I am working on Django Project where I need to extract the list of user to excel from the Django Admin's Users Screen. I added actions variable to my Sample Class for getting the CheckBox before each user's id.
class SampleClass(admin.ModelAdmin):
actions =[make_published]
Action make_published is already defined. Now I want to append another button next to Add user button as shown in fig. . But I dont know how can I achieve this this with out using new template. I want to use that button for printing selected user data to excel. Thanks, please guide me.
Create a template in you template folder: admin/YOUR_APP/YOUR_MODEL/change_list.html
Put this into that template
{% extends "admin/change_list.html" %}
{% block object-tools-items %}
{{ block.super }}
<li>
Export
</li>
{% endblock %}
Create a view function in YOUR_APP/admin.py and secure it with annotation
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required
def export(self, request):
... do your stuff ...
return HttpResponseRedirect(request.META["HTTP_REFERER"])
Add new url into YOUR_APP/admin.py to url config for admin model
from django.conf.urls import patterns, include, url
class YOUR_MODELAdmin(admin.ModelAdmin):
... list def stuff ...
def get_urls(self):
urls = super(MenuOrderAdmin, self).get_urls()
my_urls = patterns("",
url(r"^export/$", export)
)
return my_urls + urls
Enjoy ;)
The easy and accepted way is to override the template.
If you don't want to mess with the Django templates, you could add a Media class to your admin and add some javascript to create the button although I think creating elements with javascript is a bit nasty and should be avoided.
Though other answers are entirely valid, I think it is important to note that it is absolutely not necessary to add a button to get such behavior. You can use admin actions, as you did for the make_published action.
This as the advantage of not requiring to override any template, and thus prevent from potential troubles when upgrading django version (as admin templates may change, and changes might not be "compatible" with the way you overrode it).
import csv
from django.http import HttpResponse
from django.utils import timezone
def export_as_csv(modeladmin, request, queryset):
opts = modeladmin.model._meta
filename = format(timezone.now(), "{app}_{model}-%Y%m%d_%H%M.csv").format(
app=opts.app_label, model=opts.model_name)
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
writer = csv.writer(response)
field_names = [f.get_attname() for f in opts.concrete_fields]
writer.writerow(field_names)
for obj in queryset.only(*field_names):
writer.writerow([str(getattr(obj, f)) for f in field_names])
return response
Admin actions are made for this, adding a custom button is one step closer to "over-customization", which means it's probably time to write your own views.
The admin has many hooks for customization, but beware of trying to use those hooks exclusively. If you need to provide a more process-centric interface that abstracts away the implementation details of database tables and fields, then it’s probably time to write your own views.
Quote from the introduction paragraph of Django Admin's documentation

render cms page within another page

Im trying to render a cms page, within another page using a custom cms plugin.
The this is my plugin class:
class PageInDiv(CMSPlugin):
page_div = models.ForeignKey(Page, verbose_name= "page")
def __unicode__(self):
return self.page_div.get_title()
as you can see all it does is link the plugin to a page then on my cms_plugins.py i have
class PageInDivPlugin(CMSPluginBase):
model = PageInDiv
name = _("Page in div")
render_template = "page.html"
admin_preview = False
def render(self, context, instance, placeholder):
temp = loader.get_template(instance.page_div.get_template())
html = temp.render(context)
context.update({
'html': html,
'title':instance.page_div.get_title(),
'placeholder':placeholder,
})
return context
as you can see i pass the html for the provided page to the plugin template, then the plugin template is rendered within the page thats hosting the plugin.
The problem i am having is that the placeholder content from the page thats selected via foreignkey is not being rendered ( displayed ).
So my question is, is there a way to render a pages placeholders programatically ?
Just for a moment ignoring the idea of creating a custom plugin in order to do what you describe (ie, render a page's placeholders programatically), the following might be a viable alternative, depending on what exactly you are trying to achieve...
You should be able, just in the template for your "outer" cms page (ie, the page within which you want to display the contents of another cms page), to get access to the current page like this:
{{ request.current_page }}
This is by virtue of the cms page middleware. So taking that a step further, you should be able to access the page's placeholders like this:
{% for placeholder in request.current_page.placeholders %}
{{ placeholder.render }}
{% endfor %}
That's one way you could go about rendering a page's placeholders "inside" another page.
I needed to render another page from within the template which could be accomplished with:
#register.simple_tag(takes_context=True)
def render_page(context, page, default="base.html"):
if not page:
return loader.get_template(default).render(context)
new_context = copy(context)
new_context['request'] = copy(context['request'])
new_context['request'].current_page = page
new_context['current_page'] = page
new_context['has_change_permissions'] = page.has_change_permission(context['request'])
new_context['has_view_permissions'] = page.has_view_permission(context['request'])
if not new_context['has_view_permissions']:
return loader.get_template(default).render(context)
return loader.get_template(page.get_template()).render(new_context)

Categories

Resources