Django 1.4 - Keeping track of multiple queries and pagination - django

I have a site that basically displays a huge table of people that I want to be able to filter, sort and page through. I want to do this in multiple views that have different preexisting constraints on which people are displayed in the table. My problem is that, if I filter by state for example, sort by name, and then try to go to the next page, it resets. For example, if I filter once I get my/url/2013/?sort=n&state=MN&page=1; then if I go to page 2, I just get my/url/2013/?page=2. It doesn't remember what I've already asked it.
def all(request, year=default_year):
#sorting (sort_options was defined prior to this view)
if 'sort' in request.GET:
sort = request.GET.get('sort')
order = sort_options[sort]
else:
order = '-score'
players = Player.objects.filter(year=year).order_by(order)
url_query = ''
#filtering
if 'position' in request.GET:
filterData = _filter(request,players)
players = filterData['players']
url_query += filterData['url_query']
# pagination
paginator = Paginator(players,25)
page = request.GET.get('page')
try:
players = paginator.page(page)
except PageNotAnInteger:
players = paginator.page(1)
except EmptyPage:
players = paginator.page(paginator.num_pages)
data = {'players':players,'url_query':url_query}
return render_to_response('all.html', data, context_instance = RequestContext(request))
In my template I reference url_query like so:
<th class="name">Name</th>
and like so:
Next
This is pretty obviously wrong to me but I don't know how to do it right.

When you render your template you can pass to the context (in your case data) object sort parameter and then in the template you can use url to show which view to invoke.

Related

How to modify url in a django get request

I have a web page which displays some data (with get parameters to filter in the view) and I use a pagination.
To go through the data i use a link to change the page in the paginator :
<li class="paginator-data">Next</li>
Here is the view :
#login_required
def search_and_export(request):
# initialisation
page_obj = Tree.objects.all()
# FORM
if request.method == 'GET':
## Here I get GET parameters or a default value like
data = str(request.GET.get('data', 1))
page = str(request.GET.get('page', 1))
## creating a queryDict
querydict = QueryDict('data=' data + '&page=' + page)
big_request_form = BigRequestForm(querydict)
if big_request_form.is_valid():
# Getting data from front endda
data = big_request_form.cleaned_data['data']
# Extracting QuerySet
page_obj = filtering_function(data)
paginator = Paginator(page_obj,25)
page_obj = paginator.get_page(page)
page_obj.adjusted_elided_pages = paginator.get_elided_page_range(number = page, on_each_side=1, on_ends=1)
else:
big_request_form = BigRequestForm()
# return values
context = {
'big_request_form' : big_request_form,
'page_obj' : page_obj,
}
return render(request, 'filtered_stats.html', context)
Here is the problem : url looks like that after browsing a few pages :
http://127.0.0.1:8000/?data=x&page=2&page=3&page=4&page=5&page=6
The view is working well (It displays me the last page) but I got this ugly link.
I tried something like that :
request.GET.get = querydict
(with querydict a QueryDict object with only one page data)
But It didn't work. I also thought of parsing data in the template but maybe there is a better way.
What you want to do in HTML template is to get current URL. But actually you don't have to do it - relative path can be used instead. So just remove it:
<li class="paginator-data">Next</li>
The browser knows current URL and will use href attribute relative to it.
UPD
alternative - to keep other parameters in the URL is to construct "next URL" inside the view and pass it in context as next_url.
I.e. something like so:
In views:
next_url = request.get_full_path()
next_url.replace(f'page={current}', f'page={page_obj.next_page_number}')
context['next_url'] = next_url
In template:
<li class="paginator-data">Next</li>

How to show users own post along with post from users he follows in Django

as part of learning django, I am creating a blog website and also a user follow model. Here in the homepage I am able to see only the posts from the people I follow and not the users own posts. I tried using chained filter/ multiple filter but it seems it doesn't work in this case. Please have a look at this view code and let me know what changes I should make here.
#login_required
def myhome(request, tag_slug=None):
current_user = request.user
following_ids = request.user.following.values_list('id',flat=True)
actions = Action.objects.filter(user_id__in=following_ids) #Action notification
posts_list = Post.objects.filter(user_id__in=following_ids).filter(user=current_user).\
order_by('-post_date') #See only followed people post
tag = None
if tag_slug:
tag = get_object_or_404(Tag, slug=tag_slug)
posts_list = posts_list.filter(tags__in=[tag])
paginator = Paginator(posts_list, 5)
page = request.GET.get('page')
try:
posts = paginator.page(page)
except PageNotAnInteger:
posts = paginator.page(1)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
if following_ids:
actions = actions.filter(user_id__in=following_ids)
actions = actions.select_related('user', 'user__profile').prefetch_related('target')[:10]
context = {
'page':page,
'posts':posts,
'tag':tag,
'actions':actions,
}
return render(request, 'posts/users/myhome.html', context)
Thanks in advance!
If I understand you correctly you are trying to get all the posts of the user plus the posts of the users that the user follows. If that is the case I don't think you will be able to do it by chaining filters, because you want an "OR" condition.
So, you can achieve this in several ways, one simple way is to use | operator, like this,
posts_list = Post.objects.filter(user_id__in=following_ids) | Post.objects.filter(user=current_user)
But I think Django construction for this and other complex cases are Q objects. Something like this should work,
from django.db.models import Q
posts_list = Post.objects.filter(Q(user_id__in=following_ids) | Q(user=current_user))
Here you have a nice discussion of this subject (How do I do an OR filter in a Django query?)[How do I do an OR filter in a Django query?.

Django pagination while objects are being added

I've got a website that shows photos that are always being added and people are seeing duplicates between pages on the home page (last added photos)
I'm not entirely sure how to approach this problem but this is basically whats happening:
Home page displays latest 20 photos [0:20]
User scrolls (meanwhile photos are being added to the db
User loads next page (through ajax)
Page displays photos [20:40]
User sees duplicate photos because the photos added to the top of the list pushed them down into the next page
What is the best way to solve this problem? I think I need to somehow cache the queryset on the users session maybe? I don't know much about caches really so a step-by-step explanation would be invaluable
here is the function that gets a new page of images:
def get_images_paginated(query, origins, page_num):
args = None
queryset = Image.objects.all().exclude(hidden=True).exclude(tags__isnull=True)
per_page = 20
page_num = int(page_num)
if origins:
origins = [Q(origin=origin) for origin in origins]
args = reduce(operator.or_, origins)
queryset = queryset.filter(args)
if query:
images = watson.filter(queryset, query)
else:
images = watson.filter(queryset, query).order_by('-id')
amount = images.count()
images = images.prefetch_related('tags')[(per_page*page_num)-per_page:per_page*page_num]
return images, amount
the view that uses the function:
def get_images_ajax(request):
if not request.is_ajax():
return render(request, 'home.html')
query = request.POST.get('query')
origins = request.POST.getlist('origin')
page_num = request.POST.get('page')
images, amount = get_images_paginated(query, origins, page_num)
pages = int(math.ceil(amount / 20))
if int(page_num) >= pages:
last_page = True;
else:
last_page = False;
context = {
'images':images,
'last_page':last_page,
}
return render(request, '_images.html', context)
One approach you could take is to send the oldest ID that the client currently has (i.e., the ID of the last item in the list currently) in the AJAX request, and then make sure you only query older IDs.
So get_images_paginated is modified as follows:
def get_images_paginated(query, origins, page_num, last_id=None):
args = None
queryset = Image.objects.all().exclude(hidden=True).exclude(tags__isnull=True)
if last_id is not None:
queryset = queryset.filter(id__lt=last_id)
...
You would need to send the last ID in your AJAX request, and pass this from your view function to get_images_paginated:
def get_images_ajax(request):
if not request.is_ajax():
return render(request, 'home.html')
query = request.POST.get('query')
origins = request.POST.getlist('origin')
page_num = request.POST.get('page')
# Get last ID. Note you probably need to do some type casting here.
last_id = request.POST.get('last_id', None)
images, amount = get_images_paginated(query, origins, page_num, last_id)
...
As #doniyor says you should use Django's built in pagination in conjunction with this logic.

Sorting through request.GET in Django

I want people to be able to sort things and not just me sorting things manually. So, for example, I will have a link to sorting, and the link will be something like, /?sort=issues and this would show a list of issues in alphabetical order, etc. Or /?sort=cover and it will show a list of issues with covers only.
Views.py
def issues(request):
issues_list = Issue.objects.order_by('-date_added')
paginator = Paginator(issues_list, 24)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
try:
issues = paginator.page(page)
except (EmptyPage, InvalidPage):
issues = paginator.page(paginator.num_pages)
return render_to_response('comics/issues.html', {'issues': issues}, context_instance=RequestContext(request))
So, I'd want anyone to have the option of ordering by something like -date_added, date_addded, pub_date, importance, etc.
I'd imagine I'd have to fix my views and do some request.GET magic, but I am pretty new to Django and don't really know how to go about doing this. I checked Django docs, too.
sort_by = request.GET.get('sort', '-date_added')
if sort_by not in ['-date_added', 'date_addded', 'pub_date', 'importance']:
sort_by = '-date_added'
issues_list = Issue.objects.order_by(sort_by)
Put it at the top of your view:
user_specified = request.GET.get('sort', **''**)
if user_specified:
issues_list = Issue.objects.order_by(user_specified)
else: # default ordering
issues_list = Issue.objects.order_by('-date_added')
NB: Haven't tested this!

Want to print out a list of items from another view django

I have a view which displays a list items.
def edit_order(request, order_no):
try:
status_list = models.Status.objects.all()
order = models.Order.objects.get(pk = order_no)
if order.is_storage:
items = models.StorageItem.objects.filter(orderstoragelist__order__pk = order.pk)
else:
items = models.StorageItem.objects.filter(orderservicelist__order__pk = order.pk)
except:
return HttpResponseNotFound()
I want to put these list of item in another view. Unfortunately this is proving to be trickier then I thought.
#login_required
def client_items(request, client_id = 0):
client = None
items = None
try:
client = models.Client.objects.get(pk = client_id)
items = client.storageitem_set.all()
item_list = models.StorageItem.objects.filter(orderstoragelist__order__pk = order.pk)
except:
return HttpResponse(reverse(return_clients))
return render_to_response('items.html', {'items':items, 'client':client, 'item_list':item_list}, context_instance = RequestContext(request))
I thought maybe I can just paste the definition of items and just call that item_list but that does not work. Any ideas
items.html
{% for item in item_list %}
{{item.tiptop_id}
{% endfor %}
From your comment:
I get a white screen with the url printed on the screen. /tiptop/client in this case.
Because that's what you've asked for:
except:
return HttpResponse(reverse(return_clients))
This means that if there are any bugs or problems in the above, your view will simply output a response containing just that URL. Maybe you meant to use HttpResponseRedirect, so the browser actually redirects to the URL - but still you should not use a blank except, as it prevents you from seeing what is actually going wrong.
To answer the main question, think about what your edit_order view returns: it gives you a complete HTML response with a rendered template. How could you use that as an element in a query in another view? You need to think logically about this.
One possible solution would be to define a separate function which just returns the data you want - as a plain queryset - and both views can call it. Does that do what you want?