Django, custom tag... how? - django

I would like to make a django custom tag to display 10 entry titles from the category where the user is reading an article. How can I do this? I need to pass the category from the actual entry.

The best way to do this would be with an inclusion tag. This is a tag that renders a template fragment which renders the 10 related articles.
You just pass in the current article into the tag, and return the context for the template fragment - ie the related articles.
#register.inclusion_tag('related_articles.html')
def related_articles(article, count):
category = article.category
articles = category.article_set.exclude(id=article.id)[:count]
return {'articles': articles}
You'll need a related_articles.html file in your templates directory which outputs the articles. Then, to call it from your main template, you would just do
{% related_articles article 10 %}
where article is the name of the article object.

Why a custom tag? It's probably better, cleaner, to add a method to the article model, and call that from the template. Depending on your model, the method would be practically trivial.

Check here for making custom tags:
http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#howto-custom-template-tags
You would need to pass the relevant category object to the template context.

Related

Should Django Querysets Be Called in the Templates or Passed Into The Template Context?

I may get blowback from asking this question but it's something I've wondered about for a while so here goes.
In a Django template, you can use .all to get the queryset of things, like so:
{% for prerequisite in object.prerequisites.all %}
<li class="list-group-item">{{ prerequisite.text }}</li>
{% endfor %}
The model in this case looks like this, but the only relevant piece of information is that object above has a relation to the Prerequisite model through the ForeignKey.
class Prerequisite(models.Model):
content = models.ForeignKey(Content, on_delete=models.CASCADE,
related_name='prerequisites')
text = models.CharField(max_length=100)
def __str__(self):
return str(self.text)
My question is
Is it best-practice to call Django querysets in the template, (ie: object.prerequisites.all) or should one pass it in the view through the context?
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['prerequisites'] = self.object.prerequisites.all()
return context
It's really convenient to do querysets in the template, but it seems like it should be business logic located in the view. Does Django have a specific stance on this?
according to django documentation:
https://docs.djangoproject.com/en/4.1/#the-view-layer
The view layer -
Django has the concept of “views” to encapsulate the logic responsible for processing a user’s request and for returning the response.
https://docs.djangoproject.com/en/4.1/#the-template-layer
The template layer -
The template layer provides a designer-friendly syntax for rendering the information to be presented to the user.
i have different example, i know a lot of programmers loves to move buisness logic from views.py to serializer.py, even if original purpose of serialezers life is - to serialize
i believe the most appropriate way is to pass your data through context in the views.
Views originally is all your buisness logic, serializer suppose only serialization, and templates purpose is to show html page, python wasn't originaly desined to manipulate html page

Django: is it safe to include secret data in the context dict used to render a template in a view?

Is data that is included in the context dict passed to the render function but is not called in the template accessible to the user or their browser?
Is it ok to just pass a QuerySet to the template even when some of the fields of the models must be kept secret from the user?
I would appreciate a reference to the official Django documentation or any other reliable sources confirming this is safe, if anyone finds one.
Code Example
models.py:
class Riddle(models.Model):
question = models.TextField() # show to user
solution = models.TextField() # very secret
views.py
def riddles_list(request):
data = Riddle.objects.all()
return render(request, 'riddles.html', {'riddles': data})
riddles.html
<ul>
{% for riddle in riddles %}
<li>{{ riddle.question }}</li>
{% endfor %}
</ul>
Is this safe, or does the user has a way of accessing the solution of the riddle? Is the solution now available anywhere except the Django server?
A possible approach would be to change the query to:
data = Riddle.objects.values('question')
Is this considered better code than just fetching the whole object? (for security, efficiency, readability, maintainability etc.) In the real code there are more calls like filter and annotate in the query, and the models have more fields.
Yes, of course, it is ok as long as you don't render that value to the template.
because the context is only accessible on the backend rendering engine for HTML pages.
whenever the page is rendered and sent to the browser, no one can access it.
Is data that is included in the context dict passed to the render function but is not called in the template accessible to the user or their browser?
The data won't be accessible to the user in their browser as long as you don't render it. Once you render the page, what you see is the resulting string from django rendering the template.
You can read more about it here, specifically this paragraph under Templates.
Django defines a standard API for loading and rendering templates regardless of the backend. Loading consists of finding the template for a given identifier and preprocessing it, usually compiling it to an in-memory representation. Rendering means interpolating the template with context data and returning the resulting string.

Django - Rendering tooltips stored in the database in templates

I'm building a Django site and in various places I want to display a little question mark and if the user hovers over it, it should display a message. I think the best approach is to store the tooltip in the database like this:
class Tooltip(models.Model):
key = models.Charfield(max_length=20)
message = models.Charfield(max_length=300)
My question is what is the best way to get the tooltips in the templates? So far my idea is to create a custom template tag, which will do the lookup and return the result, but I haven't used template tags much and wondering if there is a problem with this approach?
{% tooltip 'message_key' %}
The Django documentation explains how to create template tags.
Could be as simple as:
from django import template
register = template.Library()
#register.simple_tag
def tooltip(key):
try:
tooltip = Tooltip.objects.get(key=key)
return tooltip.message
except Tooltip.DoesNotExist:
return '' # or return a message stating that the key does not exist.
You need to save this file in your app's templatetags/ directory, along with an __init__.py. You can then {% load "<appname>" %} in your template and use the tag like you suggested.
While you might not need a database to store these, if you do, at least add an index to the key field on your model.

Django Loading data independantly in a partial

I have some partial templates which I load in on various pages and sometimes the inclusion of these partials is dynamic, these have their own related models.
At present I am passing models via views to the main page, but is there not a way to load data for these partials independant of the parent page and view?
Just seems like I am duplicating code in the views which cant be right!
Can I not create a custom tag or something which would allow me to load data into the partial irrespective of the data passed in the parent page and its view?
A good example of this is a partial for "latest posts" which exists in a sidebar partial and loads on many different parent templates
Cheers
Kevin
A custom template tag can do this for you. You could write an inclusion tag, which will output the rendered template directly:
# yourapp/templatetags/appname_tags.py
def latest_posts(num_posts):
posts = Post.objects.all()[:num_posts]
return {'posts': posts}
register.inclusion_tag('yourapp/partials/latest_posts.html')(latest_posts)

Django - filtering RelatedManager _set in template?

We have a Django DetailView where we're displaying an an object (Site), along with all of it's related objects (Rooms).
Now, in the template, we could simply iterate over the RelatedManager set:
{% for room in site.room_set.all %}
do stuff
{% endfor %}
However, the problem for this is that this will pick up all related rooms to a site - however, we need to narrow this set down somewhat by another attribute (let's call it year) - and this attribute is stored in a Django session variable.
Currently, we're just using Room.objects.filter(site=some_site, year='2009') in the view code, and that's fine.
My question is more from curiosity - is there any way to use _set in the template, and still filter down or narrow the set?
Could you write a custom Model Manager to do this, so that _set will only ever return objects for the current year? Or is there some other way?
Cheers,
Victor
My question is more from curiosity - is there any way to use _set in
the template, and still filter down or narrow the set?
Not by default, since there's no way to pass arguments to the filter call.
If it's functionality that needs to be repeated variably, often, and related to the template: build a custom template tag or filter (which can accept arguments).
If it's functionality that's specific to a view, code it in the view.
If it's functionality that's the same across multiple views, build the function in a dry place.
For #3, there are so many factors that determine where this code should go that there's no general solution. You could import a function into your views? Use a model manager? A model instance method? context processor? etc.
Could you write a custom Model Manager to do this, so that _set will
only ever return objects for the current year? Or is there some other
way?
It looks like you can actually just by using a model manager for your reverse related model.
class RoomManager(models.Manager):
def current_year(self):
return self.get_queryset().filter(year=datetime.date.today().year)
for room in site.room_set.current_year():
...
Or just on the parent model:
class Site(models.Model):
...
def year_room_set(self):
return self.room_set.filter(year=datetime.date.today().year)