Django 'could not parse remainder' on related model call in template - django

When I try to call this in my template
{% if member.departments.relationship(department).is_manager is True %}
I get this error
Could not parse the remainder: '(department).is_manager' from 'member.departments.relationship(department).is_manager'
But the same call works when I debug my view
(Pdb) member.departments.relationship(department).is_manager
True
Here is my view
def department_detail(request, uid):
department = Department.nodes.get(uid=uid)
return render(request, 'department/detail.html', {'department': department,})

To solve it, I made use of templatetags. Fun and powerful to learn:
Added /templatetags to same directory as models.py
Added /templatetags/init.py
Added /templatetags/department_extras.py
department_extras.py:
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
#register.filter
def check_is_manager(member, department):
if member.departments.relationship(department).is_manager is True:
html_response = """<i class="fa fa-user-tie" title="Manager"></i>"""
else:
html_response = ""
return mark_safe(html_response)
then in template:
{% load department_extras %}
{{member|check_is_manager:department}}
Disclaimer: If I was using a standard ORM then I would not have this problem, and I am only using it for visual effect, not business logic. If it weren't for the fact that 3 views and other models will need the same functionality, then I would have passed in an additional argument to the template.
I like templatetags because they are almost like sub-views that can act as common denominators across views so that you don't have to keep passing in supplemental data in your arguments of each view.

Related

Render template tags when passing as variable in django

I want to pass some html as a string in a variable to a template, which also includes a link: <a href= {%url: 'app:page'%}> If I pass the variable though, the template tags are not invoked. If I use {{variable | safe}} to escape the restrictions, still it is not being called. I think I have a major error in my thinking, or is it just not possible to do this? The idea is to ultimately load those strings from the database to show users customized webpage elements.
view.py:
def custom(request, slug):
link = """<li>Link</li>"""
return render(request, 'basics/custom.html', {'link': link})
and in the webpage custom.html:
{{link | safe }}
You should use format_html to build a HTML fragment for use in your templates, and reverse for resolving a url
from django.urls import reverse
from django.utils.html import format_html
def custom(request, slug):
link = format_html(
'<li>Link</li>',
reverse('app:page'),
)
return render(request, 'basics/custom.html', {'link': link})

Django Custom Filter Tag Returns Nothing

I want to filter the objects in a model using custom filter tag, but I got nothing.
Here is the profile_tag.py in the templatetags folder.
from django import template
register = template.Library()
from ..models import Profile
#register.inclusion_tag('register/profile.html')
def filter_profile(request):
profile = Profile.objects.filter(user=request.user)
return {'profile' : profile}
I loaded the tag in the html file: {% load profile_tags %}
Then I tried to print {{profile.user}} in different ways, but nothing is showing up.
If I have an error, can you fix it or can you give me another solution to filter the model by the user.
note: I don't want to use the Profile.objects.filter(user=request.user) in the views.py file.
Thanks.

Is it possible to preprocess a template to remove its `{% extends %}` tag?

Scenario
I'm building an SPA and need to be able to render 3rd party app templates as partials within some of the pages. So they can't include any of the existing base.html markup.
Problem
I only need this to occur for some of the templates from a specific 3rd party app, as some of its views will still need to be rendered outside of the SPA as individual pages. So i can't just simply create my own base.html file with nothing in it, to prevent the other markup from being added, as this would affect every template.
At the moment, even though i'm not actually changing the templates in any way, i'm having to create like for like duplicates of those specific templates, which simply remove {% extends "base.html" %} from the top.
Question
Is there a way to preprocess only a specific template and remove its {% extends %} tag?
This is what i actually needed...
Any way to make {% extends '...' %} conditional? - Django
... and below is the solution i spent several hours coming up with, until i finally realised the question i actually wanted to ask. I'm going to post it here anyway, just incase someone else finds it useful.
__init__.py
{% extends %} always has to be first in a template, so it's not possible to use {% load %} to load this tag. Instead it must be loaded in the same way as the django tags.
So this needs to go somewhere that will force it to run immediately.
# The `add_to_builtins` function changed module in 1.7
try:
from django.template.loader import add_to_builtins
except ImportError:
from django.template.base import add_to_builtins
add_to_builtins('my_app.templatetags.overriden_tags')
my_app/templatetags/overriden_tags.py
from django import template
from django.template.loader_tags import do_extends, ExtendsNode
register = template.Library()
#register.tag('extends')
def preventable_extends(parser, token):
node = do_extends(parser, token)
return PreventableExtendsNode(node.nodelist, node.parent_name)
class PreventableExtendsNode(ExtendsNode):
def render(self, context):
prevent = context.get('prevent_extends')
if prevent == self.parent_name.var:
return self.nodelist.render(context)
return super(PreventableExtendsNode, self).render(context)
mixins.py
class PreventableExtendsMixin(object):
def __init__(self, **kwargs):
self.prevent_extends = kwargs.pop('prevent_extends')
super(PreventableExtendsMixin, self).__init__(**kwargs)
def get_context_data(self, **kwargs):
context = super(PreventableExtendsMixin, self).get_context_data(**kwargs)
context['prevent_extends'] = self.prevent_extends
return context
Usage
from third_party_app import LoginView
class MyLoginView(PreventableExtendsMixin, LoginView):
def __init__(self, **kwargs):
kwargs['prevent_extends'] = 'base.html'
super(MyLoginView, self).__init__(**kwargs)

How to run Django code saved inside Model instances?

I'm using a custom Django (v1.5.5) model Widget used to contain HTML code (using a TextField) to be placed in certain places of different pages and allow staff users change the contents easily through the Django admin backend.
This approach works perfectly with HTML, CSS and Javascript code. Using the following syntax at the templates:
{{ widget.content|safe }}
However, I'd also like to be able to put Django code (using templatetags, calling methods...) within it, but this Django code does NOT get executed and instead it's directly printed as if it was plain text.
Is there any way to execute Django code using the safe filter? Or am I missing another filter? Maybe I'd have to use a custom filter or templatetag... Any ideas?
Thank you!
Django doesn't have a template tag/filter like that by default. I'm using this in one of my projects:
class IncludeString(ttag.Tag):
template_src = ttag.Arg()
def render(self, context):
data = self.resolve(context)
try:
t = loader.get_template_from_string(data['template_src'])
except Exception, e:
return u"error rendering template: %s" % unicode(e)
return t.render(context)
class Meta:
name = "include_string"
register.tag(IncludeString)
And use it like {% include_string variable_name_with_template_content %}. This implementation uses django-ttag, but I think something like the following should work, too (untested):
#register.simple_tag(takes_context=True)
def include_string(context, template_src):
try:
t = loader.get_template_from_string(template_src)
return t.render(context)
except Exception, e:
return u"error rendering template: %s" % unicode(e)
Based on #sk1p's answer, I've created a Django simple_tag that takes an HTML string (which can contain "Django code") and the context (which is absolutely necessary here). Then I just render it and return. That's it!
I created a file called smart_html.py that contains:
from django import template
from django.template import Template
register = template.Library()
#register.simple_tag(takes_context=True)
def render_html(context, html):
return Template(html).render(context)
Then, from the templates I just have to do:
{% load smart_html %}
{% render_html widget.content %}

Django: How to include modelform?

{% include 'django.contrib.auth.views.login' %}
I don't want to write everything by hand.. I hate this really, django full of automatic stuff.
Goal is to include registration/login.html into base.html, so that I could have this form in every page
If I include only template itself (registration/login.html), problem appears that "form.login", I mean "form" var is not defined because this one comes from VIEW which called when you going to login url. So how can I call that view MANUALLY with include or at least to grab django.contrib.auth.views.login variables by my self in my own view and pass then to base.html?
P.s. It's not just about login form, I think there will be more situations like this
I have found better solution in #django irc.
They called inclusion tags
I'll give you my code, because I got lot's of problem learning new stuff in django =)
file: templatetags/form_login.py
from django import template
register = template.Library()
from django.contrib.auth.forms import AuthenticationForm
#register.inclusion_tag('registration/login.html')
def form_login():
return { 'form': AuthenticationForm() }
Now you can have your form anywhere, this will prerender template and THAT'S IT! no stupid context processors which requires to modify whole project settings.py, which is really sux if you writing stand alone little application..
If you need login-form on every page
Create a context processor:
def login_form_processor(request):
return {
'login_form': LoginForm(request.POST or None)
}
Add it to settings.CONTEXT_PROCESSORS.
Include the template for login form:
{% with login_form as form %}
{% include "registration/login.html" %}
{% endwith %}
You can also make you form lazy-loading, so form will not be created until it is used for the first time.
from django.utils improt functional
def login_form_processor(request):
create_login_form = lambda: LoginForm(request.POST or None)
return {
'login_form': functional.lazy(create_login_form, LoginForm)
}
But I guess you won't want the lazy-loading feature, because login-form is cheap to initialize.
Reusing views
Concerning the "grabbing variables" part from your question: you cannot grab variable from view. Django view is method which returns response object. You can not get variables from response. However some of views accept extra_context and other attributes. Those attributes allow you to configure those views in urls, or to wrap them with your own view, for example:
def my_login_view(request):
some_extra_data = get_some_data()
extra_context = {
'some_extra_var': some_extra_data
}
return login_view(request, extra_context=extra_context, template="my_template.html")
This is not exactly grabbing the variables from views, more like augmentation of existing views.
If you expect to have more situations like this, do less data-porcessing in views. Call some methods which checks for permissions. Collect some data from context-processors. Return rendered response. Now you can reuse the data in other views.
You can specify the action on the form html to point to the URL that accesses the corresponding view.
If you want a form, say called as login_form always populated in all templates, then put it in the context_processors.
Browsing the code for django.contrib.auth.views, you will see that the variables form, site and *site_name* are passed to the template.
Either you (1) provide your custom registration form or (2) you can just import django.contrib.auth.forms.AuthenticationForm in your view if you want to use it.