dynamic pagination with django - python

Okay so this is first time using pagination with Django and I am trying to prevent it from reloading my view on each page turn.
I'm handling the pagination in the view like this:
page = request.GET.get('page', 1)
print page
paginator = Paginator(list(od.iteritems())[:24], 12)
try:
data = paginator.page(page)
except PageNotAnInteger:
data = paginator.page(1)
except EmptyPage:
data = paginator.page(paginator.num_pages)
print data
save_query_form = SaveQueryForm(request.POST or None)
#if request.method == 'POST':
if save_query_form.is_valid():
profile = save_query_form.save(commit=False)
profile.user = request.user
profile.save()
context = {
"title":"Search",
'data': data,#list(od.iteritems()),
'tools': od_tools.iteritems(),
'methods': od_methods.iteritems(),
'data4': od_data.iteritems(),
'search_phrase': " ".join(instanceValuesString),
'json_dump': js_data,
'form': save_query_form,
}
return render(request, 'results.html', context)
and the pagination is handled in the html:
{% if data.has_other_pages %}
<div id='page-slide'>
<ul class="pagination" start='$offset'>
{% if data.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in data.paginator.page_range %}
{% if data.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if data.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
</div>
{% endif %}
The issue that I am having is that whenever I switch to another page my entire view will run again and the data will does not reflect the original search query and instead defaults to an empty query.
I was wondering if there is a simple way to either handle pagination dynamically or prevent the page reload when toggling between pages?
Any help is appreciated, thanks.
Update Search Form:
<form action="{% url 'results-view' %}" method="POST" class="autocomplete-me ui-widget" id="myform" >
{% csrf_token %}
<div class="ui-widget" style="text-align:center;">
<input type="text" id="id_q" name="q" placeholder="{{ search_phrase }}">
<br></br>
<div style="text-align:center;" id='adjust-button'>
<input type='submit' class='btn btn-secondary btn-lg' id ='search-btn' value='Search'/>
<a class='btn btn-secondary btn-lg' id ='clear-btn' href="{% url 'inital' %}">Clear</a>
</div>
</div>
</form>

You noted in a comment that you get your search value with instanceValuesString = request.POST.get(u"q").encode('utf-8').strip(). As one commenter correctly pointed out, this means that when you click your "next page" links (making a GET request), your view doesn't receive the information it needs to return search results.
One way to fix this would be to get your instanceValuesString from a GET request instead of a POST request. For instance, perhaps your list view is at
http://example.com/StuffList
You could look for URLs that provide a search querystring:
http://example.com/StuffList?search=goodstuff
And then grab that in your view:
instanceValuesString = request.GET.get('search', None)
if instanceValuesString is not None:
#you have detected a search query; filter results, process request, etc.
One side effect here is that the way you currently construct your next/previous page URLs will break. Consider the example search URL; your current template would construct a link for page 2 like so:
http://example.com/StuffList?search=goodstuff?page=2
This won't work; it should be &page=2. Fortunately there's an easy fix; check out the second answer to this question: Altering one query parameter in a url (Django). Using that url_replace instead of constructing those links with the basic url template tag will solve this part of the issue.

This is very much simplified with below package
http://django-simple-pagination.readthedocs.io/en/latest/

Related

Failed to get value from html page in Django

I have a problem with trying to get a response from my HTML page using Django (admin).
I have a pretty simple div = contenteditable and need to pass data from this div back after the submit button was clicked.
Everything, including choosing selection and opening the intermediate page works fine. But when I tapped submit button, the condition if "apply" in request.POST failed to work.
Please, tell me, what I'm doing wrong?
This is my Django admin:
class QuestionAdmin(AnnotatesDisplayAdminMixin, admin.ModelAdmin):
def matched_skills(self, question):
return ', '.join(s.name for s in question.skills.all())
def update_skills(self, request, queryset):
if 'apply' in request.POST:
print("something")
skills = []
for question in queryset:
skills.append(self.matched_skills(question))
return render(request,
'admin/order_intermediate.html',
context={'skills': skills})
update_skills.short_description = "Update skills"
This is my order_intermediate.html page:
{% extends "admin/base_site.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
<h1>Adjust skills. </h1>
{% for skill in skills %}
<div>
<div id="title" style="margin-left: 5px" contenteditable="true" > {{ skill }} </div>
</div>
{% endfor %}
<input type="hidden" name="action" value="update_status" />
<input type="submit" name="apply" value="Update skills"/>
</form>
{% endblock %}
Actually, request.POST is an HttpRequest object. For getting available keys in the body of the request, you need to use "request.POST.keys()" method. So, you can simply change your condition to:
if 'apply' in request.POST.keys():
print("something")
In my knowledge, you can not send div content with form submit. However you can use input tag with array in name attribute for this. This will send an array as post variable when submit
First, send skills as a enumerate object from your views
return render(request, 'admin/order_intermediate.html', context={'skills': enumerate(skills)})
Then edit your html to this (Note: if you have css in title id, change it to title class)
{% for i,skill in skills %}
<div>
<input class="title" name="skill[{{ i }}]" value="{{ skill }}" style="margin-left: 5px">
</div>
{% endfor %}
and handle array with any action you want to perform in update_skills()
for skill in request.POST.getlist('skill[]'):
# your code

Django Get Root Path From Current URL

I am developing a Django website using the Wagtail CMS. I have a navbar at the top of the page where using template tags, it loops through pages in the navigation variable.
{% for item in navigation.menu_items.all %}
<a class="nav-link {% if request.get_full_path == item.link %}active{% endif %}" href="{{ item.link }}" {% if item.open_in_new_tab %} target="_blank"{% endif %}>{{ item.title }}</a>
{% endfor %}
Say that the URL is http://localhost:8000/blog/ and the page URL is the same, then the active class is applied to that iteration.
The problem arises when I am on a page with the URL such as http://localhost:8000/blog/example-blog-post/, this does not match with http://localhost:8000/blog/ and the active class is not applied, even though I am in the blog.
Is there a way to strip the URL and only keeping the root path, so http://localhost:8000/blog/example-blog-post/ becomes http://localhost:8000/blog/ so that the active class can be applied to subpages in the directory?
You can use slice filter
{% if request.path|slice:":5" == item.link %} active{% endif %}
OR
You can use in operator.
So instead {% if request.get_full_path == item.link %} do {% if item.link in request.get_full_path %} or to catch homepage {% if request.get_full_path in item.link and request.get_full_path != '/' or request.get_full_path == item.link %}
This probably isn't the most efficient way, but at the moment for me, it's the only way.
I created a custom template tag which takes in the context and the menu item object, then returns the active class name if the current URL matches the URL of the nav-item (item)
In the HTML, as each nav-item is iterated, the item is passed to the get_active method (a custom template tag that I made)
{% load menu_tags %}
{% get_menu "MAIN" as navigation %}
{% for item in navigation.menu_items.all %}
{% get_active item as active_class %}
<a class="nav-link {{ active_class }}" href="{{ item.link }}" {% if item.open_in_new_tab %} target="_blank"{% endif %}>{{ item.title }}</a>
{% endfor %}
Template tag:
#register.simple_tag(takes_context=True)
def get_active(context, item):
request = context['request']
currentURL = request.path
linkURL = item.link
currentURLStripped = str(currentURL).replace("/", "")
linkURLStripped = str(linkURL).replace("/", "")
if linkURLStripped=="" and currentURLStripped=="":
return "active"
elif linkURLStripped in currentURLStripped and linkURLStripped!="":
return "active"
else:
return ""
The code above simply takes the URL of the page the user is currently on, for example, if the user is on http://localhost:8000/blog then currentURL will be /blog/. The linkURL is the URL property of the item object, for the item object which links to the contact me page, its URL property will be /contact-me/ and thus the linkURL will be the same.
The method simply strips the "/" from the URL strings. If the URL is for the homepage (i.e. it's /) then the variable will be empty. if linkURLStripped=="" and currentURLStripped=="": catches the homepage.
elif linkURLStripped in currentURLStripped and linkURLStripped!="": catches the other pages and ignores the homepage.

How to access variables within html template url_for

I'm building a Netflix like website for my Devops course. I made a Python list of dictionaries (Mockfilms) to define my films, and want to populate a database (Ratings) with reviews in preparation for sending data in the format :filmid: :userid: :rating: to a recommendation engine.
My index page is a list of film images with a link to a review form under each one. I want each review form to appear on a different url (/review/ID where ID is saved in mockfilms as oid). In order to do this I want to access mockfilms.oid, then pass it to the view function to make the url for the form. Once the form is complete I then want to add this ID to the Ratings database. Here is what I have so far:
Index:
{% extends "base.html" %}
{% block content %}
<h1>Hello, {{ current_user.username }}! Welcome to our extensive video library:</h1>
{% for film in mockfilms %}
{% set ID = film.oid %}
<div>
<a href = {{ film.video }}>
<img src = {{ film.image }} alt = "doh" style = "width:200px;height:200px;border:0;">
</a>
</div>
<div>
">Leave a review here!
{% endfor %}
{% endblock %}
Route:
#app.route('/review/<ID>', methods = ['GET', 'POST'])
#login_required
def review(ID):
form = ReviewForm()
if form.validate_on_submit():
review = Ratings(User_id = current_user.id, Score_given = form.score.data, Film_id = ID)
db.session.add(review)
db.session.commit()
flash('Thanks for your review')
return redirect(url_for('index'))
return render_template('review.html', title='Review Page', form=form)
The following error is what I get when I run it:
File "/home/jc/Desktop/Lokal/DevopsAssig/microblog/Kilfinnan/lib/python3.5/site-packages/werkzeug/routing.py", line 1768, in build
raise BuildError(endpoint, values, method, self)
werkzeug.routing.BuildError: Could not build url for endpoint 'review'. Did you forget to specify values ['ID']?
From this I assume that the issue is with the ID variable within this template. My searchings and learnings led me to believe that {% set %} in the index template would let me declare the ID variable and then use it in the dynamic.
Try this:
{% block content %}
<h1>
Hello, {{ current_user.username }}!
Welcome to our extensive video library:
</h1>
{% for film in mockfilms %}
<div>
<a href="{{ film.video }}">
<img src="{{ film.image }}" alt="doh" style="width:200px;height:200px;border:0;" />
</a>
</div>
<div>
<a href="{{ url_for('review', ID=film.oid) }}">
Leave a review here!
</a>
</div>
{% endfor %}
{% endblock %}
Ultimately your solution was quite close, but it is not necessary to use the Jinja set command when you need to pass the variable into url_for() function using the keyword for the parameter. You could still do it using {% set ID = film.oid %} but it would be a bit superfluous.
Try to provide key=value arguments into your url_for function.
Something like this
">Leave a review here!
Also Flask have a great documentation, Flask docs

django-filter use paginations

I'm using the django-filter package to provide a search functionality on my List View.
Now I want to add a pagination to that view as well.
I'm trying to combine pagination to a filtered queryset, but I have no clue on how to go on.
So far, I have tried the following on views.py:
def search(request):
qs = local_url.objects.filter(global_url__id=1).all()
paginator = Paginator(qs, 25)
page = request.GET.get('page')
try:
pub = paginator.page(page)
except PageNotAnInteger:
pub = paginator.page(1)
except EmptyPage:
pub = paginator.page(paginator.num_pages)
url_filter = PublicationFilter(request.GET, queryset=qs)
return render(request, 'ingester/search_list.html', {'filter': url_filter, 'publication':pub})
This worked for me:
in my template instead of using this
<li>{{ i }}</li>
I wrote this:
{% if 'whatever_parameter_you_use_to_filter' in request.get_full_path %}
<li><a href="{{ request.get_full_path }}&page={{ i }}"{{ i }}</a></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
I hope it helps :)
To use Django Filter and paginate the filtered result you can do the following:
Create a filter class for your model:
On my_project/my_app/filters.py:
import django_filters
class MyModelFilter(django_filters.FilterSet):
class Meta:
model = MyModel
# Declare all your model fields by which you will filter
# your queryset here:
fields = ['field_1', 'field_2', ...]
Every FilterSet object has a .qs property which contains the filtered queryset and you can even override it if you want.
We will paginate the .qs property of our MyModelFilter:
On my_project/my_app/views.py:
from . import filters
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def my_view(request):
# BTW you do not need .all() after a .filter()
# local_url.objects.filter(global_url__id=1) will do
filtered_qs = filters.MyModelFilter(
request.GET,
queryset=MyModel.objects.all()
).qs
paginator = Paginator(filtered_qs, YOUR_PAGE_SIZE)
page = request.GET.get('page')
try:
response = paginator.page(page)
except PageNotAnInteger:
response = paginator.page(1)
except EmptyPage:
response = paginator.page(paginator.num_pages)
return render(
request,
'your_template.html',
{'response': response}
)
And there you have it!
PS_1: Django filter in my experience, "plays" better with Django Rest Framework.
PS_2: If you are about to utilize DRF, I have written an example on how to use pagination in a function based view which you can easily combine with a FilterSet:
#api_view(['GET',])
def my_function_based_list_view(request):
paginator = PageNumberPagination()
filtered_set = filters.MyModelFilter(
request.GET,
queryset=MyModel.objects.all()
).qs
context = paginator.paginate_queryset(filtered_set, request)
serializer = MyModelSerializer(context, many=True)
return paginator.get_paginated_response(serializer.data)
To add to the answers, I did it with html tables too along with django-filters and Paginator. Below are my view and template files. The template tag is needed to make sure you pass the right parameters to the pagination url.
search_view.py
from django.shortcuts import render
from app.models.filters_model import ApiStatusFilter
from app.models.api_status import ApiStatus
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from datetime import datetime, timedelta
def status(request):
all_entries_ordered = ApiStatus.objects.values().order_by('-created_at')[:200]
for dictionarys in all_entries_ordered:
dictionarys
apistatus_list = ApiStatus.objects.values().order_by('-created_at')
apistatus_filter = ApiStatusFilter(request.GET, queryset=apistatus_list)
paginator = Paginator(apistatus_filter.qs, 10)
page = request.GET.get('page')
try:
dataqs = paginator.page(page)
except PageNotAnInteger:
dataqs = paginator.page(1)
except EmptyPage:
dataqs = paginator.page(paginator.num_pages)
return render(request, 'status_page_template.html', {'dictionarys': dictionarys, 'apistatus_filter': apistatus_filter, 'dataqs': dataqs, 'allobjects': apistatus_list})
status_template.html
{% load static %}
{% load my_templatetags %}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="{% static 'css/table_styling.css' %}">
<meta charset="UTF-8">
<title>TEST</title>
</head>
<body>
<table>
<thead>
<tr>
{% for keys in dictionarys.keys %}
<th>{{ keys }}</th>
{% endfor %}
</tr>
</thead>
<form method="get">
{{ apistatus_filter.form.as_p }}
<button type="submit">Search</button>
{% for user in dataqs.object_list %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.date_time }}</td>
<td>{{ user.log }}</td>
</tr>
{% endfor %}
</form>
</tbody>
</table>
<div class="pagination">
<span>
{% if dataqs.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ dataqs.number }} of {{ dataqs.paginator.num_pages }}.
</span>
{% if dataqs.has_next %}
next
last »
{% endif %}
</span>
</div>
</body>
</html>
my_templatetags.py
from django import template
register = template.Library()
#register.simple_tag
def query_transform(request, **kwargs):
updated = request.GET.copy()
for k, v in kwargs.items():
if v is not None:
updated[k] = v
else:
updated.pop(k, 0)
return updated.urlencode()
It took me some time to find the DRYer and cleaner solution to fix this issue and the best one, in my opinion, is the one using template tags.
from django import template
register = template.Library()
#register.simple_tag
def relative_url(value, field_name, urlencode=None):
url = '?{}={}'.format(field_name, value)
if urlencode:
querystring = urlencode.split('&')
filtered_querystring = filter(lambda p: p.split('=')[0] != field_name, querystring)
encoded_querystring = '&'.join(filtered_querystring)
url = '{}&{}'.format(url, encoded_querystring)
return url
and in your template
{{ i }}
Source: Dealing With QueryString Parameters
The most important part here is the how you construct your URLs in the template.
you probably have
{% if pages.has_previous %}
<li>Prev</li>
{% endif %}
which is perfectly fine if you are using only it for switching between the initial paginated results.
But the tricky part is when you use the django-fitler filters, the querystring (that part after the '?') gets totally new key-values pairs, disregarding your ?page=2 or similar.
So to make pagination work with filtered results, when you click the "Next" or "Prev" button - among the key-values from django-fitler you also need to pass the &page=5 as pair.
As #stathoula mentioned, you need to check if at least one of your filter fields is already present in the querystring. If it is, then you need to use the already present key-value pairs, followed by the new &page=3 pair.
It seems very simple, but I had to done small hackish not to repeat the &page=1 over and over again within the querystring as a user is clicking trough the arrows.
In my case I'm having 'title' as a filter, so I need to check if it's already present there.
Here's a snippet of what I've made working perfectly fine for my project.
templates/pagination.html
<div class="paginator">
{% with request.get_full_path as querystring %}
<ul class="pagination nav navbar-nav">
<!-- Previous page section -->
{% if pages.has_previous %}
{% if 'title' in querystring %}
{% if 'page' in querystring %}
<li class="paginator {% if pages.number == page %}active{% endif %}">
Prev
</li>
{% else %}
<li class="paginator {% if pages.number == page %}active{% endif %}">
Prev
</li>
{% endif %}
{% else %}
<li class="paginator {% if pages.number == page %}active{% endif %}">
Prev
</li>
{% endif %}
{% endif %}
<!-- All pages section -->
{% for page in pages.paginator.page_range %}
{% if 'title' in querystring %}
{% if 'page' in querystring %}
<li class="paginator {% if pages.number == page %}active{% endif %}">
{{ page }}
</li>
{% else %}
<li class="paginator {% if pages.number == page %}active{% endif %}">
{{ page }}
</li>
{% endif %}
{% else %}
<li class="paginator {% if pages.number == page %}active{% endif %}">
{{ page }}
</li>
{% endif %}
{% endfor %}
<!-- Next page section -->
{% if pages.has_next %}
{% if 'title' in querystring %}
{% if 'page' in querystring %}
<li class="paginator {% if pages.number == page %}active{% endif %}">
Next
</li>
{% else %}
<li class="paginator {% if pages.number == page %}active{% endif %}">
Next
</li>
{% endif %}
{% else %}
<li class="paginator {% if pages.number == page %}active{% endif %}">
Next
</li>
{% endif %}
{% endif %}
</ul>
{% endwith %}
</div>
Here is the view, just in case:
app/views.py
def index(request):
condo_list = Condo.objects.all().order_by('-timestamp_created')
condo_filter = CondoFilter(request.GET, queryset=condo_list)
paginator = Paginator(condo_filter.qs, MAX_CONDOS_PER_PAGE)
page = request.GET.get('page')
try:
condos = paginator.page(page)
except PageNotAnInteger:
condos = paginator.page(1)
except EmptyPage:
condos = paginator.page(paginator.num_pages)
return render(request, 'app/index.html', {
'title': 'Home',
'condos': condos,
'page': page,
'condo_filter': condo_filter,
})
Here's a working example:
.
My approach for "remember filter/query URL parameters" for paginated results: passing the current URL parameters as a context variable:
# views.py
class PublicationFilterView(FilterView):
model = Publication
filterset_class = PublicationFilter
paginate_by = 15
def get_context_data(self, *args, **kwargs):
_request_copy = self.request.GET.copy()
parameters = _request_copy.pop('page', True) and _request_copy.urlencode()
context = super().get_context_data(*args, **kwargs)
context['parameters'] = parameters
return context
# templates/path/to/pagination.html
<a href="?page={{ page_obj.next_page_number }}&{{ parameters }}">
Next
</a>
this one works 100% with me
views.py:
def search(request):
category=Category.objects.all()
try:
qs=request.GET["qs"]
products=Product.objects.filter(Q(name__icontains=qs) |Q(details__icontains=qs) | Q(category__name__icontains=qs) | Q(branch__child__icontains=qs) | Q(manufacturer__name__icontains=qs) | Q(color__name__icontains=qs)).distinct()
print(products)
search=f"qs={qs}"
except:
search=None
and in HTML
<ul class="shop-p__pagination">
{% if products.has_provious %}
<li>
<a class="fas fa-angle-left" href="?page={{ products.previous_page_number }}&{search}"></a></li>
{% endif %}
{% for i in products.paginator.page_range %}
{% if products.number == i %}
<li class="is-active">{{i}}</li>
{% else %}
<li>{{i}}</li>
{% endif %}
{% endfor %}
{% if products.has_next %}
<li>
<a class="fas fa-angle-right" href="?page={{ products.next_page_number }}&{{search}}"></a></li>
{% endif %}
</ul>
As I understood you goal is to paginate your filtered query set. If so, you can pass "qs" property of PublicationFilter object to Paginator constructor:
def search(request):
qs = local_url.objects.filter(global_url__id=1).all()
url_filter = PublicationFilter(request.GET, queryset=qs)
paginator = Paginator(url_filter.qs, 25)
page = request.GET.get('page')
try:
pub = paginator.page(page)
except PageNotAnInteger:
pub = paginator.page(1)
except EmptyPage:
pub = paginator.page(paginator.num_pages)
url_filter = PublicationFilter(request.GET, queryset=qs)
return render(request, 'ingester/search_list.html', {'publication':pub})
url_filter.qs contains filtered QuerySet
url_filter.queryset contains non-filtered QuerySet
Simple & sweet,
use this, pip install filter-and-pagination
https://pypi.org/project/filter-and-pagination/
Implementation Step
install package by pip install filter-and-pagination
import FilterPagination by from filter_and_pagination import FilterPagination in view.py
in your function writte code as bellow standards...
queryset = FilterPagination.filter_and_pagination(request, Customer)
serialize_data = CustomerSerializer(queryset['queryset'], many=True).data
resultset = {'dataset': serialize_data, 'pagination': queryset['pagination']}
in this code Customer is Django model &
CustomerSerializer is a DRF Serializer class
in the resultset it contains dataset & pagination data, In this format (API Response) link: https://github.com/ashish1997it/filter-pagination-dj#demo
For the API request follow PostMan collection link: https://github.com/ashish1997it/filter-pagination-dj#postman in the header section it will take a parameter & request you customize as per your requirement
If you still face any difficulty then contact me :)
In get_context_data() function:
form_submitted = 'csrfmiddlewaretoken' in self.request.GET
context['cleaned_full_path'] = '{}{}'.format(
self.request.get_full_path().split('&page' if form_submitted else '?page')[0],
'&' if form_submitted else '?'
)
Then, in your template, load in something like
<a href="{{ cleaned_full_path }}page={{ page_obj.paginator.num_pages }}"
In addition to #stathoula and in response to #Benbb96, I managed to erase the additional page parameters with a regular expression, overriding the setup method in the class based view:
import re
...
class MyView(ListView):
...
def setup(self, request, *args, **kwargs) -> None:
request.GET.get("page")
request.META["QUERY_STRING"] = re.sub("(&|\?)page=(.)*", "", request.META.get("QUERY_STRING", ""))
return super().setup(request, *args, **kwargs)
Hope it helps anyone!
More info:
Request and Response Objects - Django docs
as_view() - Django docs

Updating single Django model field

I have 3 database model - Semester, Section and Notecard
The Notecard model has a "Known" field that I use to classify the Notecard objects into "piles" as Known (1) or Unknown (0):
class Notecard(models.Model):
notecard_name = models.CharField(max_length=50)
notecard_body = models.TextField()
section = models.ForeignKey(Section)
known = models.BooleanField()
I have two views - known_list and unkown_list that displays the corresponding piles (known_list below for reference):
def known_list(request, section_name):
try:
section = Section.objects.get(section_name__iexact = section_name)
except Section.DoesNotExist:
raise Http404
known_list = Notecard.objects.filter(known=1, section=section)
paginator = Paginator(known_list, 1)
if known_list:
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
try:
known = paginator.page(page)
except (EmptyPage, InvalidPage):
known = paginator.page(paginator.num_pages)
context = RequestContext(request)
return render_to_response('notecards/known.html', {"known": known}, context_instance=context)
else:
url = reverse('notecard_list', kwargs={'section_name': section_name})
return HttpResponseRedirect(url)
This view brings in the section_name from the previous view to display all the Notecard objects that are in the section that was clicked on, and in the known pile.
In the template below, you can see that I paginate the notecards to one a page:
{% extends "base.html" %}
{% block content %}
<h1 class='title'>NoteCards!</h1>
{% for notecard in known.object_list %}
<h1 class='notecard'>{{ notecard.notecard_name }}</h1>
<h3 class='notecard'>{{ notecard.notecard_body }}</h3>
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if known.has_previous %}
<a class="navlink" href="?page={{ known.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ known.number }} of {{ known.paginator.num_pages }}
</span>
{% if known.has_next %}
<a class="navlink" href="?page={{ known.next_page_number }}">next</a>
{% endif %}
</span>
</div>
{% endblock %}
urls.py
urlpatterns += patterns('',
url(r'^(?P<section_name>[\w|\W]+)/unknown/$', unknown_list, name="unknown_list"),
url(r'^(?P<section_name>[\w|\W]+)/known/', known_list, name="known_list"),
url(r'^semester/(?P<semester_name>[\w|\W]+)/', section_list, name="section_list"),
url(r'^section/(?P<section_name>[\w|\W]+)/', notecard_list, name="notecard_list"),
url(r'^notecard/(?P<notecard_name>[\w|\W]+)/', notecard_detail, name="notecard_detail"),
url(r'^$', semester_list, name="semester_list"),
)
That said, I would like to add a "Send to Unknown" button that will allow users to send the notecard whose page they are currently on to the unknown pile (Simply changing the known field to = 0, removing the notecard from the pagination list, and moving to the next page in the pagination).
I have tried replicating my new_notecard view which contains a full form of the model, but I was unable to figure out how to update a single field.
I have also tried using queryset.update() but was unable to figure out how to capture the pk from the specific notecard.
I've been trying to figure this out on my own for over a month, but I've been unsuccessful. Thank you in advance.
EDIT:
It seems like my hang up is pulling the pk of the notecard on each page of the pagination. For example, if I am on page 3 of the pagination - when the "Send to Unknown" button is pushed, how do I identify that notecard in my view and update it from known (1) to unknown (0)
you must create a specific view with a specific url to handle this, for example:
# urls.py
url(r'^movetounknown/(?P<notecard_id>[\w|\W]+)/', notecard_move_to_unknown)
# views.py
#require_POST
def notecard_move_to_unknown(request, notecard_id):
notecard = Notecard.objects.get(pk=notecard_id)
notecard.known = False
notecard.save()
return HttpResponseRedirect(request.POST['next'])
# template
{% for notecard in known.object_list %}
<h1 class='notecard'>{{ notecard.notecard_name }}</h1>
<h3 class='notecard'>{{ notecard.notecard_body }}</h3>
<form action="{% url views.move_to_unknown notecard.pk %}" method="post">
<input type="hidden" name="next" value="{% url known_list known.section.section_name %}?page={{known.paginator.number}}"/>
<input type="submit" value="Move to unknown list"/>
</form>
{% endfor %}
You also can pass the notecard id as a post parameter.
The next parameter tells where to go after the change, here I choose the same page of the known list because once the current card is removed the next one is at this index
Capturing the pk of a specific notecard object can be done by defining a specific url for that notecard. For example:-
# urls.py
url(r'^notecard/(?P<notecard_id>\d+)/$',
'notecard',
name='notecard'),
# corresponding views.py
def notecard(request, note_card_id):
notecard = get_object_or_404(Notecard, pk=note_card_id)
template = 'notecard/notecard.html'
template_vars = {'notecard': notecard}
render(request, template, template_vars)
# notecard/notecard.html
<h2>{{ notecard.notecard_name }}</h2>
<p>{{ notecard.notecard_body }}</p>
You can also define a form with the notecard id/pk being a hidden field for submission and updating into your database (and of course, you will need to update your view function correspondingly).
In essence, to update a specific notecard object, you will simply do in your view function (with form submission or, if you prefer, a pure ajax implementation in your listing page) like this
notecard.known = False
notecard.save()

Categories

Resources