How to modify url in a django get request - django

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>

Related

Django how to pass an id of pagination and model id?

Thankyou i am getting a problem.I am using pagination in url passing a page id and want to pass my model driver id also. if a page is at 1 the url is
*url(r'^rentacar/list/$', extension_views.rentacar_list),*
but after user go to next page the url is:
*url(r'^rentacar/list/(\d+)/$', extension_views.rentacar_list),*
but i actually want to do is to reference driver id in the url that i am trying to pass.
What am trying to do is to get driver id and page# in my url. how do i go about doing this? and how can i change my views and achieve it whereas its running through pagination
Views.py
#csrf_protect
def rentacar_list(request, page_number=1):
all_cars = Car.objects.all().order_by('-id')
if menu_config.menu_item_rentacar_list_show_unavailable == 0:
all_cars = all_cars.exclude(car_available=0)
else:
all_cars = all_cars
cars_page = Paginator(all_cars, menu_config.menu_item_rentacar_list_pagination)
args['cars'] = cars_page.page(page_number)
template = Template.objects.get(template_default__exact=1)
template_page = template.template_alias + str("/rentacar/rentacar_cars_list.html")
return render(request, template_page, args)
Urls.py
url(r'^rentacar/list/$', extension_views.rentacar_list),
url(r'^rentacar/list/(\d+)/$', extension_views.rentacar_list),
but i want to achieve :
url(r'^rentacar/list/driver/id/$', extension_views.rentacar_list),
url(r'^rentacar/list/(\d+)/driver/id/$', extension_views.rentacar_list),
your driver id shouldnt in urlpath,you can transmit data in url parameters,for example:
yourhost/rentacar/list/<page_id>?driver=<driver_id>
get driver_id form request, for example:
driver_id = request.GET.get('driver', None)
last, you can refactor view by django ListView which has buildin pagination function,here is django ListView documnet django listview
example
url.py
url(r'^rentacar/list/(\d+)/$', extension_views.rentacar_list),
views.py
def you_view(request, page_number):
driver_id = request.GET.get('driver', None)
your request url
127.0.0.1/rentacar/list/1?driver=2

Django not displaying correct URL after reverse

There's lots of documentation about Django and the reverse() method. I can't seem to locate my exact problem. Suppose I have two urlconfs like this:
url(r'ParentLists/$', main_page, name = "main_page"),
url(r'ParentLists/(?P<grade_level>.+)/$', foo, name = "foo")
and the two corresponding views like this:
def main_page(request):
if request.method == 'POST':
grade_level = request.POST['grade_picked']
return HttpResponseRedirect(reverse('foo', args = (grade_level,)))
else:
return render(request, 'index.html', context = {'grade_list' : grade_list})
def foo(request, grade_level):
grade_level = request.POST['grade_picked']
parent_list = # get stuff from database
emails = # get stuff from database
return render(request, 'list.html', context = {'grade_list' : grade_list, 'parent_list' : parent_list})
Here, list.html just extends my base template index.html, which contains a drop down box with grade levels. When the user goes to /ParentLists, the main_page view renders index.html with the drop down box as it should.
When the user picks a grade level from the drop down box (say 5th Grade), the template does a form submit, and main_page once again executes - but this time the POST branch runs and the HttpResponseRedirect takes the user to /ParentLists/05. This simply results in an HTML table pertaining to grade 5 being displayed below the drop down box.
The problem is, when the user now selects say 10th Grade, the table updates to show the grade 10 content, but the URL displayed is still /ParentLists/05. I want it to be /ParentLists/10.
Clearly, after the first selection, the main_page view never executes again. Only foo does...and so the HttpResponseRedirect never gets called. How should I reorganize this to get what I'm looking for? Thanks in advance!
As you correctly mentioned you will never redirect to foo() from foo().
So the simple way to fix this is just add similar code as in main_page() view:
def foo(request, grade_level):
if request.method == 'POST':
grade_level = request.POST['grade_picked']
return HttpResponseRedirect(reverse('foo', args = (grade_level,)))
else:
parent_list = # get stuff from database
emails = # get stuff from database
return render(request, 'list.html', context = {'grade_list' : grade_list, 'parent_list' : parent_list})
Please note that I remove grade_level = request.POST['grade_picked'] because as Nagkumar Arkalgud correctly said it is excessively.
Also instead of combination of HttpResponseRedirect and reverse you can use shortcut redirect which probably little easy to code:
from django.shortcuts redirect
...
return redirect('foo', grade_level=grade_level)
I would suggest you to use kwargs instead of args.
The right way to use the view is:
your_url = reverse("<view_name>", kwargs={"<key>": "<value>"})
Ex:
return HttpResponseRedirect(reverse('foo', kwargs={"grade_level": grade_level}))
Also, you are sending "grade_level" to your view foo using the URL and not a POST value. I would remove the line:
grade_level = request.POST['grade_picked']
as you will override the grade_level sent to the method from the url.

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.

How to get /test/?grid-view or /test/?list-view' in Django view

I have a page that can be viewed by list or grid view through jQuery, but I want the URL to display what view it is -- especially when user paginate so that they don't have to reclick what they want to view -- so something like this: /test/?grid_view or /test/?list_view.
I've been trying to use request.GET.get, but it doesn't seem to be working. Here is what I have so far:
def test (request):
grid_view = request.GET.get('grid_view')
list_view = request.GET.get('list_view')
if grid_view:
return render_to_response('grid_view.html', {}, context_instance=RequestContext(request))
elif list_view:
return render_to_response('list_view.html', {}, context_instance=RequestContext(request))
else:
return render_to_response('default_view.html', {}, context_instance=RequestContext(request))
Then in my main template I'd point the different views to list view, etc. There is probably a better way to do it so any suggestions are appreciated too.
An empty string is evaluated to False and, that is what your GET request Query String parameters will contain.
You could modify your code to:
if 'grid_view' in request.GET or 'grid_view/' in request.GET:
pass
elif 'list_view' in request.GET or 'list_view/' in request.GET:
pass
Or:
if request.GET.has_key('grid_view') or request.GET.has_key('grid_view/'):
# ...
That's not a GET parameter. Use request.META['QUERY_STRING'] to get the query string.

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?