ListView on slug request - python

My URL
/tags/{slug}
points to this view:
class TagsDetailList(request, ListView):
queryset = Link.objects.filter(tags__name__in=['request.slug'])
template_name = "links/tags_detail_list.html"
So i have the request.slug object in the url.
Now i want to make a ListView that filters Link.objects by request.slug and respond to given template with the queried result.
All works but no queries are given on my template.
response template is:
{% extends "base.html" %}
{% block content %}
<h2>Tags Detail List</h2>
<ul>
{% if link in object_list %}
{% for link in object_list %}
<li>
{{ link.title }}
</li>
{% endfor %}
{% else %}
<p> Error: No Links in queryset! </p>
{% endif %}
<ul>
{% endblock %}
i dont get some elements, only the error message. its something bad on the request on my view.
who can help me and give me a hint how i can retrieve the request slug on my view?
EDIT:
nice solutions. i learned the way kwargs work(a small part).
But on the template i get still the error for no queryset. Tried both answers and also changed a bit but never really worked. Any hint what cause this?

If your urls pattern something like this:
r'^list/(?P<slug>[\w,\*]+)?$'
So in the views should be:
class TagsDetailList(ListView):
model = Link
template_name = "links/tags_detail_list.html"
def get_queryset(self):
qs = self.model.objects.all()
if self.kwargs.get('slug'):
qs = qs.filter(tags__name=self.kwargs['slug'])
return qs

What you've done here doesn't make sense: you're just asking for tags whose names are in the list consisting of the literal text " request.slug".
You need to override get_queryset so that it queries on the actual value of the slug, which is in self.kwargs.
def get_queryset(self, *args, **kwargs):
return Link.objects.filter(tags__name=self.kwargs ['slug'])
Also, I don't know what that if statement is doing in your template, but you haven't defined "link" do it will never evaluate to true, so no links will show.

Maybe it will help, but it works for me in Django 2+:
Your urls.py:
...
path('tags/<slug>/', TagsDetailList.as_view(), name='tags_detail'),
...
Your views.py:
...
class TagsDetailList(ListView):
model = Link
template_name = 'links/tags_detail_list.html'
def get_queryset(self, *args, **kwargs):
return Link.objects.filter(tags__slug=self.kwargs['slug'])
...

Related

How to display the children of a wagtail page

My requirement sounds simple, but I am struggling to make it work
In my wagtail project I have a BlogListingPage which has many child pages. I am trying to create a list of the children in a web page. Here is my code:
models.py
class BlogListingPage(Page):
...
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
context['blog_pages'] = BlogDetailPage.objects.live().child_of(self)
return context
class BlogDetailPage(Page):
...
blog_listing = models.ForeignKey(BlogListingPage,
on_delete=models.PROTECT,
blank=True,
null=True,)
views.py
def blog(request):
url = 'blog/blog_listing_page.html'
context = {'data': BlogListingPage.objects.all()}
return render(request, url, context)
blog_listing_page.html
{% block content %}
{% for blog_listing in data %}
{% for post in blog_listing.blogdetailpage %}
{{ post.blog_title }}
{% endfor %}
{% endfor %}
{% endblock content %}
I am lost in the fog of the django/wagtail pages/objects/querysets/all()/specific
Can someone please lead me to clear blue water?
A couple of suggestions regarding your approach:
Having a foreign key from BlogDetailPage to BlogListingPage is not the Wagtail way to organise parent-child relationships. All Page objects already have a tree hierarchy and you should rely on that, otherwise you lose all the built-in tree handling provided by Wagtail.
You should use the parent/subpage rules to specify how page types can be related to each other. In you case it would look something like:
class BlogListingPage(Page):
subpage_types = ['myapp.BlogDetailPage']
class BlogDetailPage(Page):
parent_page_types = ['myapp.BlogListingPage']
The get_context() method on BlogListingPage only runs when Wagtail is serving that specific page in a view. It doesn't run when you're iterating over these pages in your blog view. In order to access the children there, you need to do something like this:
{% for blog_listing in data %}
{% for post in blog_listing.get_children.live %}
{{ post.blog_title }}
{% endfor %}
{% endfor %}
i.e., you need to use the get_children() method on the page to obtain it's children. live() further filters this to exclude unpublished pages. Note that this will only work if the blog posts are children (in the Wagtail page tree sense) of the listing page. specific() isn't important here if you're only using the post URL and title - it is relevant if you want to display fields that are part of your model rather than the base Page model.

Django template url not correctly getting parameter

I am able to print the correct object pk in the template using a template tag, but when I use the same code in a url parameter it does not show up.
I am trying to using the first result pk from a many to many relationship to create a url parameter to link to that page. It works when I manually input the pk, but when I use category.quote_set.first.pk it does not work.
"category" is is queryset of all categories, which have a many to many relationship to quotes.
<p>{{ category.quote_set.first.pk }}</p>
<p></p>
The url file has path('motto/<int:pk>/', views.QuoteView.as_view(), name='quote'),
Going to the page shows an error Reverse for 'quote' with arguments '('',)' not found. 1 pattern(s) tried: ['motto\\/(?P<pk>[0-9]+)\\/$']
I believe the reason for this is that the url is created first, and the category.quote_set.first.pk is created after the page, but that is just my theory.
View for the page:
class CategoryView(generic.ListView,ContextMixin):
template_name = 'mottos/category.html'
context_object_name = 'motto_list'
def get_queryset(self):
return Quote.objects.all().annotate(score=Avg('vote__score')).filter(categories__slug=self.kwargs['cat
egory']).order_by('-score')
Try something like this
{% for quote in quote_list %}
<p>
</p>
{% endfor %}
Another solution:
in you views fie add this:
def get_context_data(self, **kwargs):
context['quote_list'] = Quote.objects.all().annotate(score=Avg('vote__score')).filter(categories__slug=self.kwargs['category']).order_by('-score')
return context
Then in your template add this
{% for quote in quote_list %}
<p>
</p>
{% endfor %}
I was able to get the quote pk by using
{% for quote in category.quote_set.all|slice:"0:1" %}
<p></p>
% endfor %}
Since I only wanted the first quote, I used slice:"0:1" to only get the first quote, and then used the pk from that result.

How can I create a sidebar in all my Django templates using CBV?

My problem is that I need dynamic tags list on all my pages and all posts (one post) content on that page too. How can I include tag sidebar on all the pages using Class Based Views? Thanks.
EDIT:
the tags list must be sorted by the frequency of using.
My code:
class AllTagCloudView(ListView):
model = Tag
template_name = 'tag_cloud.html'
context_object_name = 'tag_cloud'
def get_queryset(self):
qs = Tag.objects.values("name", "slug").annotate(Count("post")).order_by('-post__count')
return qs
I've tried to use
#register.inclusion_tag('tag_cloud.html', takes_context=True)
def sidebar_sorted_tags(context):
but I don't understand how to do it to work.
Also I have tried to use {% include 'tag_cloud.html' %}:
<div>
<p>Tags</p>
{% for tag in tag_cloud %}
<ul>
<li>{{ tag.name }}</li>
</ul>
{% empty %}
There is no tags yet
{% endfor %}
</div>
I think this is something stupid or I do something wrong.
This task is not related to class based views. You need to use custom inclusion template tag.
#register.inclusion_tag('tag_cloud.html')
def sidebar_sorted_tags():
return {'tag_cloud': Tag.objects.values("name", "slug")
.annotate(Count("post")).order_by('-post__count')}
And now in your base.html template write:
{% load my_tags %}
{% sidebar_sorted_tags %}
You can use context processors and global base template that all of your other templates will extend. Also you can use simple include in your template tag instead of global base template.

multiple pagination in django class based view - listview

The code below paginates the queryset but how can I paginate context['guser1']
class AuthorList(ListView):
template_name = 'authorList.html'
paginate_by = 10
queryset = Author.objects.order_by('date')
def get_context_data(self, **kwargs):
context = super(AuthorList, self).get_context_data(**kwargs)
context['guser1'] = Author.objects.order_by('date')
return context
You'll have to use a Paginator object like you would in a view. Here are the docs.
The paginator for the view can be accessed under the context variable page_obj in your template. The page number is passed as a GET parameter in the url, page. Here's a simple example:
{% if page_obj.has_previous %}
Previous page
{% endif %}
{% if page_obj.has_next %}
Next page
{% endif %}

Separate admin page for one-to-many objects

I have the following model:
class Campaign:
...
class Location:
...
campaign = models.ForeignKey(Campaign)
I normally would edit the locations using a tabular inline, but because the number of Locations is very high I would like to move the management of the locations to another page. It should pretty much do the same as normal Admin page, except it should only display Locations for the selected campaign and when adding a new Location, it should be automatically attached to the right Campaign. To access the Location Admin page a link in the Campaigns list should be clicked.
What would be the right way to do this? Are there other options?
There is no out of the box solution for your problem, but it is pretty easy to do.
I would register Locations as a separate model admin object like this:
from django.contrib import admin
from models import Location
class LocationAdmin(EnhancedModelAdminMixin, admin.ModelAdmin):
list_filter = 'campaign',
admin.site.register(Location, LocationAdmin)
This way you will have admin interface where you can filter your locations by campaigns.
To have a direct access to Location on Campaign model admin I would use https://github.com/charettes/django-admin-enhancer
Auto selecting a filtered campaign on new Location create form it is a bit more trickier... But my approach would be to add a query parameter to Add new Location button and then catch it when initializing create form, to define initial value of field campaign.
First of all create a template on let's say your_app/admin/change_list.html, which looks like this:
{% extends "admin/change_list.html" %}
{% load i18n admin_static admin_list %}
{% load admin_urls %}
{% block object-tools %}
{% if has_add_permission %}
<ul class="object-tools">
{% block object-tools-items %}
<li>
<a href="{% url cl.opts|admin_urlname:'add' %}{% if is_popup %}?_popup=1{% if selected_campaign %}&selected_campaign={{ selected_campaign }}{% endif %}
{% elif selected_campaign %}?selected_campaign={{ selected_campaign }}{% endif %}"
class="addlink">
{% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}
</a>
</li>
{% endblock %}
</ul>
{% endif %}
{% endblock %}
Then append your LocationAdmin class to look like this:
class LocationAdmin(EnhancedModelAdminMixin, admin.ModelAdmin):
list_filter = 'campaign',
list_display = 'location_name', 'campaign'
change_list_template = 'your_app/admin/change_list.html'
def changelist_view(self, request, extra_context=None):
if 'campaign__id__exact' in getattr(request, request.method):
selected_campaign = getattr(request, request.method)['campaign__id__exact']
if not extra_context:
extra_context = {'selected_campaign': selected_campaign}
else:
extra_context['selected_campaign'] = selected_campaign
return super(LocationAdmin, self).changelist_view(request, extra_context)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'campaign':
if 'selected_campaign' in getattr(request, request.method):
kwargs['initial'] = getattr(request, request.method)['selected_campaign']
return db_field.formfield(**kwargs)
return super(LocationAdmin, self).formfield_for_foreignkey(
db_field, request, **kwargs
)
So what we are doing here is we are overriding modeladmins changelist_view function just to add additional context variable of current 'selected_campaign'.
Then we adding it as a query parameter on Add new Location button.
Finaly we are using model admins formfield_for_foreignkey function to dynamically define initial value of campaign field based on parameter received with request when user clicks on Add new location.
Cheers! Do not forget to up-vote this answer if you feel it was helpful ;)

Categories

Resources