Reference Wagtail root page in templates - templates

Currently I display a link to my sites homepage in the base template using this code :
{% if page.slug != 'homepage' %}
<!-- insert link code here -->
{% endif %}
However I like to reuse my code and sometimes the page is called something else. What I would like to write is something like :
{% if page != request.site.root_page %}
However this does not work, although if I display the values of these fields in the template they are both equal or not as expected ...
<p>{{ request.site.root_page }} and {{ page }}<p>
Results in :
<p>Contact and Contact</p> # On the root page
or
<p>Contact and Test</p> # On a different page
The opposite problem to this as the data is not specific to one page, but to every page except one.
What am I missing?

Okay in the process of explaining the question, I also found the answer. It doesn't work (I believe) because although these values look the same, they are actually objects with different properties, therefore the comparison fails.
In order for the comparison to succeed you need to extract values from each object and compare them. So this will not work ...
{% if page != request.site.root_page %}
However both of these do work ...
{% if page.title != request.site.root_page.title %}
... or ...
{% if page.url != request.site.root_page.url %}
Kicking myself now, but hopefully my public humiliation will help someone in the future.

Related

Django - the effect on page load speed of using include tag inside for loop

The odds are that this question will be banned, because this forum seems to me a site for "why it doesn't work"-type of questions, rather than "is it a good idea to do what I do" ones. And yet, I am very much concerned about preserving DRY-ness in my code.
I have a Django template which looks like this:
<ol id = 'task_list'>
{% for item in qs %}
{% include 'list_item.html' with item=item %}
{% endfor %}
</ol>
list_item.html:
<li>
{{item}}
</li>
The advantage (at least, for me) of this code: it positively affects DRYness when I have a ajax code which posts a request for creating new items of the list and renders them on the client side subsequently:
JS:
$.post('my_view_url', function(response)
$('#container').append(response);
Django view:
def my_view(response)
#...
return render_to_response('list_item.html',....)
This way, list_item.html helps me use the same HTML code for both initial rendering of existing elements and client-side rendering of newly created items.
The disadvantage is that {% include %} is known to be rather slow.
The question: Is this code pattern not a performance killer in case of paginated rendering of large arrays of data ?
Additional note:
AFAIK, {% block %} is faster than {% include %}. But I've got no idea how to rewrite the code pattern using block.

Django template custom tag as boolean

This seems such a simple thing but I can't seem to get it to work for one, and two I can't seem to be getting a straight forward answer online on wether it can or can't be done.
I just want a simple tag that will work like so
{% if my_tag %}
render something
{% else %}
render something else
{% endif %}
now I don't care about filters or any other ways you might be able to do the same thing, I want it to look exactly like that, and work like described, I have a simple tag made that actually does return True or False as needed, and it's called if I call the tag like this
{% my_tag %}
however it does't get called if I add an if in front of the tag, is a feature this simple and logical not implemented?
Like the link to the potential duplicate states, you can use an assignment tag similar to the following that will return the current time:
#register.assignment_tag
def get_current_time(format_string):
return datetime.datetime.now().strftime(format_string)
Within your template you can then do what you are desiring to do:
{% if get_current_time %}
...show time
{% else %}
...don't show time
{% endif %}

Django Pass Multiple Parameters to Custom Template Filter Inside If Statement

I have an issue. I've written a custom template tag with a function signature like this-
def has_paid_for_article(article, request):
Now, in my template tag I have a conditional statement to determine whether a user can download an article or not (this is determined by if the article is older than two years or the logged in user has paid for the article). Here's the snippet-
{% if article|is_older_than_two_years %}
<span class="amp">& </span>{% get_article_download_link article %}
{% else %}
download
{% endif %}
The aforementioned snippet works fine, however I need to call the has_paid_for_article() function inside of a conditional statement. I've tried the following ways to make this happen-
{% if article|is_older_than_two_years or article|request|has_paid_for_article %}
,
{% if article|is_older_than_two_years or [article, request]|has_paid_for_article %}
This one works outside of the conditional statement-
{% if article|is_older_than_two_years or has_paid_for_article article request %}
What would be the correct syntax here? Also, I've read other posts on the topic, I CANNOT put this logic in the view. I won't go into detail, but with the way it works, that is not an option. Thank you!
Try
{% if article|is_older_than_two_years or article|has_paid_for_article:request %}
See Writing custom template filters

Django: How do I get the number of elements returned in a database call?

This seems to me like a very simple question, but I can't seem to find the answer.
All I need to do is determine the number of objects returned by a database query.
The specific circumstance is this: I have a model named Student. This model has a ManyToManyField member named courses_current, which relates to a table of Course models. When I pass my Student instance to a template, I want to be able to do something like the following (the syntax may not be exact, but you'll get the basic idea):
<div id="classes">
{% if student.classes_current.all.size == 0 %}
<h1> HEY! YOU AREN'T TAKING ANY CLASSES! REGISTER NOW!
{% else %}
Here are your courses:
<!-- ... -->
{% endif %}
</div>
Now, I'm fairly certain that X_set.all.size is not a real thing. In the manage.py shell I can just use len(student.classes_current.all()), but I don't know of any way to use built-in functions, and "dictionary-like objects" don't have .size() functions, so I'm at a loss. I'm sure there's a very simple solution (or at least I hope there is), but I can't seem to find it.
{{ student.classes_current.all.count }} but be warned that it doesn't fetch the objects so you will need to do a separate query if you want to loop over them.
If you need loop over the classes for tag has way to get what you need.
{% for cl in student.current_classes.all %}
{{ cl }}
{% empty %}
<h1>Hey! ...</h1>
{% endfor %}
Documentation https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#for-empty

What's the best way to allow an infinite amount of foreign keys to the same model?

I'm making a forum with django right now, and I want it so that anyone can comment on anyone else's comment. Right now I have a foreign key in my 'Comment' model that points back to itself so I can get the parent comment easily from any comment.
In my theory this worked great, because from any comment I could get all of its child comments, and then just keep branching down from there to get every single child comment. However I'm having trouble actually implementing this when it comes to getting the data from the view to the template.
I want it to be possible to have an infinite number of child comments, because who knows how long a discussion will last and I don't want to arbitrarily limit it. The problem I'm having is would you get all of those comments from the view to the template without losing their relationship to their parent comment?
Currently this is what the psuedocode for my code looks like:
#the view
def comment_page(request, forum, comment_id):
#this is the main comment that all others will stem from
main_comment = Comment.objects.get(id=comment_id)
children_comments = main_comment.comment_set.all()
#the template
{% for comment in children_comments %}
<p class='comment'>{{comment}}</p>
{% endfor %}
Obviously I'm not even trying to get all the child comments here, it just gets child comments of the very first post. What I don't understand is how can I then go through each of these child comments and then get all of theirs, and keep doing that for each new comment?
It makes the most sense to do it in the view since I am able to use Django's QuerySet API in there, but I don't see how I would be able to pass all of the comments to the template without losing their relationship to their parent. The only idea I can think of is to go through all of the comments in the view and build up a string of html that I just pass and simply display in the template, but that seems like a horrible idea because it'd be dealing with template related stuff in the view.
You might want to look into using a MPTT such as django-mptt
this can be implemented by a custom filter with an inclusion_tag that includes itself but causes a lots of queries to your db:
#register.inclusion_tag('children.html')
def comments_tree(comment):
children = comment.comment_set.all()
return {'children': children}
# children.html
<ul>
{% for child in children %}
<li> {{ child }}</li>
{% if child.comment_set.count > 0 %}
{% comments_tree child %}
{% endif %}
{% endfor %}
</ul>
# your template
{% comments_tree comment %}
This older question is probably of interest:
How can I render a tree structure (recursive) using a django template?
Edit: to future readers, don't do this as the inner for loop's comment variable does not substitute the outer comment variable during the loop execution, leading to infinite recursion. /Edit
If you need a recursive tree structure in your HTML page (i.e. a bunch of nested <div> tags), you can write a recursive "comment" template.
Sample: (untested)
{# comment.html #}
<p class='comment'>{{ comment.text }}</p>
{% if comment.children %}
{% for comment in comment.children %}
{% include "comment.html" %}
{% endfor %}
{% endfor %}
The for loop binds the comment template variable to each child before including itself.
Performance note: Unless your comment sets are often short, this will probably be very slow. I recommend that you make your comments non-editable and cache the result!
Alternate solution: If you don't need the recursive HTML <div> tags, you can write a generator that performs a pre-order traversal of the structure and yields (depth, comment) pairs. This would likely be far more efficient in rendering speed.