50977

How can I register templatetag in Zinnia for displaying latest entries in specific category?

Question:

I'm building a homepage for a django project with Zinnia installed that will show the latest entries from each category. <a href="https://groups.google.com/forum/#!topic/django-blog-zinnia/aDphpbfmnjE" rel="nofollow">Here</a>, Fantomas42 suggested that registering a new templatetag that took the get_recent_entries tag and added a filter clause would be the best way to achieve this.

I tried to look at the other templatetags to gather how to write this filter clause through context clues but the tags are designed to work dynamically, rather than grab anything specifically named, so I couldn't quite parse how to write a clause that would filter for a specific category.

I'm not sure whether it'd be best to write the clause to filter for a slug (in this case, the slug for the category is political-beat), category name via string ("The Political Beat"), or via the category's position in the category tree (this would be position 1, as it's the only category registered this far -- unless it would be 0... again, I really wish I had time to step back and take a few python tutorials...).

For context, here are some of the other templatetags registered by Zinnia:

@register.inclusion_tag('zinnia/tags/dummy.html', takes_context=True) def get_categories(context, template='zinnia/tags/categories.html'): """ Return the published categories. """ return {'template': template, 'categories': Category.published.all().annotate( count_entries_published=Count('entries')), 'context_category': context.get('category')} @register.inclusion_tag('zinnia/tags/dummy.html', takes_context=True) def get_categories_tree(context, template='zinnia/tags/categories_tree.html'): """ Return the categories as a tree. """ return {'template': template, 'categories': Category.objects.all(), 'context_category': context.get('category')} @register.inclusion_tag('zinnia/tags/dummy.html', takes_context=True) def get_authors(context, template='zinnia/tags/authors.html'): """ Return the published authors. """ return {'template': template, 'authors': Author.published.all().annotate( count_entries_published=Count('entries')), 'context_author': context.get('author')} @register.inclusion_tag('zinnia/tags/dummy.html') def get_recent_entries(number=5, template='zinnia/tags/entries_recent.html'): """ Return the most recent entries. """ return {'template': template, 'entries': Entry.published.all()[:number]} @register.inclusion_tag('zinnia/tags/dummy.html') def get_featured_entries(number=5, template='zinnia/tags/entries_featured.html'): """ Return the featured entries. """ return {'template': template, 'entries': Entry.published.filter(featured=True)[:number]} @register.inclusion_tag('zinnia/tags/dummy.html') def get_draft_entries(number=5, template='zinnia/tags/entries_draft.html'): """ Return the last draft entries. """ return {'template': template, 'entries': Entry.objects.filter(status=DRAFT)[:number]}

I'm kind of experimenting with the solution blindly, but if I happen to stumble upon it, I'll update with an answer!

EDIT: Here's a photo of the home template I'm integrating with Zinnia, in case it's helpful in clarifying the goal of creating the new templatetag(s).<a href="https://i.stack.imgur.com/5zeTo.png" rel="nofollow"><img alt="Photo of homepage." class="b-lazy" data-src="https://i.stack.imgur.com/5zeTo.png" data-original="https://i.stack.imgur.com/5zeTo.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

Answer1:

This isn't the prettiest solution (mainly because you have to select your category in two places, if you want to keep your sitemap in order), but it <em>does</em> allow you to get recent entries based on their respective categories, and I want to share it in case somebody else who lacks thorough expertise in python is looking to implement this functionality.

And, if you are proficient with python, maybe this solution will upset you enough to post a better one :)

From the context clues I could gather from Zinnia's other templatetags, I deduced that if "Featured=True" filtered by "Featured" entries, then maybe I could extend the entry model in the admin to included other types of "Featured" checkboxes, which would correspond with the categories of the entry posted. In this case, I added "Featured_politics."

I then made changes to the following files (essentially, I searched for "featured" throughout the project, and copy/pasted the corresponding code, and changed "featured" to "featured_politics"):

<strong>zinnia/admin/entry.py</strong>

class EntryAdmin(admin.ModelAdmin): """ Admin for Entry model. """ form = EntryAdminForm date_hierarchy = 'publication_date' fieldsets = ( (_('Content'), { 'fields': (('title', 'status'), 'lead', 'content',)}), (_('Illustration'), { 'fields': ('image', 'image_caption'), 'classes': ('collapse', 'collapse-closed')}), (_('Publication'), { 'fields': ('publication_date', 'sites', ('start_publication', 'end_publication')), 'classes': ('collapse', 'collapse-closed')}), (_('Discussions'), { 'fields': ('comment_enabled', 'pingback_enabled', 'trackback_enabled'), 'classes': ('collapse', 'collapse-closed')}), (_('Privacy'), { 'fields': ('login_required', 'password'), 'classes': ('collapse', 'collapse-closed')}), (_('Templates'), { 'fields': ('content_template', 'detail_template'), 'classes': ('collapse', 'collapse-closed')}), (_('Metadatas'), { 'fields': ('featured', 'featured_politics', 'excerpt', 'authors', 'related'), 'classes': ('collapse', 'collapse-closed')}), (None, {'fields': ('categories', 'tags', 'slug')})) list_filter = (CategoryListFilter, AuthorListFilter, 'publication_date', 'sites', 'status') list_display = ('get_title', 'get_authors', 'get_categories', 'get_tags', 'get_sites', 'get_is_visible', 'featured', 'get_short_url', 'publication_date') radio_fields = {'content_template': admin.VERTICAL, 'detail_template': admin.VERTICAL} filter_horizontal = ('categories', 'authors', 'related') prepopulated_fields = {'slug': ('title', )} search_fields = ('title', 'excerpt', 'content', 'tags') actions = ['make_mine', 'make_published', 'make_hidden', 'close_comments', 'close_pingbacks', 'close_trackbacks', 'ping_directories', 'put_on_top', 'mark_featured', 'mark_featured_poltics', 'unmark_featured_poltics', 'unmark_featured'] def mark_featured_politics(self, request, queryset): """ Mark selected as featured post. """ queryset.update(featured_politics=True) self.message_user( request, _('Selected entries are now marked as featured in politics.')) mark_featured_politics.short_description = _('Mark selected entries as featured in politics.') def unmark_featured(self, request, queryset): """ Un-Mark selected featured posts. """ queryset.update(featured=False) self.message_user( request, _('Selected entries are no longer marked as featured.')) unmark_featured.short_description = _( 'Unmark selected entries as featured') def unmark_featured_politics(self, request, queryset): """ Un-Mark selected featured posts. """ queryset.update(featured_politics=False) self.message_user( request, _('Selected entries are no longer marked as featured in politics.')) unmark_featured_politics.short_description = _( 'Unmark selected entries as featured in politics.')

<strong>zinnia/fixtures/helloworld.json</strong> (not sure if this was necessary, but this is one excerpt from several -- that look the exact same -- changes that I made in here).

"featured": false, "featured_politics": false, "start_publication": null, "pingback_enabled": true, "trackback_enabled": true, "authors": [

<strong>zinnia/models_bases/entry.py</strong>

class FeaturedEntry(models.Model): """ Abstract model class to mark entries as featured. """ featured = models.BooleanField( _('featured'), default=False) class Meta: abstract = True class FeaturedEntryPolitics(models.Model): """ Abstract model class to mark entries as featured. """ featured_politics = models.BooleanField( _('featured_politics'), default=False) class Meta: abstract = True

And, at the bottom of models_bases/entry.py, update this list to include your new class(es):

class AbstractEntry( CoreEntry, ContentEntry, DiscussionsEntry, RelatedEntry, LeadEntry, ExcerptEntry, ImageEntry, FeaturedEntry, FeaturedEntryPolitics, AuthorsEntry, CategoriesEntry, TagsEntry, LoginRequiredEntry, PasswordRequiredEntry, ContentTemplateEntry, DetailTemplateEntry):

<strong>zinnia/templatetags/zinnia.py</strong>

@register.inclusion_tag('zinnia/tags/dummy.html') def get_featured_entries(number=5, template='zinnia/tags/entries_featured.html'): """ Return the featured entries. """ return {'template': template, 'entries': Entry.published.filter(featured=True)[:number]} @register.inclusion_tag('zinnia/tags/dummy.html') def get_politics_entries(number=5, template='recent_politics.html'): """ Return the featured entries. """ return {'template': template, 'entries': Entry.published.filter(featured_politics=True)[:number]}

<strong>zinnia/xmlrpc/metaweblog.py</strong>

# Useful Wordpress Extensions 'wp_author': author.get_username(), 'wp_author_id': author.pk, 'wp_author_display_name': author.__str__(), 'wp_password': entry.password, 'wp_slug': entry.slug, 'sticky': entry.featured, 'sticky': entry.featured_politics} @xmlrpc_func(returns='struct[]', args=['string', 'string', 'string']) def get_users_blogs(apikey, username, password): """ blogger.getUsersBlogs(api_key, username, password) => blog structure[] """ authenticate(username, password) site = Site.objects.get_current() return [blog_structure(site)] @xmlrpc_func(returns='struct', args=['string', 'string', 'string']) def get_user_info(apikey, username, password): """ blogger.getUserInfo(api_key, username, password) => user structure """ user = authenticate(username, password) site = Site.objects.get_current() return user_structure(user, site) @xmlrpc_func(returns='struct[]', args=['string', 'string', 'string']) def get_authors(apikey, username, password): """ wp.getAuthors(api_key, username, password) => author structure[] """ authenticate(username, password) return [author_structure(author) for author in Author.objects.filter(is_staff=True)] @xmlrpc_func(returns='boolean', args=['string', 'string', 'string', 'string', 'string']) def delete_post(apikey, post_id, username, password, publish): """ blogger.deletePost(api_key, post_id, username, password, 'publish') => boolean """ user = authenticate(username, password, 'zinnia.delete_entry') entry = Entry.objects.get(id=post_id, authors=user) entry.delete() return True @xmlrpc_func(returns='struct', args=['string', 'string', 'string']) def get_post(post_id, username, password): """ metaWeblog.getPost(post_id, username, password) => post structure """ user = authenticate(username, password) site = Site.objects.get_current() return post_structure(Entry.objects.get(id=post_id, authors=user), site) @xmlrpc_func(returns='struct[]', args=['string', 'string', 'string', 'integer']) def get_recent_posts(blog_id, username, password, number): """ metaWeblog.getRecentPosts(blog_id, username, password, number) => post structure[] """ user = authenticate(username, password) site = Site.objects.get_current() return [post_structure(entry, site) for entry in Entry.objects.filter(authors=user)[:number]] @xmlrpc_func(returns='struct[]', args=['string', 'string', 'string']) def get_tags(blog_id, username, password): """ wp.getTags(blog_id, username, password) => tag structure[] """ authenticate(username, password) site = Site.objects.get_current() return [tag_structure(tag, site) for tag in Tag.objects.usage_for_queryset( Entry.published.all(), counts=True)] @xmlrpc_func(returns='struct[]', args=['string', 'string', 'string']) def get_categories(blog_id, username, password): """ metaWeblog.getCategories(blog_id, username, password) => category structure[] """ authenticate(username, password) site = Site.objects.get_current() return [category_structure(category, site) for category in Category.objects.all()] @xmlrpc_func(returns='string', args=['string', 'string', 'string', 'struct']) def new_category(blog_id, username, password, category_struct): """ wp.newCategory(blog_id, username, password, category) => category_id """ authenticate(username, password, 'zinnia.add_category') category_dict = {'title': category_struct['name'], 'description': category_struct['description'], 'slug': category_struct['slug']} if int(category_struct['parent_id']): category_dict['parent'] = Category.objects.get( pk=category_struct['parent_id']) category = Category.objects.create(**category_dict) return category.pk @xmlrpc_func(returns='string', args=['string', 'string', 'string', 'struct', 'boolean']) def new_post(blog_id, username, password, post, publish): """ metaWeblog.newPost(blog_id, username, password, post, publish) => post_id """ user = authenticate(username, password, 'zinnia.add_entry') if post.get('dateCreated'): creation_date = datetime.strptime( post['dateCreated'].value[:18], '%Y-%m-%dT%H:%M:%S') if settings.USE_TZ: creation_date = timezone.make_aware( creation_date, timezone.utc) else: creation_date = timezone.now() entry_dict = {'title': post['title'], 'content': post['description'], 'excerpt': post.get('mt_excerpt', ''), 'publication_date': creation_date, 'creation_date': creation_date, 'last_update': creation_date, 'comment_enabled': post.get('mt_allow_comments', 1) == 1, 'pingback_enabled': post.get('mt_allow_pings', 1) == 1, 'trackback_enabled': post.get('mt_allow_pings', 1) == 1, 'featured': post.get('sticky', 0) == 1, 'featured_politics': post.get('sticky', 0) == 1, 'tags': 'mt_keywords' in post and post['mt_keywords'] or '', 'slug': 'wp_slug' in post and post['wp_slug'] or slugify( post['title']), 'password': post.get('wp_password', '')} if user.has_perm('zinnia.can_change_status'): entry_dict['status'] = publish and PUBLISHED or DRAFT entry = Entry.objects.create(**entry_dict) author = user if 'wp_author_id' in post and user.has_perm('zinnia.can_change_author'): if int(post['wp_author_id']) != user.pk: author = Author.objects.get(pk=post['wp_author_id']) entry.authors.add(author) entry.sites.add(Site.objects.get_current()) if 'categories' in post: entry.categories.add(*[ Category.objects.get_or_create( title=cat, slug=slugify(cat))[0] for cat in post['categories']]) return entry.pk @xmlrpc_func(returns='boolean', args=['string', 'string', 'string', 'struct', 'boolean']) def edit_post(post_id, username, password, post, publish): """ metaWeblog.editPost(post_id, username, password, post, publish) => boolean """ user = authenticate(username, password, 'zinnia.change_entry') entry = Entry.objects.get(id=post_id, authors=user) if post.get('dateCreated'): creation_date = datetime.strptime( post['dateCreated'].value[:18], '%Y-%m-%dT%H:%M:%S') if settings.USE_TZ: creation_date = timezone.make_aware( creation_date, timezone.utc) else: creation_date = entry.creation_date entry.title = post['title'] entry.content = post['description'] entry.excerpt = post.get('mt_excerpt', '') entry.publication_date = creation_date entry.creation_date = creation_date entry.last_update = timezone.now() entry.comment_enabled = post.get('mt_allow_comments', 1) == 1 entry.pingback_enabled = post.get('mt_allow_pings', 1) == 1 entry.trackback_enabled = post.get('mt_allow_pings', 1) == 1 entry.featured = post.get('sticky', 0) == 1 entry.featured_politics = post.get('sticky', 0) == 1

Don't forget that you've made changes to your models, so you need to run migrations!

And now, I can call the latest entries that I want to appear under a (in this example) "Politics" header on the homepage by using {% get_politics_entries 3 template="homepage_latest_politics.html" %}.

Not entirely relevant, but just in case it's helpful to anybody, my template for homepage_latest_politics.html is:

{% load i18n %} <div class="row {% if not entries %}no-{% endif %}entries-featured_poltics"> {% for entry in entries %} <div class="col s4"> <div class="card large"> <div class="card-image"> <img src="{% if entry.image %}{{ entry.image.url }}{% endif %}" alt="{{ entry.title }}"> <span class="card-title">{{ entry.title }}</span> </div> <div class="card-content">

{{ entry.excerpt|safe|linebreaks|truncatewords_html:30 }}

</div> <div class="card-action"> <a href="#">Click to read more.</a> </div> </div> </div> {% endfor %} </div>

This is an integration of zinnia's tags with the "Card" component of Materialize, and produces this -- with the entries being the latest to have the "Featured_politics" box checked:

<a href="https://i.stack.imgur.com/wfAmG.jpg" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/wfAmG.jpg" data-original="https://i.stack.imgur.com/wfAmG.jpg" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

<a href="https://i.stack.imgur.com/GljZ4.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/GljZ4.png" data-original="https://i.stack.imgur.com/GljZ4.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

Hey -- I <em>did</em> say that it wasn't the prettiest solution, but... it works!

Recommend

  • How can I force an AngluarJS form to realize required fields have been filled through DOM manipulati
  • calculate daily averages for 3d array
  • Finding references in Visual Studio
  • Integrate two requests into one Javascript
  • How to display the hotel names that have the maximum count from data base by calculating sum in PHP?
  • Read stdin in chunks in Bash pipe
  • How to share a database between ASP.NET MVC 5 application and .NET console application?
  • UI-GRID column summation on checkbox change
  • What is “PHP-standardized” version number strings?
  • Generating random numbers directly inside a .htaccess file
  • Certain Arabic text gets incorrectly shown while other Arabic text gets showed normally?
  • How to create virtual printer with iOS Simulator?
  • Quick Question About Get and Set
  • Find VMID for running instance
  • Geom_jitter colour based on values
  • event.getSource() returns null Accessibility in android
  • Most efficient way to move table rows from one table to another
  • Wrong labels when plotting a time series pandas dataframe with matplotlib
  • Primefaces ManyCheckbox inside ui:repeat calls setter method only for last loop
  • Cast between interfaces whose interface signatures are same
  • Clarification on min distance on LocationManager.requestLocationUpdates method, min Distance paramet
  • Do I need to seed any random number generator before using EVP_PKEY_keygen of OpenSSL?
  • std::remove_copy_if_ valgrind bytes in block are possibly lost in loss record
  • Functions in global context
  • C++ Partial template specialization - design simplification
  • Spring Data JPA custom method causing PropertyReferenceException
  • Javascript simulate pressing enter in input box
  • Why ng-show works with ng-repeat but ng-if doesn't? [duplicate]
  • Excel - Autoshape get it's name from cell (value)
  • How to apply VCL Styles to DLL-based forms in Inno Setup?
  • XCode can't find symbols for a specific iOS library/framework project
  • jqPlot EnhancedLegendRenderer plugin does not toggle series for Pie charts
  • How do I rollback to a specific git commit
  • Is there a mandatory requirement to switch app.yaml?
  • How can I get HTML syntax highlighting in my editor for CakePHP?
  • Acquiring multiple attributes from .xml file in c#
  • How to CLICK on IE download dialog box i.e.(Open, Save, Save As…)
  • How can I remove ASP.NET Designer.cs files?
  • Busy indicator not showing up in wpf window [duplicate]
  • java string with new operator and a literal