How can I paginate list of dictionaries in django - django

I am working on Django pagination and trying to paginate my data which is in the form of dictionaries inside a list, I tried function based approach as per documentation
class MyUserData(TemplateView):
template_name = "myuserdata.html"
form = "userform"
def listing(request):
contact_list = Contact.objects.all()
paginator = Paginator(contact_list, 25) # Show 25 contacts per page.
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'list.html', {'page_obj': page_obj})
I am writing above function inside my class which is inheriting (TemplateView) now the problem is that it is reducing the number of results to 25 but when i click on next it doesn't show next page
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
last »
{% endif %}
</span>
</div>
Flow is like this the user will come and enter the start date and end date and click on submit and get's his history of data and the issue is user gets all his history if he selects longer date range which I want to paginate
this is how data is coming in the form of dictionaries inside list
[{'date':'2021/12/02','address':'minneapolis 111 NY','machine':'TR'},{'date':'2021/12/03','address':'minneapolis 111 NY','machine':'MR'},{'date':'2021/12/02','address':'minneapolis 111 NY','machine':'PR'},{'date':'2021/12/02','address':'minneapolis 111 NY','machine':'CR'},{'date':'2021/12/12','address':'minneapolis 111 NY','machine':'RR'}]
This data is coming from api and not from django models.
I tried function based approach inside class which inherits the template view
is it possible that I can try this inside class, as per documentation if I am writing class I need to use ListView is it possible in Templateview? i have to use ListView only?

Assuming you have a list of dictionaries in a variable data:
from django.core.paginator import Paginator
paginator = Paginator(data, 3)
# 3 here is limit number, you can give dynamic value changing it to your required limit number
objects = paginator.page(1)
# 1 here is page number, you can give dynamic value changing it to your required page number
print(list(objects))
# This should print first 3 data as a list

Related

How to use Jinja in Django framework?

I want to access the elements of a list using Jinja.
Here in the below code both "id" and images are list.
image_name is the field that stores the image
{% for blog in id%}
<h3>{{blog.news_title}}</h3><br/>
<img src="images[loop.index0].image_name"/><br/>
<time>{{blog.news_date}}</time><br/>
click here<br/>
{% endfor%}</li>
Views.py
def BlogViews(request,blog_type):
"""
The blogs are displayed according to the latest, current-month and last-month classification
"""
blog_type=blog_type.replace('-','_')
response_blog=requests.get("API" % (settings.BASE_URL,blog_type),headers=headers,verify=False)
if(response_blog.status_code==200):
data_blog=response_blog.json()
if(data_blog['ErrorCode']==0 and data_blog['SubErrorCode']==0):
blog=BlogYearViews()
blog_images=BlogImages(request,data_blog)
return render(request,"CombinedBlog.html",{"id":data_blog['Data'],"years":blog,"images":blog_images})
else:
return render(request,"Page404.html")
def BlogImages(request,data_blog):
"""
Returns a list of all the images
"""
data_image_list=[]
for i in range(0,len(data_blog['Data'])):
images=data_blog['Data'][i]['image_id']
response_image=requests.get("API"%(settings.BASE_URL),headers=headers,verify=False)
data_image=(response_image.json())
data_image_list=data_image_list+data_image['Data']
return (data_image_list)
You need to zip the two lists together in your view and iterate through them in the template.
blog_images = BlogImages(request, data_blog)
blogs_and_images = zip(data_blog['Data'], blog_images)
return render(request, "CombinedBlog.html", {"blogs_and_images": blogs_and_images, "years":blog})
...
{% for blog, image in blogs_and_images %}
<h3>{{ blog.news_title }}</h3><br/>
<img src="{{ image.image_name }}"/><br/>
<time>{{ blog.news_date }}</time><br/>
click here<br/>
{% endfor %}</li>
Note, you really should be using the {% url %} tag to create the hrefs rather than building it manually like that. Also note, your BlogImages function doesn't need to take the request, since it never uses it, and is anyway extremely un-Pythonic. It should look like this:
def blog_images(data_blog):
data_image_list = []
for blog in data_blog['Data']:
image_id = blog['image_id']
response_image = requests.get("API" % settings.BASE_URL, headers=headers, verify=False)
data_image = response_image.json()
data_image_list.append(data_image['Data'])
return
data_image_list
To access images list using index you can use forloop.counter.
You can use either:
{{ forloop.counter }} # index starts at 1
{{ forloop.counter0 }} # index starts at 0.
In template, you can do:
<img src="{{images[forloop.counter0].image_name.url}}"/>
Advice:
You should consider renaming blog lists as blogs or blog_list inplace of id.

Django using more than one forms in one page from same model

I want to use more than one forms in the same page from the same model.
Ok, lets take it easy.
I have Social modules that have 3 attributes (network, url and image) charfield.
I've added 4 values in Social database (Facebbok, Twitter, Youtube, Pinterest)
In the settings view (settings.html) i want to have all 4 forms (for Facebook, Twitter etc.) to edit them.
Something like that:
Facebook: text input (that contains the current facebook url)
Youtube: text input (that contains the current youtube url)
etc.
So when i go to settings.html i can change, update social url for all networks.
I have something like this for General Settings module (that have 3 fields, Title, Slug, Description and 1 attribute cuz the website have 1 title, 1 slug and 1 description). For this one is pretty simple i can use get_object_or_404 because Settings module have just 1 value and i can select it.. but the problem is Social Module have more values and i want to have on my page all forms from them so i can edit how ever i want.
views.py
def settings(request):
sidebar_items = Sidebar.objects.order_by('position')
social_items = Social.objects.order_by('network')
settings = get_object_or_404(Settings, pk = 1)
if request.method == "POST":
form_settings = SettingsForm(request.POST, instance = settings)
if form_settings.is_valid():
settings = form_settings.save(commit = False)
settings.save()
return HttpResponseRedirect('/dashboard/settings')
else:
form_settings = SettingsForm(instance = settings)
context = {'sidebar_items' : sidebar_items, 'form_settings' : form_settings, 'social_items' : social_items}
return render(request, 'dashboard/settings.html', context)
Django doesn't care how many forms you want to initialize in your view. If they're for the same model, you can use a formset. Otherwise, you can initialize and create objects however you want.
Example:
def your_view(request):
social_items = Social.objects.order_by('network')
forms = []
for index, social_item in enumerate(social_items):
forms.append(SocialForm(request.POST or None, instance=social_item,
prefix="form_{}".format(index)))
if request.method == 'POST':
for form in forms:
if form.is_valid():
form.save()
# do whatever next
return render(request, 'some-template.html', {'forms': forms})
You don't need three separate form tags in your template. You can submit all of the data in one post. Django will try to hydrate an instance of each model from the POST data, and return any errors if that fails.
In your template, you'll need to iterate over the form instances:
# some-template.html
<form action="." method="post" enctype="x-www-form-urlencoded">
{% for form in forms %}
<ol>
<li>
{{ form.network }}
{{ form.network.errors }}
</li>
<li>
{{ form.url }}
{{ form.url.errors }}
</li>
<li>
{{ form.image }}
{{ form.image.errors }}
</li>
</ol>
{% endfor %}
<button type="submit">Save</button>
</form>

next and previous week with Django 1.3 generic views week archive

I am new to Django and I would like to know how can I get the next and previous week links in my template using the week archive generic view. For the archive_month generic view there is a next_month and previous_month object in the template context but not for the archive_week generic view.
models.py
class Day(models.Model):
day = models.AutoField(primary_key=True)
date = models.DateField()
description = models.TextField()
def __unicode__(self):
return str(self.day)
urls.py
week_info = {
"queryset" : Day.objects.order_by('-date'),
"date_field" : "date",
}
urlpatterns = patterns('journal.views',
(r'^(?P<year>\d{4})/(?P<week>\d{2})/$', date_based.archive_week, week_info),
)
You need two links: if current week is 33, previous should read 32, next 34.
Could you possibly grab the current week from the url from within the extra_context dictionary? The dictionary is iterated over after the week variable in generic view code itself, meaning you should have access to it directly in your urls.py (my suspicion)
The url grabs only numbers, but the view works with strings (line 201 in date_based.py):
try:
tt = time.strptime(year+'-0-'+week, '%Y-%w-%U')
date = datetime.date(*tt[:3])
except ValueError:
raise Http404
time.strptime operates on strings, meaning that we need to turn them into integers, add or subtract one, and save those new values as keys in extra context. So I would add the following to your week_info dict:
"next_week" : int(week) + 1,
"prev_week" : int(week) - 1,
Since these links are meant to be args for other date based views, it's fine to leave them as integers. Then build your links from the newly passed context variables.
Hope this helps ;)
You can use the date filter to format the year and week number, and use it to get next and previous week links:
{% if previous_week %}
{% with prev_week_year|date:"Y" prev_week=previous_week|date:"W" %}
<a href="{% url <NAME_OF_YOUR_VIEW> prev_week_year prev_week %}">
See Previous Week</a>
{% endwith %}
{% endif %}
{% if previous_week and next_week %} | {% endif %}
{% if next_week %}
{% with next_week_year|date:"Y" next_week=next_week|date:"W" %}
<a href="{% url <NAME_OF_YOUR_VIEW> next_week_year next_week %}">
See Next Week</a>
{% endwith %}
{% endif %}
You'll also need to name your view.
Don't forget upgrade Django to a newer (more secure) release.

Pagination problem

I'm using this code for my pagination, and I'd like the user's choice to be persistent throughout the site (this has been solved so far)...the only problem now is that the session variable now is permanent until the session is cleared by closing the browser. Also, how can I get the adjacent pages displayed...like in the digg-style Django paginator. I haven't been able to make sense of how to implement this into my code.
The code is as follows:
from django.core.paginator import Paginator, InvalidPage, EmptyPage
def paginate(request, object_list, paginate_by=10):
try:
if "per_page" in request.session:
per_page = request.session["per_page"]
else:
request.session["per_page"] = int(request.REQUEST['p'])
per_page = request.session["per_page"]
request.session.set_expiry(0)
except:
per_page = 10
paginator = Paginator(object_list, per_page)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
try:
items = paginator.page(page)
except (EmptyPage, InvalidPage):
items = paginator.page(paginator.num_pages)
return items
Then in my template I have this to render the pagination links:
<div class="pagination" align="center">
<span class="step-links">
{% if items.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ items.number }} of {{ items.paginator.num_pages }}
</span>
{% if items.has_next %}
next
{% endif %}
</span>
</div>
You can achieve this by enabling sessions.
I recommend reading through the chapter Sessions, Users, and Registration on the djangobook website.
Edit: Now that you've enabled sessions, I think the problem is the hyperlinks in the template. Use an ampersand to separate multiple parameters in a url, for example
next
Edit 2: I'm not sure if I understand what the problem is with the session expiry. The line that sets the session to expire when the browser closes is request.session.set_expiry(0). See the django docs on Using Sessions in views if you want to change that.
To make a Digg style paginator, you need to write a function that takes the current page number and the total number of pages, and returns a list of page numbers. Then, in the template, loop through the page numbers and construct links to the pages.
A list of lists of page numbers would allow you to split the page numbers into groups, eg
[[1,2], [20,21,22,23,24], [30,31]]

Django pagination and "current page"

I'm currently developing a Django application which will make use of the infamous "pagination" technique. I'm trying to figure out how the django.core.paginator module works.
I have an application with a Question model. I will be listing all of the questions using this paginator. There will be 20 questions per page.
def show_question(question_pk):
questions = Question.objects.all()
paginator = Paginator(questions, 20)
page = ... # Somehow figure out which page the question is on
return render_to_response('show_question.html', { 'page' : page })
In the view, where I list the different pages as "... 2, 3, 4, 5, 6, ..." I want to highlight the current page somehow, like many pages do.
There are really two things I want to know:
How do I make Django figure out which page the question is located at?
How would I write my template to properly "highlight" the currently visited page?
EDIT: Sorry, I forgot part of this question. I would also like any page except for the current one to be a link to /questions/{{ that_page.start_index }}. So basically every page link would link to the first question on that page.
Hmm... I see from your comment that you don't want to do the ol' GET parameter, which is what django.core.paginator was written for using. To do what you want, I can think of no better way than to precompute the page that each question is on. As an example, your view will end up being something like:
ITEMS_PER_PAGE = 20
def show_question(question_pk):
questions = Question.objects.all()
for index, question in enumerate(questions):
question.page = ((index - 1) / ITEMS_PER_PAGE) + 1
paginator = Paginator(questions, ITEMS_PER_PAGE)
page = paginator.page(questions.get(pk=question_pk).page)
return render_to_response('show_question.html', { 'page' : page })
To highlight the current page in the template, you'd do something like
{% for i in page.paginator.page_range %}
{% ifequal i page.number %}
<!-- Do something special for this page -->
{% else %}
<!-- All the other pages -->
{% endifequal %}
{% endfor %}
As for the items, you'll have two different object_lists to work with...
page.object_list
will be the objects in the current page and
page.paginator.object_list
will be all objects, regardless of page. Each of those items will have a "page" variable that will tell you which page they're on.
That all said, what you're doing sounds unconventional. You may want to rethink, but either way, good luck.
Django, at least from version 1.2, allows us to complete this task by using pure default pagination template tags.
{% for page in article_list.paginator.page_range %}
{% if page == article_list.number %}
{{ page }}
{% else %}
{{ page }}
{% endif %}
{% endfor %}
Where article_list is instance of
paginator = Paginator(article_list, 20)
try:
article_list = paginator.page(int(page))
except (EmptyPage, InvalidPage):
article_list = paginator.page(paginator.num_pages)
django-pagination should do what you want and comes wrapped in a pretty package you can just plug-in and use. It essentially moves the code from your views to the templates and a middleware.
EDIT: I just saw your edit.
You can get the current objects on a page using {% autopaginate object_list %}, which replaces object_list with the current objects for any given page. You can iterate through it and if you want the first, you should be able to treat it like a list and do object_list[0].
If you want to keep this within your views, you could do something like this:
def show_question(question_pk):
questions = Question.objects.all()
paginator = Paginator(questions, 20)
return render_to_response('show_question.html', { 'page' : paginator })
Within your template, you can access the current page you're on by doing:
# Gives you the starting index for that page.
# For example, 5 objects, and you're on the second page.
# start_index will be 3.
page.start_index
# You can access the current page number with:
# 1-based index
page.number
With that, you should be able to do everything you need.
There are a couple good examples here.