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.
Related
I have a view called 'Teams' that loops through different NBA teams in a dictionary and shows their name and logo. When the user clicks on one of these logos, I want them to be taken to the 'TeamDetailView'. This should carry over the chosen team's city/name/logo, and I can see this information being passed in the URL. When I attempt to load the team's individual page, though, it gives me a type error and says that
TeamDetailView() got an unexpected keyword argument 'city'
In the local vars section, it shows my key/value pairs being passed correctly. How can I access these parameters on the team page and correct this error?
callback_kwargs {'city': 'Atlanta', 'logo': 'atlanta-logo.png', 'name': 'Hawks'}
Here is my view:
def TeamDetailView(request):
return render(request, 'bandwagon/team.html/')
Here is my URL:
path('team/<str:city>/<str:name>/<str:logo>/', views.TeamDetailView, name='bandwagon-team'),
Here is my Template for the Teams List:
{% for key, value in teams.items %}
<a class="stream-list" href="{% url 'bandwagon-team' value.city value.name value.logo %}">
<img class="stream-img" alt="The Logo for the {{ value.city }} {{ value.name }}" src="../../../media/logos/{{ value.logo }}">
<p class="name">{{value.city }} {{value.name}}</p>
</a>
{% endfor %}
Here is my Template for the Individual Team Page, which is quite basic for now until I get these parameters passed correctly:
{% extends 'bandwagon/base.html' %}
{% block content %}
<h1 class="article-title">Team</h1>
{% endblock content %}
Have you tried updating your TeamDetailView function to accept the url parameters? Something like -
def TeamDetailView(request, city, name, logo):
return render(request, 'bandwagon/team.html/')
As they've tell you before you're not extracting the data from the path, you're just rendering the HTML without any context:
To solve this I would get the Team filtered by the data you're getting of the path, for example:
def TeamDetailView(request, city, name, logo):
Result = Your_Model.objects.all().filter(Q(City=city, Name=name, Logo=logo))
return render(request, 'bandwagon/team.html', {'Teams': Result})
And then in your template you could do something like:
{% for Team in Teams %}
# What you want to achieve
{% endfor %}
I got an error,
TypeError at /app/^detail/(?P1[0-9]+)/$
detail() got an unexpected keyword argument 'pk'
.
I wrote urls.py
urlpatterns = [
path('top/', views.top, name='top'),
path(r'^detail/(?P<pk>[0-9]+)/$',views.detail , name='detail'),
]
in views.py
def top(request):
content = POST.objects.order_by('-created_at')
page = _get_page(blog_content, request.GET.get('page'))
return render(request, 'top.html',{'content':content,"page":page})
def detail(request):
content = POST.objects.order_by('-created_at')
return render(request, 'detail.html',{'content':content})
in top.html
<div>
{% for content in page %}
<div>
<h2>{{ content.title }}</h2>
</div>
{% endfor %}
</div>
<div>
{% for content in page %}
<h2>{{ content.title }}</h2>
<p>SHOW DETAIL</p>
{% endfor %}
</div>
When I put "SHOW DETAIL" button, this error happens.I really cannot understand why I can't access pk. pk is default value, so I think I can access it from everywhere.I wanna make a system when I put "SHOW DETAIL button",content's detail is shown.What is wrong in my code?How should I fix this?Am I wrong to write url?Or is this error's meaning I should write pk in detail method?
You're using the new path() function, which does not take a regex. Either go back to the old url() function, or use <type:name> syntax:
path('detail/<int:pk>/', ...)
In the view function detail(), there's no argument named pk. Add it as the second argument should solve your problem:
def detail(request, pk):
BACKGROUND: I'm learning Django by working on a simple to-do-list app using Django 1.11. The app has "groups" and "items", where items are the individual to-do items and every item belongs to exactly one group. The URLs may end up working something like this:
# snippet from urls.py
url(r'^groups$', views.all_groups, name="all_groups"),
url(r'^groups/(\d+)/$', views.view_group, name="view_group"),
url(r'^items$', views.all_items, name="all_items"),
url(r'^items/(\d+)/$', views.view_item, name="view_item"),
Each of the above pages would display a one or two column table (e.g. all items, or all items in a specific group, etc.), where each table element would be a link to a page to show either a specific group or a specific item.
I have a separate view for each of the above URLs, but I was able to have a single HTML template to render each table successfully. Depending on the URL, there are either 0 or 1 arguments to the URL tag: {% url 'my_url' %} or {% url 'my_url' arg1 %}
Here is a snippet of the HTML template that can render the table with an arbitrary number of rows and columns, but at most two arguments for the url tag:
# lists.html - works for several different views
# every table entry is a dictionary with an 'page_ref' key and a 'display_text' key
<table>
{% for row in url_table %}
<tr>
{% for col in row %}
{% if col.arg2 %}
<td>{{col.display_text}}</td>
{% elif col.arg1 %}
<td>{{col.display_text}}</td>
{% else %}
<td>{{col.display_text}}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</table>
MY QUESTIONS:
1) My code in the HTML template to handle {% url 'my_url' %} vs. {% url 'my_url' arg1 %} vs. {% url 'my_url' arg1 arg2 %} works but it's ugly and limited to at most 2 levels deep on the URL. That is probably fine for what I want, but I don't like the code. Is there a cleaner way to handle an arbitrary number of arguments? When I tried simply not passing arg1/arg2 if not needed, I got an exception about not being able to reverse '' (or some similar error message).
2) In general, is it a bad idea to make a generic template like this? I.e., it is a better practice to have one specific HTML template per view, without making it more general? My guess is no, but I figured that I'd ask.
I can provide the view code as well, but I don't think that it is needed for my question.
Thanks
As you have different views, but you are using same template
So what you can do is remove conditions from template and
send same context variable name from each view containing different absolute url with respect to your view.
# view1 no arguments
class AllGroupView(TemplateView):
template_name = 'path/to/your.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['my_url'] = reverse('all_groups')
return context
#view2 one argument
class GroupView(TemplateView):
template_name = 'path/to/your.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['my_url'] = reverse('view_group', args=[arg1])
return context
...
#view3 two argument
class ItemView(TemplateView):
template_name = 'path/to/your.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['my_url'] = reverse('view_item', args=[arg1, arg2])
return context
In html just use this variable
# use my_url absolute: http://localhost:8000/...
<td>{{display_text}}</td>
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.
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'])
...