Passing variable to href django template - python

I have some problem and maybe I can give an example of two views below what I want to achieve.
class SomeViewOne(TemplateView):
model = None
template_name = 'app/template1.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# The downloads view contains a list of countries eg France, Poland, Germany
# This returns to context and lists these countries in template1
class ItemDetail(TemplateView):
model = None
template_name = 'app/template2.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
countries_name = kwargs.get('str')
The view should get the passed "x" with the name of the country where I described it
below.
Then on the page I have a list of these countries.
After clicking on the selected country, a new tab should open and show a list of cities in the selected country.
So I am using in loop template1.html as below
{% for x in list_countries %}
<li>
{{ x }}<br>
</li>
{% endfor %}
I can't pass "x" this way. Why?
The url for the next view looks like this
path('some/countries/<str:x>/',views.ItemDetail.as_view(), name='some-name-url'),
And I can't get that 'x' given in the template in the href

There are several mistakes such as:
It should be only x in url tag neither {{x}} nor '{{x}}'
you have passed the value as x in url params (some/countries/<str:x>/) and accessing it using kwargs.get('str') which is not correct it should be kwargs.get('x').
Also you are not including variable countries_name in context and not even returning context.
Note: Assuming that you are already getting some companies in template1.html template that's why you are running loop.
Try below code:
views.py
class ItemDetail(TemplateView):
template_name = 'template2.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['countries_name'] = self.kwargs.get('x')
return context
Template1.html file
{% for x in list_countries %}
<li>
<a onclick="window.open('{% url 'some-name-url' x %}', '_blank')" style='cursor:pointer;'>{{ x }}</a><br>
</li>
{% endfor %}
Then you can this countries_name value passed from template1.html in template2.html.
template2.html
<p>The clicked country is {{countries_name}}</p>

If Manoj's solution doesn't work, try removing the single quotes AND {{ }}. In my program, my integer doesnt need to be wrapped with {{ }}, so maybe neither does your string.
I have this in my code:
{% for item in items %}
<div class="item-title">
{{ item }}<br>
</div>
Edit {{ item }}
{% endfor %}
It works just fine. Try:
{{ x }}

You don't need pass that variable with single quotes.
<a href="{% url 'some-name-url' {{ x }} %}" #Just removed single quotes from variable x.
And see if it shows on template

Related

Displaying python list of dictionaries on front-end using django [duplicate]

I'm passing a dictionary from my view to a template. So {"key1":"value1","key2":"value2"} is passed in and looping through key,value pairs is fine, however I've not found an elegant solution from access directly in the view from a specific key, say "key1" for example bu json.items["key1"]. I could use some if/then statements, but I'd rather do directly is there a way?
Here is looping code in the html template:
{% for key, value in json.items %}
<li>{{key}} - {{value}}</li>
{% endfor %}
The Django template language supports looking up dictionary keys as follows:
{{ json.key1 }}
See the template docs on variables and lookups.
The template language does not provide a way to display json[key], where key is a variable. You can write a template filter to do this, as suggested in the answers to this Stack Overflow question.
As #Alasdair suggests, you can use a template filter.
In your templatetags directory, create the following file dict_key.py:
from django.template.defaultfilters import register
#register.filter(name='dict_key')
def dict_key(d, k):
'''Returns the given key from a dictionary.'''
return d[k]
Then, in your HTML, you can write:
{% for k in json.items %}
<li>{{ k }} - {{ json.items|dict_key:k }}</li>
{% endfor %}
For example, to send the below dictionary
dict = {'name':'myname','number':'mynumber'}
views :
return render(request, self.template_name, {'dict': dict})
To render the value in html template:
<p>{{ dict.name }}</p>
It prints 'myname'
To overcome this problem you could try something like this:
def get_context_data(self, **kwargs):
context['cart'] = []
cart = Cart()
cart.name = book.name
cart.author = book.author.name
cart.publisher = book.publisher.name
cart.price = 123
cart.discount = 12
cart.total = 100
context['cart'].append(cart)
return context
class Cart(object):
"""
Cart Template class
This is a magic class, having attributes
name, author, publisher, price, discount, total, image
You can add other attributes on the fly
"""
pass
By this way you can access your cart something like this:
{% for item in cart %}
<div class="jumbotron">
<div>
<img src="{{item.image}}" />
<div class="book_name"> <b>{{item.name}}</b></div>
<div class="book_by"><i>{{item.author}}</i></div>
<span>Rs. {{item.price}}</span> <i>{{item.discount}}% OFF </i>
<b>Rs. {{item.total}}</b>
{% endfor %}

Making Pagination work with django-filter library and CBV

I know a related question has been asked before here:
how can I use pagination with django_filter but I really tried to make it work with mine but because I use a custom LinkWidget or class i find it hard to included the pagination in to my ResultsFilter class or even get it to work with views and template.
Here is my code so far:
filter.py
# I didn't do much just changed so filters would be displayed as text/url just like django admin works instead of FORMS
# and i also add style to the returned <li>
class MyLinkWidget(widgets.LinkWidget):
"""docstring for ClassName"""
def __init__(self, **karg):
# super().__init__()
super(widgets.LinkWidget, self).__init__(**karg)
def option_string(self):
return '<li class="list-group-item list-group-item-dark"><a%(attrs)s href="?%(query_string)s">%(label)s</a></li>'
class ResultsFilter(FilterSet):
important = AllValuesFilter(widget=MyLinkWidget(attrs={"class":"list-group"}))
viewed = AllValuesFilter(widget=MyLinkWidget(attrs={"class":"list-group"}))
class Meta:
model = Results
fields = ['viewed', 'important',]
views.py
class ResultView(ListView):
paginate_by = 3
model = Results
template_name = 'results_filter.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = ResultsFilter(self.request.GET, queryset=self.get_queryset())
return context
and finally template file is:
results_filter.html
<div class="filter_header">
<span class="l">Filter by Viewed</span>
{{filter.form.viewed}}
</div>
<div class="filter_header">
<span class="l>Filter by Viewed</span>
{{filter.form.important}}
</div>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
{% endif %}
</span>
</div>
EDIT
So basically, when i am at http://127.0.0.1:8000/ it shows all records ignoring paginate_by = 3 and i click next url becomes http://127.0.0.1:8000/?page=2 but still showing all records. Which means pagination is not working but clicking filter for important or entering url as http://127.0.0.1:8000/results/?page=3&important=False. I noticed only data i.e (20 records) where importance is False are shown but i need just 3 page records so i can click next to view others.
Bottom line is i think paginate_by is not linked to the queryset returned by Django-Filter based on my classes above
This is a little late, but try this:
from django_filters.views import FilterView
class ResultView(FilterView):
filterset_class = ResultsFilter
paginate_by = 3
Then in you html use object_list, it will contain the paginated results.
{% for object in object_list %}
...
{% endfor %}

Unexpected Keyword when I Pass Url Parameter

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 %}

populate bootstrap dropdown on the base.html using django

Im using django 1.9 and python 3.5 which i'm both really new too, and i'm having trouble populating a bootstrap dropdown which is located in the base.html.
So far i have this:
base.html:
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Events
<b class="caret"></b></a>
<ul class="dropdown-menu">
{% if categories %}
{% for cat in category %}
<li><a href="{% url 'categories' pk=cat.pk %}">
{{ cat.name }}</a></li>
{% endfor %}
{% else %}
There are no categories present.
{% endif %}
</ul>
</li>
views.py:
def categories(request, pk):
category = Category.objects.get(pk=pk)
return render(request, 'categories.html', {'category': category})
urls.py:
url(r'^categories/(?P<pk>\d+)/$', views.categories, name='categories'),
So i want the drop down to display the available categories from the database and then when i click on one it will obviously load up the categories.html displaying the relevant category.
any help would be much appreciated.
Edit: sorry i forgot to say the problem i am having.
Im not getting the drop down populated, and is only giving me the "there is no categories present"
There are a couple of issues here:
Firstly, you don't have any context in your view called categories, yet you're checking for them in your template. Your view function is called 'categories', which might be creating some confusion for you. However, this is not context that is accessible to your view. It's just a function.
Secondly, you're not getting a list of categories (which you could iterate as you are in your template) in your view, you're getting a single category with:
category = Category.objects.get(pk=pk)
# using the get() method means you're requesting a single object
So you need to do something more like:
categories = Category.objects.all()
# here, we're getting a QuerySet (list of objects), rather a single object
Then add the categories to your context. So your view would end up looking like this:
def categories(request, pk):
categories = Category.objects.all()
return render(request, 'categories.html', {'categories': categories})
Also, you'll need to change your iterator to iterate over categories, not category:
{% for cat in categories %}
<li><a href="{% url 'categories' pk=cat.pk %}">
{{ cat.name }}</a></li>
{% endfor %}
So, "categories" variable will never give you "true", while you do not define it, and add it to template context.
Do this in your python code
def categories(request, pk):
categories = Category.objects.all()
return render(request, 'categories.html', {'categories': categories})

How to paginate a filtered queryset in listview

i am trying to add a search bar on my listview page. it will get all the post items first time, and if a query put in search box, and submitted, it will return a filtered queryset. it renders fine, but only has problem with pagination. for non-filtered queryset, the next page will get the next two posts without any problem, but for filtered queryset, i can see the queryset is correctly reflected by see less pages, but the next page gets me the second page of non-filtered queryset not the filtered queryset. Can anyone give me some pointers on what i am doing wrong here. Thanks
My template looks like this:
{% block content %}
.....
<form action="" method="get">
<input type="text" name='q'>
<input type="submit" value="Search">
</form>
{% for post in object_list %}
....
{% endfor %}
{% if is_paginated %}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
<<
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
>>
{% endif %}
</span>
</div>
{% endif %}
I have a listview like below.
class Postlist(ListView):
model=post
paginate_by = 2
query_string = ''
def get_queryset(self):
if ('q' in self.request.GET) and self.request.GET['q'].strip():
query_string = self.request.GET['q']
entry_query = get_query(query_string, ['title', 'body',]) ## call get_query() function
queryset = post.objects.filter(entry_query).order_by('-created')
else:
queryset=post.objects.all().order_by('-created')
return queryset
def get_context_data(self):
context = super(ListView, self).get_context_data(**kwargs)
context['q'] = query_string
## do sth here to pre-populate the input text box
return context
Let me close this. Two options: using hidden field to save the user search terms, and get this to filter off queryset on each request; or using session. Here is the session code sample. The only drawback i could think of using session cache is it caches a whole queryset in memory, so it will kill the memory for high traffic website. Since i am using it for personal website, it doesnt hurt at all.
View file
class Postlist(ListView):
model=post
paginate_by = 2
def get_queryset(self):
query_string = ''
if ('search' in self.request.GET) and self.request.GET['search'].strip():
query_string = self.request.GET['search']
entry_query = get_query(query_string, ['title', 'body',]) ## call get_query to clean search terms
queryset = post.objects.filter(entry_query).order_by('-created')
self.request.session['searchset']=queryset
else:
if self.request.session.get('searchset') and ('page' in self.request.GET):
queryset=self.request.session['searchset']
else:
queryset=post.objects.all().order_by('-created')
if self.request.session.get('searchset'):
del self.request.session['searchset']
return queryset
form file
from django import forms
class SearchForm(forms.Form):
search = forms.CharField(max_length=30)

Categories

Resources