I have the normal pagination like this in my view:
paginator = Paginator(book_list, 100)
And then in my view I am passing the values to my template:
return render(request,
...
'paginator': paginator,
...
And I have a custom tag for my pagination, which I am loading like this:
{% if paginator.count > paginator.per_page %}
{% load paginator %}
{% paginator 3 %}
{% endif %}
In my custom template pagination tag, I have the following along the code:
def paginator(context, adjacent_pages=2):
page_obj = context['paginator'].page(context['object_list'].number)
...
'hits': context['paginator'].count,
...
Everything is working as expected but I am worried about context['paginator'].page(context['object_list'].number), is Django fetching the data from DB with this bit or it's using the same data that was fetched from my main view?
Please advise. Thanks.
The paginator keeps the query_set as object_list, in django 1.3.4, the page method, is
def page(self, number):
"Returns a Page object for the given 1-based page number."
number = self.validate_number(number)
bottom = (number - 1) * self.per_page
top = bottom + self.per_page
if top + self.orphans >= self.count:
top = self.count
return Page(self.object_list[bottom:top], number, self)
Only the last line related to db,
self.object_list[bottom:top]
The object_list is just a QuerySet, so the problems comes to if you invoke query_set[x:y] more times, whether there exists multiple queries.
Django's query_set is lazy, if you don't iterate through it, no sql will be triggered. Otherwise, there will be db queries.
You can use check queries in django.db.connection.queries for following code,
from django.db import connection
original = XXXX.objects.filter(...)
res1 = original[x:y]
for item in res1:
print item
print len(connection.queries), connection.queries[-1]
res2 = original[x:y]
for item in res2:
print item
print len(connection.queries), connection.queries[-1]
You'll find that the query length grows.
My understanding here is that it's simply using whatever object you passed it in your main view. context['paginator'] is going to return the object stored in the paginator variable that you passed to the context, an instance of the Paginator class.
The question of whether or not it's going back to the database is simply about the .page(...) method. If calling Paginator.page(...) issues a database query, then it will be going back to the database--it wouldn't cache that value. However, if that information is already available locally in the paginator variable and that is what is called up by the .page method, then you're not refetching the data from the database.
Related
So I have a model Listing() that has a field views. In my one of my views, when someone looks at the listing's page, the views field is incremented by one via listing.views = F('views') + 1 and listing.save(update_fields=['views']). My issue is that when I access the views attribute from that same template using {{ listing.views }}, instead of display the current amount of views, the template displays F(views) + Value(1) (literally that text). Now, I assume I could use a Model method such as def get_views() which will return self.views, but I was wondering why I am getting this weird issue. Also, is there a way without writing a model method that I can get the actual integer instead of the odd F(views) + Value(1)?
Here is my current code:
models.py
class Listing(models.Model):
...
views = models.IntegerField(default=0)
listings.py
class ListingView(View):
def get(self, request):
listing_id = request.GET['listing_id']
listing = get_object_or_404(Listing, id=listing_id)
listing.views = F('views') + 1
listing.save(update_fields=['views'])
return render(request, 'listings.html', {'listing': listing})
listings.html
<html>
{{ listing.views }}
</html>
Using F expressions like this requires you to re-fetch the item once saved in order to get updated values (due to the nature of the F expression updating at a database level and not the model instance itself; perhaps that's where the decrease in operational costs come in).
From the docs -
In order to access the new value that has been saved in this way, the object will need to be reloaded:
reporter = Reporters.objects.get(pk=reporter.pk)
# Or, more succinctly:
reporter.refresh_from_db()
I am working on my first django project and i am having problems displayin 'categories' from my database onto a webpage as a list. I am getting the error "object has no attribute 'Name'. My code so far is:
Model:
class Category(models.model):
name = models.Charfield(max_length=128)
def __unicode__(self):
return self.Name + ": " +str(self.id)
Views:
from django.shortcuts import render_to_response, redirect
from forms.models import Form, Group, Flow, Gate, Field, Event, Category
from django.core.context_processors import csrf
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
def homepage (request):
CatName = Category.objects.order_by('id')
output = {
'category_name': CatName.Name,
}
return render_to_response('forms/formsummary.html', output)
HTML:
<div>{{ category_name }}</div>
Can anybody point me in the right direction?
In Django, when you use the ORM to query for objects, there are two possibilities (excluding each case returning nothing):
Query returns just one objects: if so, you queried using the get() method of the manager.
Query returns a collection: if so, you queried by using an all(), filter() or any method like those.
In this case, your query returned a collection of Category objects, you can do a couple of things about this, you can either generate a list with only the names by using a list comprehension:
cnames = [c.name for c in Category.objects.all()]
Or you can iterate the list using a for loop and do whatever you need to do with each object.
Django already orders your data by the id field, so, I guess there is no need to specify an ordering in this case.
Later, when your view is returning, you can deliver the list to your template and iterate it to extract what you need, for example.
In your view:
def get_categories(request):
categories = Category.objects.all()
context = {'categories': categories}
return render_to_response('template.html', RequestContext(request, context))
Then, in your template:
{% for c in categories %}
<p>{{c.name}}</p>
{% endfor %}
Here's some useful documentation
Django Templates
Django Querysets
Hope this helps.
It seems like case sensitive,
def__unicode__(self):
return self.Name + ": " +str(self.id)
^
name
CatName is a collection of Category instances. The CatName object does not have a name property because it is not a Category object. It contains Category objects.
you can iterate through your collection and display each categories name:
for category in CatName:
print category.name
It is good to at least read through QuerySet documentation even if you don't fully grasp it yet.
if you want just the most recent category you could do something like:
def homepage (request):
most_recent_category = Category.objects.order_by('-id')[0]
output = {
'category_name': most_recent_category.name
}
return render_to_response('forms/formsummary.html', output)
I am using flask.
On the server server when a page loads I create a dictionary.
#app.route('/edit_creative', methods=["GET", "POST"])
#login_required
def edit_creative():
if request.method == "POST":
pass
query = """select * from mystable"""
print query
rows = execute_query(query,select=True)
creative_handler={}
for row in rows:
j = row[2].strip("'")
j = json.loads(j)
creative_handler[row[1]]=j
return render_template("edit_creatives.html",title = 'Edit Creative')
On the client side I want to iterate over the hash:
{% for crid, object in creative_handler.iteritems() %}
{{ crid }}<br>
{% endfor %}
On the page I get this error
UndefinedError: 'creative_handler' is undefined
So..how do I use jinja templates to iterate over a hash creates server side?
You need to pass creative_handler to the template:
return render_template("edit_creatives.html", title='Edit Creative', creative_handler=creative_handler)
Well you need to pass in the variable(s) you want to use, in the template.
>>> from flask import render_template
>>> help(render_template)
render_template(template_name, **context)
Renders a template from the template folder with the given
context.
:param template_name: the name of the template to be rendered
:param context: the variables that should be available in the
context of the template.
so return render_template("edit_creatives.html",title = 'Edit Creative', creative_handler = creative_handler)
Try
return render_template("edit_creatives.html",title = 'Edit Creative', creative_handler = creative_handler)
If creative_handler contains your data anyway.
You have to actually pass the object to the template so it can be seen and give it a name which you then use in the template. Also FYI the code is not executed client side, it's built inside your app then sent to the client. They just see the HTML that results from the loop etc.
I would like to display all results which match selected facets even though a search query has not been inserted. Similar to how some shop applications work e.g. Amazon
e.g. Show all products which are "blue" and between $10-$100.
Haystack does not return any values if a search query is not specified.
Any ideas how I can get around it?
Thanks!
If anyone is still looking, there's a simple solution suggested in haystack code:
https://github.com/toastdriven/django-haystack/blob/master/haystack/forms.py#L34
class SearchForm(forms.Form):
def no_query_found(self):
"""
Determines the behavior when no query was found.
By default, no results are returned (``EmptySearchQuerySet``).
Should you want to show all results, override this method in your
own ``SearchForm`` subclass and do ``return self.searchqueryset.all()``.
"""
return EmptySearchQuerySet()
Why No Results?
I imagine you're using a search template similar to the one in the haystack getting started documentation. This view doesn't display anything if there is no query:
{% if query %}
{# Display the results #}
{% else %}
{# Show some example queries to run, maybe query syntax, something else? #}
{% endif %}
The second problem is that the default search form's search() method doesn't actually search for anything unless there's a query.
Getting Results
To get around this, I'm using a custom search form. Here's an abbreviated sample:
class CustomSearchForm(SearchForm):
...
def search(self):
# First, store the SearchQuerySet received from other processing.
sqs = super(CustomSearchForm, self).search()
if not self.is_valid():
return sqs
filts = []
# Check to see if a start_date was chosen.
if self.cleaned_data['start_date']:
filts.append(SQ(created_date__gte=self.cleaned_data['start_date']))
# Check to see if an end_date was chosen.
if self.cleaned_data['end_date']:
filts.append(SQ(created_date__lte=self.cleaned_data['end_date']))
# Etc., for any other things you add
# If we started without a query, we'd have no search
# results (which is fine, normally). However, if we
# had no query but we DID have other parameters, then
# we'd like to filter starting from everything rather
# than nothing (i.e., q='' and tags='bear' should
# return everything with a tag 'bear'.)
if len(filts) > 0 and not self.cleaned_data['q']:
sqs = SearchQuerySet().order_by('-created_date')
# Apply the filters
for filt in filts:
sqs = sqs.filter(filt)
return sqs
Also, don't forget to change the view:
{% if query or page.object_list %}
{# Display the results #}
{% else %}
{# Show some example queries to run, maybe query syntax, something else? #}
{% endif %}
Actually, the view code is a little hackish. It doesn't distinguish query-less searches with no results from search with no parameters.
Cheers!
Look at SearchQuerySet.
This should be possible if color and price has been defined in your SearchIndex:
sqs = SearchQuerySet().filter(color="blue", price__range=(10,100))
You can limit the query to certain models by adding models(Model) to the SearchQuerySet. So if you want to limit your query to the model Item use:
sqs = SearchQuerySet().filter(color="blue", price__range=(10,100)).models(Item)
Following form display all the result if not query string is present. Now you can add custom filters.
from your_app.forms import NonEmptySearchForm
url(r'^your_url$',
SearchView(template='search.html',searchqueryset=sqs,form_class=NonEmptySearchForm), name='haystack_search'),
forms.py
#Overridding because the default sqs is always none if no query string is present
class NonEmptySearchForm(SearchForm):
def search(self):
if not self.is_valid():
return self.no_query_found()
sqs = self.searchqueryset.auto_query(self.cleaned_data['q'])
if self.load_all:
sqs = sqs.load_all()
return sqs
Stumpy Joe Pete's answer is pretty spot on, but as he mentioned, the template if query or page.object_list check is a little hacked. A better way of solving this would be to create your own SearchForm which would still find something if q is empty - will not repost that - AND to customize the SearchView with something like:
class MySearchView(SearchView):
def get_query(self):
query = []
if self.form.is_valid():
for field in self.form:
if field.name in self.form.cleaned_data and self.form.cleaned_data[field.name]:
query.append(field.name+'='+str(self.form.cleaned_data[field.name]))
return ' AND '.join(query)
In most cases, you won't even be using the query value, so you could just as well do a quick check if any of the fields is set and return True or something like that.. or of course you can modify the output any way you want (I'm not even 100% sure my solution would work for all field types, but you get the idea).
I'm using Django Forms to do a filtered/faceted search via POST, and I would like to Django's paginator class to organize the results. How do I preserve the original request when passing the client between the various pages? In other words, it seems that I lose the POST data as soon as I pass the GET request for another page back to my views. I've seen some recommendations to use AJAX to refresh only the results block of the page, but I'm wondering if there is a Django-native mechanism for doing this.
Thanks.
If you want to access the store data in later request, you would have to store it somewhere. Django provides several ways to archive this:
1) You can use sessions to store the query: Every visitor who visits your site will get an empty session object and you can store whatever you want inside this object, which acts like a dict. Drawback: A single visitor can't do multiple searches with pagination concurrently.
2) Use cookies: If you set a cookie which is stored on the client side, the browser will append the data of the cookie to each request where you can access it. Cookies are more server friendly, because you don't need a session manager for them on the server, but the data stored in cookies is visible (and editable) to the client. Drawback: same as before.
3) Use hidden fields: You can add a form with some hidden fields on your search-result page and store the query inside them. Then, the client will resend the query whenever you submit the form. Drawback: You must use a form with submit buttons for the pagination on your page (simple links wont work).
4) Create Links which contain the query: Instead of using POST, you can also use GET. For example, you could have a link like "/search/hello+world/?order=votes" and "paginated links" like "/search/hello+world/2/?order-votes". Then the query can be easily retrieved from the URL. Drawback: The maximum amount of data you can send via GET is limited (But that shouldn't be a problem for a simple search).
5) Use a combination: You might want to store all the data in a session or a database and access them via a generated key which you can put in the URL. URLs might then look like "/search/029af239ccd23/2" (for the 2nd page) and you can use the key to access a huge amount of data which you have stored before. This eliminates the drawback of solution 1 as well as that of solution 4. New drawback: much work :)
6) Use AJAX: With ajax you can store the data inside some js-variables on the client side, which can then passed to the other requests. And since ajax will only update your result list, the variables aren't getting lost.
Reading the very nice answer from tux21b I decided to implement the first option, i.e., to use the session to store the query. This is an application that searches real estate databases. Here is the view code (using django 1.5):
def main_search(request):
search_form = UserSearchForm()
return render(request, 'search/busca_inicial.html', {'search_form': search_form})
def result(request):
if request.method == 'POST':
search_form = UserSearchForm(request.POST)
if search_form.is_valid():
# Loads the values entered by the user on the form. The first and the second
# are MultiChoiceFields. The third and fourth are Integer fields
location_query_list = search_form.cleaned_data['location']
realty_type_query_list = search_form.cleaned_data['realty_type']
price_min = search_form.cleaned_data['price_min']
price_max = search_form.cleaned_data['price_max']
# Those ifs here populate the fields with convenient values if the user
# left them blank. Basically the idea is to populate them with values
# that correspond to the broadest search possible.
if location_query_list == []:
location_query_list = [l for l in range(483)]
if realty_type_query_list == []:
realty_type_query_list = [r for r in range(20)]
if price_min == None:
price_min = 0
if price_max == None:
price_max = 100000000
# Saving the search parameters on the session
request.session['location_query_list'] = location_query_list
request.session['price_min'] = price_min
request.session['price_max'] = price_max
request.session['realty_type_query_lyst'] = realty_type_query_list
# making a query outside the if method == POST. This logic makes the pagination possible.
# If the user has made a new search, the session values would be updated. If not,
# the session values will be from the former search. Of course, that is what we want because
# we want the 'next' and 'previous' pages correspond to the original search
realty_list_result = FctRealtyOffer.objects.filter(location__in=request.session['location_query_list']
).filter(price__range=(request.session['price_min'], request.session['price_max'])
).filter(realty_type__in=request.session['realty_type_query_lyst'])
# Here we pass the list to a table created using django-tables2 that handles sorting
# and pagination for us
table = FctRealtyOfferTable(realty_list_result)
# django-tables2 pagination configuration
RequestConfig(request, paginate={'per_page': 10}).configure(table)
return render(request, 'search/search_result.html', {'realty_list_size': len(realty_list_result),
'table': table})
Hope it helps!If anyone has any improvement to suggest, be welcome.
As #rvnovaes, a way to use session to solve the matter.
The drawback of his solution is that if there are many search fields you have to write many lines of code, and also if you show the search form in the result page, all the fields will be blank, while they should keep their values.
So I'd rather save all the post data in session, and at the beginning of the view force the value of request.POST and request.method if a session is defined:
""" ... """
if not request.method == 'POST':
if 'search-persons-post' in request.session:
request.POST = request.session['search-persons-post']
request.method = 'POST'
if request.method == 'POST':
form = PersonForm(request.POST)
request.session['search-persons-post'] = request.POST
if form.is_valid():
id = form.cleaned_data['id']
""" ... """
More info here
I did this in my web application with get parameters Maybe i can help you :
Views.py
class HomeView(ListView):
model = Hotel
template_name = 'index.html'
paginate_by = 10 # if pagination is desired
def get_queryset(self):
qs = super().get_queryset()
kwargs = {}
if 'title' in self.request.GET:
title = self.request.GET.get('title')
if title != '':
kwargs['title__icontains'] = title
if 'category' in self.request.GET:
category = self.request.GET.get('category')
if category:
kwargs['category_id'] = category
if 'size' in self.request.GET:
size = self.request.GET.get('size')
if size:
kwargs['size_id'] = size
if 'service' in self.request.GET:
service = self.request.GET.get('service')
if service:
kwargs['service_id'] = service
if 'ownership' in self.request.GET:
ownership = self.request.GET.get('ownership')
if ownership:
kwargs['ownership_id'] = ownership
qs = qs.filter(**kwargs)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form_init = {}
form = SearchForm()
if self.request.GET.items():
try:
parameters = self.request.GET.items()
except KeyError:
parameters = {}
for key, value in parameters:
for field in form.fields:
if key == field:
form_init[key] = value
form.initial = form_init
if 'title' in self.request.GET:
title = self.request.GET.get('title')
if title != '':
context.update({
'title': title
})
if 'category' in self.request.GET:
category = self.request.GET.get('category')
context.update({
'category': category
})
if 'size' in self.request.GET:
size = self.request.GET.get('size')
context.update({
'size': size
})
if 'service' in self.request.GET:
service = self.request.GET.get('service')
context.update({
'service': service
})
if 'ownership' in self.request.GET:
ownership = self.request.GET.get('ownership')
context.update({
'ownership': ownership
})
context.update({
'search_form': form
})
return context
Pagination file html
<div class="row">
{% if is_paginated %}
<nav aria-label="...">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="?category={{category}}&size={{size}}&service={{service}}&ownership={{ownership}}&page={{ page_obj.previous_page_number }}">Previous</a></li>
{% else %}
<li class="page-item disabled"><span class="page-link">Previous</span></li>
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="?category={{category}}&size={{size}}&service={{service}}&ownership={{ownership}}&page={{ page_obj.next_page_number }}">Next</a></li>
{% else %}
<li class="page-item disabled"><span class="page-link">Next</span></li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
You can ask request object if it's ajax, simply request.is_ajax. This way you can detect, whether it's first post request or further questions about the next pages.
Have the search form and the results display on one single django template. Initially, use css to hide the results display area. On POSTing the form, you could check to see if the search returned any results and hide the search form with css if results exist. If results do not exist, use css to hide the results display area like before. In your pagination links, use javascript to submit the form, this could be as simple as document.forms[0].submit(); return false;
You will need to handle how to pass the page number to django's paging engine.
My suggestion would be to store the post request using a session or a cookie. In case the post data is sensitive, you should use session to store it. The code below contains my logic to implement it using session.
def index(request):
is_cookie_set = 0
# Check if the session has already been created. If created, get their values and store it.
if 'age' in request.session and 'sex' in request.session:
age = request.session['age']
sex = request.session['sex']
is_cookie_set = 1
else:
# Store the data in the session object which can be used later
request.session['age'] = age
request.session['sex'] = sex
if(request.method == 'POST'):
if(is_cookie_set == 0): # form submission by the user
form = EmployeeForm(request.POST)
sex = form.cleaned_data['sex']
age = form.cleaned_data['age']
if form.is_valid():
result = Employee.objects.all(sex=sex,age_gte=age) # filter all employees based on sex and age
else: # When the session has been created
result = Employee.objects.all(sex=sex,age_gte=age)
paginator = Paginator(result, 20) # Show 20 results per page
page = request.GET.get('page')
r = paginator.get_page(page)
response = render(request, 'app/result.html',{'result':result})
return response
else:
form = EmployeeForm()
return render(request,'app/home.html',{'form':form})
You should also check if the post fields are empty or not and change the logic according to it. You can also store the whole post request in the session as suggested by #abidibo.
You can also use cookies for the same. I have explained it here
The Below code is working, the first request is a GET request, which accesses the form, will go directly to the else block.
Once the user puts up a search query, results will be shown, which will be a post request, and 2nd if block will be activated, this request we will store in a session.
When the user accesses 2nd search page, it will be a GET request, but we are checking whether there is an active pagination session, and also checking whether it's not a page request of GET. At this point 1st if block will be trigerred.
def search(request):
if not request.method == "POST" and 'page' in request.GET:
if 'search-query' in request.session:
request.POST = request.session['search-query']
request.method = 'POST'
if request.method == 'POST':
form = Search_form(request.POST)
request.session['search-query'] = request.POST
if form.is_valid():
search_query = form.cleaned_data.get('search_query')
search_parameter = form.cleaned_data.get('search_parameter')
print(search_query, search_parameter)
queryset_list = CompanyRecords.objects.filter(**{f'{search_parameter}__icontains': search_query}).exclude(
company_name__isnull=True).exclude(description__isnull=True).exclude(phones__isnull=True).exclude(
emails__isnull=True)[:5]
page = request.GET.get('page', 1)
paginator = Paginator(queryset_list, 2)
try:
queryset = paginator.page(page)
except PageNotAnInteger:
queryset = paginator.page(1)
except EmptyPage:
queryset = paginator.page(paginator.num_pages)
return render(request, 'search/search_results.html', {'queryset': queryset})
else:
context = {
'form': Search_form()
}
return render(request, 'search/search.html', context)