Displaying page content using django-page-cms - django

I would like to display some content located in my models in some of my template pages.
I am using django-page cms
In the documentation views are not used to display content. Instead ready made template tags are used.
http://packages.python.org/django-page-cms/display-content.html
I do not understand a word of this. Please Bear with me I am new.
All I want to do is display some info located in my models inside a template this manner..
{% if latest_news_list %}
{% for news in latest_news_list %}
<li><h3>{{ news.title }}</h3></li>
<li><p>{{ news.body }}</p></li>
{% endfor %}
Since views are not used I cannot use if latest_news_list.
I need to somehow get my models to display in the templates using django-page cms and NOT regular views. The documentation states to use some kind of template tag for this.
Could somebody please explain to me how to do this.
And a clear concise explanation of the following ready-made template tags would also be appreciated...
* get_content
* show_content
* get_page
* show_absolute_url
taken from.http://packages.python.org/django-page-cms/display-content.html
I need to display info contained using the following models in the manner I have highlighted above. Thank you very much for your help. my models are as follows.
class Body(models.Model):
type = models.ForeignKey(Content)
title = models.CharField(max_length=100)
published = models.DateTimeField(default=datetime.now)
body = tinymce_models.HTMLField("Main content")
As I have stated I am very new to this please make explanations as simple as possible.

The template tags you mentioned are supposed to display content coming from the cms. If you want to include data coming from your app, you should see this sectionlink text.
def extra_context():
from myapp.models import Body
items = Body.object.all()
return {'items': items}
PAGE_EXTRA_CONTEXT = extra_context
{% if items %}
<ul>
{% for item in items %}
<li>{{ item.title }} </li>
{% endfor %}
<ul>
{% endif %}
Or, if you want to use your app's view, see this.

Related

django - Count objects and sum values in a field

I want to include some basic statistics about a model in a stats.html file. The variables don't show in the html. What am I doing wrong?
from django.shortcuts import render, get_object_or_404, redirect
from django.db.models import Avg, Sum, Count
from .models import Production
def statistics(request):
nr_of_plays = Production.objects.count()
nr_of_actors = Production.objects.aggregate(num_actors=Sum('nr_actors'))
nr_of_audience = Production.objects.aggregate(num_audience=Sum('est_audience'))
context = {
'nr_of_plays': nr_of_plays,
'nr_of_actors': nr_of_actors['num_actors'],
'nr_of_audience': nr_of_audience['num_audience'],
'test':'abc'
}
return render(request, 'stats.html', context)
The model:
class Production(models.Model):
title = models.CharField(max_length=200)
nr_actors = models.IntegerField(default=0)
est_audience = models.IntegerField(default=0)
...
urls.py:
path('stats/', views.statistics, name='stats'),
the relevant section of base.html:
<copyright class="text-muted">
<div class="container text-center">
<p>© One World Theatre - {% now "Y" %} {% include 'stats.html' with test=test %} </p>
</div>
</copyright>
And the stats.html template:
{% load static %}
{{ test }} - Stats: {{ nr_of_plays }} plays produced, involving {{ nr_of_actors }} actors, seen by {{ nr_of_audience }} people.
the output:
© One World Theatre - 2020 - Stats: plays produced, involving actors, seen by people.
EDIT:
I didn't mention that I'm using my template stats.html in my base.html template like this {% include 'stats.html' %}. When I add with test=test to the include tag, the test text shows. But when adding with nr_of_plays=nr_of_plays nothing happens :-/.
I ended up forgetting about trying to {% include 'stats.html' %} in my base template and just added those variables where I need them, works great. Not DRY, but what to do... .
EDIT 2:
I was too quick to cry victory. Edited the question with the latest code. Passing the variables in the view that handles the main content block works, but that means I would have to add them in every single view (not DRY). Still not getting what doesn't work with my setup. example.com/stats.html renders exactly what I want, but doesn't show the variables when I include it in my base.html. with test=test doesn't do anything. Clueless (and thankful for the help sofar).
Aggregate returns a dictionary.
You need to access its value via the key
context = {
'nr_of_plays': nr_of_plays,
'nr_of_actors': nr_of_actors['nr_actors_sum'],
'nr_of_audience': nr_of_audience['est_audience_sum']
}
Alternatively you can specify a custom key name instead of the default composite one:
nr_of_actors = Production.objects.aggregate(num_actors=Sum('nr_actors'))
nr_of_audience = Production.objects.aggregate(num_audience=Sum('est_audience'))
Note: .all() is redundant and can be removed
Base on your latest confession and symptoms, you don't seem to be going to your statistics view.
Looks like the url is rendering another view, which also extends base.html confuses you that you are in the right view.
One way to test it is to put a print statement in your statistics view and see if it prints anything in the console:
def statistics(request):
print(111111111111111111111111111111)
...
return render(request, 'stats.html', context)
Second thing is, if your base.html includes stats.html, you shouldn't be rendering the stats.html directly, you should pass the context to a template that extends base.html.
Third thing is, refer to Pynchia's answer to properly get the count of aggregated queryset.

Additional field shows up outside table in admin change_list view

I have a model called Project in an app called projects that I registered with the admin site so the instances can be added/edited/etc. This works as expected. Now I want to add a button for each project in the change list view on the admin site, that links to a custom form that requires a Project instance to do things. I followed a bunch of different tutorials to customize the admin site and managed to add another field to the table of the change list view. However the entries show up outside the table (see image).
I added the custom field by overwriting the admin/change_list.html template and calling a custom template tag custom_result_list within it. This tag adds a table field to the change list and then calls the admin/change_list_results.html template to render it. I have confirmed with a debugger that the item is added to the entries of the change list before the template is rendered (see image).
I cannot explain why the table is not rendered correctly even though the additional field has the same structure as the auto-generated ones. I have to admit I have resorted to Cargo Cult Programming, because I do not understand how this is supposed to work, despite spending too many hours trying to solve this simple problem.
Here's the relevant code.
In file /projects/templatetags/custom_admin_tags.py:
from django import template
from django.contrib.admin.templatetags.admin_list import result_list as admin_result_list
def custom_result_list(chl):
extended_cl = {}
extended_cl.update(admin_result_list(chl))
extended_cl["result_headers"].append({
'class_attrib': r' class="column-__str__"',
'sortable': False,
'text': 'Configure Project'
})
idx = 0
snippet = '<td class="action-button">{}</td>'
for project in chl.result_list:
extended_cl["results"][idx].append(snippet.format(project.id, project.unmod_name))
idx += 1
return extended_cl
register = template.Library()
register.inclusion_tag('admin/change_list_results.html')(custom_result_list)
In file templates/admin/projects/project/change_list.html:
{% extends "admin/change_list.html" %}
{% load i18n admin_urls static admin_list %}
{% load custom_admin_tags %}
{% block result_list %}
{% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% custom_result_list cl %}
{% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% endblock %}
To fix your issue:
from django.utils.html import format_html
replace your snippet.format(...) with format_html(snippet,...)
Explanation:
in django, all strings you pass from python are automatically HTML escaped. which here means, all your tags will not be considered as HTML. Such limitation is added to avoid any potential exploits by hackers. In your case, use of a template to render html is highly recommended. However, you can also send raw html from python using format_html helper function.

How to supply a QuerySet to an included template?

I have a simple Django application for a magazine website, with a sidebar that shows links for recent issues. The sidebar is its own template, which I then include into other pages. I want the Queryset for recent issues to be part of the sidebar inclusion, rather the in the view of the page that the sidebar appears on. (This is both to avoid repetition and, more importantly, to make the sidebar functional on the flatpages, which don't have views.)
This seems like iti should be a simple/common thing to do, but I can't find a good way to do it. I looked into custom template tags, context managers, etc., but all the options I saw want to return a string or a dictionary, rather than a queryset. So I ended up doing an inclusion template tag (code below), and basically building a dictionary for the issues I want displayed. It works fine, but it seems really clunky and brittle to me, and it mixes logic and display pretty badly.
Is there a better way to do this?
<!-- app_name/templates/sidebar.html -->
...
{% load pr_extras %}
...
<ul class="nav nav-sidebar">
<li>Home</li>
<li>Previous Issues</li>
<li>{% show_recent %}</li>
</ul>
...
# app_name/templatetags/pr_extras.py
from django import template
from mag.models import Issue
register = template.Library()
#register.inclusion_tag('recent.html')
def show_recent():
issues = Issue.objects.order_by('-issue_num')[1:6]
iss_dict = {}
for i, iss in enumerate(issues):
k = "recent_%d" % i
iss_dict[k] = "%s %s %d" % (iss.issue_num, iss.get_season_display(), iss.year)
u = "url_%d" % i
iss_dict[u] = iss.issue_pdf
return iss_dict
<!-- app_name/templates/recent.html -->
<ul class="nav nav-sidebar">
<li>Issue {{ recent_0 }}</li>
<li>Issue {{ recent_1 }}</li>
<li>Issue {{ recent_2 }}</li>
<li>Issue {{ recent_3 }}</li>
<li>Issue {{ recent_4 }}</li>
</ul>
A context processor would be the typical way to do this.
def recent_issues(request):
{'recent_issues': return Issue.objects.order_by('-issue_num')[1:6]}
Once you add the context processor to your TEMPLATES settings, you can access recent_issues in the template.
Alternatively, if you don't want a context processor to run for every view, you can create a template tag using the simple_tag decorator (in Django < 1.9 use an assignment tag).
#register.simple_tag
def recent_issues():
return Issue.objects.order_by('-issue_num')[1:6]
In your template, use the tag to assign the queryset to a variable
{% recent_issues as recent_issues %}
You can now loop through recent_issues in the template.
Maybe I don't understand your question very well, but are you looking for something like this:
{% include "some_include_template.html" with some_list=some_list some_var=some_var %}
?

Django functions issue and urlconf confusion

I couldn't place my question into one sentence.
I was following a tutorial to make a blog from scratch. But the tutorial predicted having all the categories, tags, months and years listings in separate templates.
I want to add a list of categories, a list of months and years on the main blog page.
So here is what I got. With this code, the list of categories is shown in the main page but only if you go to the blog/category url, and not in just blog/ where I want it to be.
**(r'^$', list),**
(r'^archive/(\d{1,2})/$', list),
(r'^\d{2}/d{4}/$', month),
(r'^([0-9]{4}/\d{1,2})/(?P<slug>.*)/$', detail),
(r'^(?P<year>\d{4})/$', year),
**(r'^category/$', category),**
I also tried:
(r'^$', category),
but no luck.
This is the same code from the template in category.html and in list.html:
{% if categories %}
{% for category in categories %}
<li class="cat-item"><a href="category/{{ category.name.lower }}/"
title="{{ category.name.capitalize }}">
{{ category.name.capitalize }}</a>
</li>
{% endfor %}
{% endif %}
Views.py:
def category(request):
return render_to_response('blog/list.html', {'categories':Category.objects.all(),},)
It was like this. I tried this, but no luck in def list:
return render_to_response('blog/list.html',{'posts':posts,
'next':next,
'previous':previous,
'categories':Category.objects.all(),
},)
How can I get what shows on blog/category to show on blog/ also?
Thanks.
When you type an url in your browser a request is sent to your server. Django then takes the url and matches it against it url patterns to determine the proper view. As soon as a view is found django stops matching and executes this view which in turn returns a response.
If you want to use your categories in different views you either have to to make sure that in every view the same categories context variable is provided to the template or, which is usually much better, write a simple custom template tag. For your categories this could look like this:
#register.inclusion_tag('blog/category_list.html')
def categories_list():
return { 'categories': Category.objects.all() }
In the file 'blog/categoy_list.html' would then be the code that is currently both in 'categories.html' and 'list.html'. In these files replace it with.
{% load your_blog_tags %}
{% categories_list %}
You can use that then wherever you need a category list. Of course the same applies for years and month lists.

loop in Django template: how to control the loop iterator?

I'm using Django to show a list of posts. Each post has a 'is_public' field, so if one post's 'is_public' equals to False, it should not be shown to the user. Also, I want to show a fixed number of posts in one page, but this number can be changing depending on views.
I decided to crop the queryset in template as a few views are using the same template, generating it in the view means a lot of repeated codes.
If written in python, it should look like this:
i=number_of_posts_to_show_in_one_page
while i:
if qs[i].is_public == True:
#show qs[i] to the page
i--
As the django template does not support while loop and for loop seems hard to control, is there a way of achieving this? Or should I do it in another way?(One idea is to crop the qs before looping)Thanks!
Update:
I've written this template tag to pre-process the queryset:
#register.simple_tag(takes_context=True)
def pre_process_list(context,list,numbers):
#if not user.has_perm('admin'):
context['result_list']=list.filter(is_public=True, is_removed=False)[0:numbers]
#else:
#context['result_list']=list[0:numbers]
return ''
Before using for loop in the template, I'll pass the queryset to this templage tag, and use a simple for loop to show its result.
If in the future I want to show non-public posts to admins(which is not decided yet), I can write in some logic like the commented ones, and have them styled differently in the template.
{% for post in posts %}
{% if post.is_public %}
{{ post }}
{% endif %}
{% endfor %}
Though this would be a perfect use case for a manager.
You could write a simple manager that filters public posts.
class PublicPostManager(models.Manager):
def get_query_set(self):
return super(PublicPostManager, self).get_query_set().filter(is_public=True)
Then you would add it to your Post Class:
class Post(models.Model):
...
public = PublicPostManager()
Then you could pass post.public.all() as public_posts to your template and simplify your loop:
{% for post in public_posts %}
{{ post }}
{% endfor %}
#arie has a good approach with the manager, but you can easily do the same without writing a manager:
# View
posts = Post.objects.filter(is_public=True) # or use the manager
# Now, you can either limit the number of posts you send
# posts = posts[:5] (only show five in the view)
return render_to_response('foo.html',{'posts':posts})
# Template
# Or you can do the limits in your template itself:
{% for post in posts|slice:":5" %}
{{ post }}
{% endfor %}
See the slice filter on more information.
However, since this is a common operation, with django 1.3 you can use class based views to automate most of this.