I'm trying to make a simple search and return results in a paginated form. Whenever I try to go to the second page, my search term is lost and thus my second page has no results.
I've found and followed the Pagination example in the Djangoproject tutorial, but I haven't found an example on how to write my URL for the search view.
I've used POST method in my form previously, for when I had little data to display (although now, after a bit of research, I know the usage difference between GET and POST). When I gained lots more data, I was constrained to use Pagination. Thus, I've changed my form method to GET but here is my problem, I don't know how to describe my URL so the data is sent to the right view.
I've tried to make it work with POST but with no success. Finally I decided to stick to GET example but stumbled on this URL thing that's keeping me back.
Here is the code in the template and the URLs file:
search.py:
<form method="GET" id="searchForm" action="/search/?page=1">
{% csrf_token %}
<input type="text" id="billSearched" name="q_word">
<input type="submit" value="{% trans "Look for" %}">
</form>
urls.py:
urlpatterns = patterns('',
url(r'^$','ps.views.bills',name="bills"),
url(r'^i18n/', include('django.conf.urls.i18n')),
url(r'^search/$','ps.views.search',name="search"),)
I've tried many forms for the URL but none have succeeded.
i.e.:
url(r'^search/(?P<page>\d+)/$','ps.views.search',name="search")
url(r'^search/','ps.views.search',name="search")
url(r'^search/(?P<page>\d+)/(?P<searchTerm>\w*)','ps.views.search',name="search")
Any explanation / solution would be really appreciated. Thank you in advance!
UPDATE:
def search(request):
searchTerm = ""
page = 1
import pdb
pdb.set_trace()
if 'q_word' in request:
searchTerm = request.GET['q_word']
if 'page' in request:
page = request.GET['page']
found_bills = Bill.objects.filter(name__icontains = searchTerm)
paginator = Paginator(found_bills,25)
try:
current_page = paginator.page(page)
except PageNotAnInteger:
current_page = paginator.page(1)
except (EmptyPage, InvalidPage):
current_page = paginator.page(paginator.num_pages)
bills_list = list(current_page.object_list)
return render_to_response('results.html',{"bills_list":bills_list,"current_page":current_page,},context_instance=RequestContext(request))
UPDATE #2:
If I use pdb I can see that there is no data being passed from the client to the server. Got to work on that, but still, any information and/or hints would be really appreciated as they can shorten my search time :)
(Pdb) request.GET
<QueryDict: {u'page': [u'1']}>
If your form's method is GET, you cannot append a query string to the action, because the browser will overwrite it. You can only do that if your form method is POST.
Change your form to this:
<form method="GET" id="searchForm" action="/search/">
<input type="text" id="billSearched" name="q_word">
<input type="submit" value="{% trans "Look for" %}">
</form>
In your view:
from django.shortcuts import render
def search(request):
if 'q_word' in request:
searchTerm = request.GET['q_word']
found_bills = Bill.objects.filter(name__icontains = searchTerm)
page = request.GET.get('page')
paginator = Paginator(found_bills,25)
try:
current_page = paginator.page(page)
except PageNotAnInteger:
current_page = paginator.page(1)
except (EmptyPage, InvalidPage):
current_page = paginator.page(paginator.num_pages)
# bills_list = list(current_page.object_list) - not needed
return render(request,
'results.html',{"results":current_page,"term": searchTerm})
In results.html:
{% for bill in results %}
# .. display bill stuff
{% endfor %}
{% if results.has_previous %}
previous
{% endif %}
{% if results.has_next %}
next
{% endif %}
What do you mean by 'describe' your url? Your urls.py look fine. Did you drop a debugger into your ps.views.search() function to verify that it is hitting? Did you look at your debug server logs to make sure the right url is being requested from the browser?
you can either have r'^search/$' and access the page param as request.GET['page'] or you can pass in the param to your view function like url(r'^search/(?P<page>\d+)/$ which would mean search would take 2 params request and then page. If passing in the page param you need to change your form URL to be
<form method="GET" id="searchForm" action="/search/1">
Instead of having page be a GET param
I don't see anything wrong with your syntax for any of the urls you've listed.
https://docs.djangoproject.com/en/1.3/topics/pagination/#using-paginator-in-a-view
If you are using url(r'^search/(?P<page>\d+)/$' make sure that search takes a variable named page as a second argument. It is important to learn how to use the debugger too.
import pdb; pdb.set_trace() (or even better ipdb!), Drop that in your view to see if its hitting, If its not hitting check dev server to see what url is actually being requested.
Related
Overview / Problem:
Hi! The wrong template and view are loading every time I click the check_tier_level link in my template.
When that parameter is in, it loads the home view with "check_tier_level" as the special_message, even though my links go to the view for check_tier_level. If I click any of the form buttons to grant access, the proper message shows up in that spot. I just can't check the level.
The app works fine and renders the right template / view only when I remove the special_message parameter from the urlpattern and view.
The only other lead I have on this is that the url in the browser will also look like http://127.0.0.1:8000/tiered_access_app/Tier 1 granted!/, instead of having the characters escaped with %20 and so on.
My goal
The whole reason I want to keep that parameter in is so a special_message can notify users of the latest update based on their actions. If anyone knows a better way to do this without making a whole new view / template (which I know is a solution, and how to do it), I'd like to know how. Anyways, here's my code:
urlpatterns.py
path('', views.home, name='home'),
path('<str:special_message>/', views.home, name='home_special_message'),
path('check_tier_level/', views.check_tier_level, name='check_tier_level'),
path('check_tier_level/gain_access/', views.gain_access, name='gain_access'),
views.py
def home(request, special_message=None):
return render(request, 'tiered_access_app/home.html', {'special_message': special_message})
def check_tier_level(request):
current_user = request.user
try:
find_user = TieredAppCustomUser.objects.get(user=current_user)
if find_user.tier_choice == 'tier1':
return render(request, 'tiered_access_app/check_tier_level.html', {'level_1': 'You have access to level 1.'})
# and so on with other levels...
except ObjectDoesNotExist:
pass
return render(request, 'tiered_access_app/check_tier_level.html', {'no_access': 'You don\'t have access to the content here yet.'})
home.html
{% if special_message %}
<h2>{{ special_message }}</h2>
{% endif %}
<form action="{% url 'tiered_access_app:gain_access' %}" method="POST">
{% csrf_token %}
<label>Check level 1 access</label>
<!-- *******PROBLEM WITH LINK HERE******** -->
<p>Try to access level 1 first. You won't be allowed unless you gain access first, by clicking the button below.</p>
<!-- *******PROBLEM WITH LINK HERE******** -->
<input type="hidden" value='1' name="tier_level">
<input type="submit" value="Enable level 1">
</form>
I FIGURED IT OUT:
All I had to do was change my url patterns into the following order:
path('', views.home, name='home'),
path('check_tier_level/', views.check_tier_level, name='check_tier_level'),
path('check_tier_level/gain_access/', views.gain_access, name='gain_access'),
path('<str:special_message>/', views.home, name='home_special_message'),
The only difference here and what I have below, is the position of the 2nd function that goes to home. I'm going to leave this question up in case someone else comes across this same problem. I don't know why this made it work, but it did. Now everything works perfectly.
I've been trying to figure this out for days and was unable to find any useful information online.
What I am trying to do is paginate objects from my model after filtering them using a drop down menu and supplying the data to python via AJAX. I know where the problem is but I am not sure how to solve it. I have two templates, first one is:
entry_index.html:
{% extends 'main/base.html' %}
<form action="" method="get" accept-charset="utf-8">
<select class="selectpicker" name="times" onchange="FilterCategories()" id="times">
<option value="1">last 24 hours</option>
<option value="30">past month</option>
<option value="365">past year</option>
<option value="10000">all time</option>
</select>
</form>
<ul id="all-games" class="list-unstyled">
{% include page_template %}
</ul>
The template that is being included in the above template is entry_index_page.html:
{% if objects %}
{% for object in objects %}
do something
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if objects.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ objects.number }} of {{ objects.paginator.num_pages }}.
</span>
{% if objects.has_next %}
next
{% endif %}
</span>
</div>
urls.py:
url(r'^$', views.entry_index, name='index')
views.py:
def entry_index(
request,
template='entry_index.html',
page_template='entry_index_page.html'):
date_from = timezone.now() - timezone.timedelta(days=1)
obj_list=Object.objects.filter(submitted__gte = date_from).order_by('-votes')
message=[]
context = {
'objects': obj_list,
'page_template': page_template}
if request.is_ajax():
template = page_template
message = []
if request.method == "GET":
time_range = request.GET.get('time_range')
if time_range is not None and time_range != u"":
time_range = request.GET['time_range']
date_from = timezone.now() - timezone.timedelta(days=int(time_range))
obj_list= Object.objects.filter.filter(submitted__gte=date_from)
paginator = Paginator(obj_list, 2)
page = request.GET.get('page')
try:
objects= paginator.page(page)
except PageNotAnInteger:
objects= paginator.page(1)
except EmptyPage:
objects= paginator.page(paginator.num_pages)
context.update({"message":message,"objects":objects})
return render_to_response(
template, context, context_instance=RequestContext(request))
ajax.js:
function FilterCategories() {
var timePosted = document.getElementById('times');
$.ajax({
type: "GET",
url: "",
data: {
'time_range': timePosted.value,
'csrfmiddlewaretoken': $("input[csrfmiddlewaretoken]").val()
},
success: filterResults,
dataType: 'html'
});
}
Now to explain what I think is going on and hopefully someone can help me find a solution.
When the home page is loaded (entry_index.html) the model gets filtered based on the first option in the drop down menu (i.e. value="1", which is filtering for the data entries submitted within the last day). The obj_list variable gets populated and is passed to the paginator and everything works as expected. I get a certain number of pages and can navigate through pages. Now lets assume we are on the home page again and I select "all time" from the drop down menu. This will trigger the onchange callback and it will call the FilterCategories() function. Note the url in AJAX is "" (an empty string, so pointing to my index page). According to urls.py, it will call my entry_index() view. Because request is ajax, the template used will change (page_template becomes the new template, page_template = entry_index_page.html). Because the new time range specified with the drop down menu and passed on with ajax, I get a new obj_list which is then paginated and produces "objects" which are then passed as context onto the template. Up until this point everything works as expected. I get right amount of pages etc. However, the problem starts when I try to go to the next page with the newly selected filter. When I click the next page button, the request that is being made is not an ajax request so everything that is in the request.is_ajax() conditional is not executed. So in another words a click to the next page is calling my entry_index view again and the template being used this time around is entry_index.html and my filter is reset back to the default, which is the "last 24 hours" filter. Therefore, when I click the next page what I end up getting is actually the default home page again instead of getting the next page of objects with my newly selected drop down filter.
My question is, is there an easy way to fix this so that I can scroll through the pages of my filtered model? Or should I completely abandon this approach and there is an easier way to do this? I apologize for a long post and I hope someone out there will be able to help me out. Thank you for taking your time to read this.
Here's an example of an approach I've taken to having dynamic content displayed on a page using Django and Ajax:
I was tooling a little browser Sci-fi game just to practice this specific technique. Everything took place in a single view:
class GameViewport(TemplateView):
template_name = "game_viewport.html"
#cached_property
def slug(self):
return self.kwargs['slug']
#cached_property
def game(self):
return Game.objects.get(url=self.kwargs['slug'])
#cached_property
def player(self):
return Player.objects.get(game=self.game)
#cached_property
def current_planet(self):
return self.player.current_planet
#cached_property
def left_column(self):
player = self.player
if player.current_location:
node = player.current_node
if len(Location.objects.filter(node=node)) == 0:
spawn_locations(node)
locations = Location.objects.filter(node=node)
else:
locations = Location.objects.filter(planet=node)
html = "Other Sites in ".format(str(node))
for location in locations:
html += '<li>{} ({})</li>'.format(location.name, location.type.name)
return html
elif player.current_node:
planet = player.current_planet
if len(Node.objects.filter(planet=planet)) == 0:
spawn_nodes(planet, get_name_choices())
nodes = Node.objects.filter(planet=planet)
else:
nodes = Node.objects.filter(planet=planet)
html = '<h4><b>Other Starports on {}:</b></h4>'.format(planet.name)
for node in nodes:
html += '<li> {} ({})</li>'.format(node.name, node.type.name)
return html
elif player.current_planet:
system = player.current_system
html = '<h4><b>Known Planets in {}:</b></h4>'.format(system.name)
for known_planet in player.known_planets.filter(solar_system=system):
html += '<li> {} ({})</li>'.format(
known_planet.name,
known_planet.classification.name
)
return html
else:
html = '<h4><bShip Status</b></h4>'
html += '<p><b>Fuel:</b> 100%</p>'
return html
So as you can see, the left column would generate different html data based on what the player's current settings are. This would be plugged into the template like so:
<div class="col-md-3">
<div class="leftColumn">
{% autoescape off %}
{{ view.left_column }}
{% endautoescape %}
</div>
</div>
If the user clicked on a new location, I would send her decision through AJAX:
$(".planetChoice").click(function(){
event.preventDefault();
var submission_data = {planet: $(this).text()};
console.log(submission_data);
$.ajax({
url: $('#visitPlanet').attr('href'),
type: 'GET',
dataType: "json",
data: submission_data,
success: function(html_data) {
window.location.reload();
},
failure: function(data) {
alert('Something went wrong. Please refresh the page.');
}
});
});
All this would do is update the player's state and reload the page according to her new settings:
def visit_planet(request, slug):
player = Game.objects.get(url=slug).player_1
planet = Planet.objects.get(name=request.GET.get('planet', "").strip())
if planet:
player.current_location = None
player.current_node = None
player.current_planet = planet
player.save()
response = {'status': 1, 'message': "Ok"}
return JsonResponse(response)
Thus displaying the new data, as determined by the left_column property.
I went about this by changing the state in the database, but it could just as easily be accomplished with session variables. I found it to be a relatively clean and DRY way of cycling dynamic content. It also has the advantage of giving Django an opportunity to generate or modify data in between clicks.
Not sure if this applies to your situation, but hopefully it sparks an idea!
EDIT: You don't even necessarily need to output HTML. Here's an approach I am using in a different application:
<!--Product Tile #1-->
{% if view.tile_data.0 %}
<div class="col-md-4">
<div class="card hoverable">
<!--Card content-->
<div class="card-block" id="tile_{{ view.tile_data.0.invoice }}_id">
<!--Title-->
<h4 class="card-title">Shipment {{ view.tile_data.0.invoice }}</h4>
<!--Text-->
<p class="card-text">{{ view.tile_data.0.supplier.name }}
<br>
<b>{{ view.tile_data.0.lbs|floatformat }} Lbs # {{ view.tile_data.0.price }} USD</b>
<br>
{{ view.tile_data.0.variety.commodity }} {{ view.tile_data.0.variety }} {{ view.tile_data.0.inshell|shell_display }}</p>
</div>
<!--/.Card content-->
</div>
</div>
{% endif %}
<!--./Product Tile #1-->
This data is directly fed through the view from a model manager:
def tile_data(self, status, first, last):
return self.model.objects.filter(status=status)[first:last]
I'm not looking for exact code, but rather some guidance on my problem. On one of my pages, I want to have a small section that will tell the user the disk space left on the server and I want this information to update every 30 seconds. I have the code already written to get the disk information. However, I am unsure on how to display that information and have it update. This is what I have, just to give you a visual:
The HTML I'm working with:
{% extends "Checklist/base.html" %}
{% block main_content %}
<form action="{% url 'Checklist:run' %}" method="post"> {% csrf_token %}
<input type="submit" name="submit_button" value="Run">
</form>
<label for="disk_space">Disk Space: </label>
<input type="text" name="disk_space" value="{{ disk_space }}" id="disk_space">
{% endblock %}
The Views Function for the HTML:
def submit(request): #start scrtipt
submit.preProcess = subprocess.Popen(['chmod', '+x /home/psvuser/Desktop/test.sh'])
submit.process = subprocess.Popen(['/home/psvuser/Desktop/test.sh'])
global sem
sem = True
disk_space = request.get('disk_space', '')
disk_space = diskSpace()
start_time= int(round(time.time()))
f = open("/home/psvuser/Desktop/writer.txt", "a")
f.write(str(start_time))
f.write(", ")
return render(request, 'Checklist/stop.html')
I have an idea on how to periodically refresh the page, however, I don't know how to display 'disk_space' onto the html.
Since you want to do regular asynch calls to your server while a page is already loaded, I would use AJAX to solve this problem.
I'd usually do something like this
def your_django_view(request):
# magic
server_data = '98% left!'
response_data['data'] = server_data
return HttpResponse(json.dumps(response_data), content_type="application/json")
Then in your view, use some jquery (or javascript..whatever) to deal with all of the data
$.ajax({type: 'POST',
url: '/function_url/',
data: {
},
success: function(response_data) {
var server_mem = response_data['data']
$('#disk_space').val(server_mem);
})
Use this return statement at the end of your submit view:
return render_to_response('Checklist/stop.html',
{'disk_space':disk_space},
context_instance=RequestContext(request))
The second argument of render_to_response is a dictionary of {'key':value, 'key2':value2} you can use in the template.
I'm trying to get my hands dirty with django and I started trying to make my own project. I'm currently having trouble passing parameters from my template to my views without using POST requests.
heres my code in the template
#in main.html
<div>
{{ event_results }}
{{ friends }}
</div>
{% for user in results %}
<div class="userblock">
<p class="user">{{ user.username }}</p>
<a href="/events/addFriend/{{user.username}}">
<button class="navbuttons" id="addfriend">Add as friend</button>
<a/>
</div>
{% endfor %}
#in urls.py
from django.conf.urls import patterns, url
from events import views, eventview
url(r'^addFriend/(<requested_friend>[a-z]*)/', views.addFriend, name='addFriend'),
)
#in views.py
def addFriend(request, requested_friend):
currentUser = User.objects.get(username=request.session['username'])
try:
list_of_friends = Friends.objects.get(username=currentUser)
except (KeyError, Friends.DoesNotExist):
return render(request, 'events/main.html', {'friends': requested_friend})
else:
return render(request, 'events/main.html', {'friends':list_of_friends})
So when I click on the button "Add friend" in main.html, it goes to the url.py and maps it to the function addFriend in views.py with the argument and from there it does its magic. However, it's not taking in the argument. I know I'm doing something wrong in the urls.py with the regex but im not sure what. Any advice is greatly appreciated. Thanks!
When you change (<requested_friend>[a-z]*) to (?P<requested_friend>[0-9A-Za-z_\-]+) than everything looks fine.
But remember to use + instead of * in the pattern. * matches also a empty string (addFriend// is matched) but with + the string must have at least one character (addFriend// isn't matched)
You can add $ on the end of url pattern r'^addFriend/(?P<requested_friend>[0-9A-Za-z_\-]+)/$' Here you can find why.
Also check if link in browser has correct value /events/addFriend/<user_name>/ maybe is something wrong with {{ user.username }}
You have error in urls.py. In named group pattern you miss ?P prefix. See doc for reference.
Instead of
url(r'^addFriend/(<requested_friend>[a-z]*)/', views.addFriend, name='addFriend'),
It should be:
url(r'^addFriend/(?P<requested_friend>[a-z]*)/', views.addFriend, name='addFriend'),
As mentioned in the title, my paginator doesn't show anything when I click to go to a page beyond the first.
First, let me describe my page in general:
Its function is to get a request input from the user specifying the period interval from which he wants to see a bunch of "call records" along with other filters (this is important). So essentially there's a start and end date from the request and I use it to filter my objects.
The link to "page2" is something like: "localhost:8000/?page=2" and redirects to my existing page but without any data. It's obvious now that the link to the next page should include the other parameters such as start_date=xxxx-xx-xx, or else it wouldn't work.
Here's part of my view.py and I took out a lot of lines to make it brief, the code runs fine:
if request.GET:
filter_form = ReportFilterForm(request.GET)
if filter_form.is_valid():
start = filter_form.cleaned_data["start_date"]
end = filter_form.cleaned_data["end_date"]
#a bunch of omitted lines that use the form to filter
paginator = Paginator(queryset, 100)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
try:
call_logs = paginator.page(page)
except (EmptyPage, InvalidPage):
call_logs = paginator.page(paginator.num_pages)
else:
filter_form = ReportFilterForm()
return render_to_response('xxxx.html',
{'queryset': queryset,
'filter_form': filter_form,
'call_logs': call_logs,
})
My template xxxx.html, just the paginator section, which is pretty standard, taken from the documentation:
{% if call_logs.paginator.num_pages %}
<div class="pagination">
<span class="step-links">
{% if call_logs.has_previous %}
<<
{% endif %}
<span class="current">
Page {{ call_logs.number }} of {{ call_logs.paginator.num_pages }}
</span>
{% if call_logs.has_next %}
>>
{% endif %}
</span>
</div>
{% endif %}
My question is how do I get the current window URL using django templates and not javascript?
Thank you.
You could add the full path to the context from the request object if I understand you correctly:
return render_to_response('xxxx.html',
{'queryset': queryset,
'filter_form': filter_form,
'call_logs': call_logs,,
'magic_url': request.get_full_path(),
})
My question is how do I get the
current window URL using django
templates and not javascript? Thank
you.
it's not necessary the right way to do it, but you can check this post
but i will suggest that you shouldn't mix the filter with the pagination.
rather that you can use AJAX when doing filtering you can create a new function that deal with filtering alone or you can just use the same function and test if request.is_ajax(): , like that when a users filter the contain you will have your filter data (start_date,end_date ) in the URL.
and now when a user want to pass to the next page you already have the filtered argument in the url that you can use to create a queryset that will be pass to the Paginator.
And to deal with the javascript not active you can replace AJAX with a simple POST form and just remember don't mix the filtering with the pagination :)
Hope this will Help :)