In my Flask application, I have a view which renders a table of items by using the Flask-SQLAlchemy pagination method. Great stuff so far. But I want to add sorting and filtering, so I created a form with selectboxes where the user can choose the sort and filter options.
When submitting the sort/filter on the page, the view works fine: the first page is sorted. But selecting another page on the page, the pagination is falling back to the original query. What should I do to save my sort/filter options during new page loads? Using flask.g has come up to me, but is it the correct way?
class ItemTableForm(Form):
sort_choices = [('id', 'ID'),
('name', 'Name'),
('status', 'Status')]
filter_choices = [('all', 'All'),
('instock', 'In stock'),
('sold', 'Sold')]
sort = SelectField(u'Sort by', choices=sort_choices)
filter = SelectField(u'Filter by', choices=filter_choices)
#app.route('/browse/<int:page>', methods=("GET", "POST"))
def browse(page):
form = ItemTableForm()
if form.validate_on_submit():
query = Item.query.order_by(getattr(Item, form.sort.data))
else:
query = Item.query
pagination = paginate(query, page)
return render_template('browse.html', pagination=pagination, form=form)
# My template contains this form and a regular HTML table
<form action="{{ url_for('browse') }}" method="POST">
{{ form.hidden_tag() }}
{{ form.sort.label }} {{ form.sort() }}
{{ form.filter.label }} {{ form.filter() }}
<button type="submit" class="btn">Submit</button>
</form>
You can use url parameters to pass the info about sorting. Say user selects sorting by name. Then add this at the end of url
your_url?sort=name
Then you can access it as
value = request.args.get('name','')
Simply pass sorting variable value to the template where you append sort value to the next url.
Edit:
To create such url in Flask, do this:
url_for('url_view_name', sort='name')
This will return the url with sort appended as query argument. Check out the flask documentation here to know more about url building
You could do this with Javascript. Since the page number is part of your URL, you could have javascript change the action of the form that changes pages to submit to a URL with the desired page number instead of the current page.
Just to clarify, when the user clicks the "next" link/button or page number, use Javascript to change the action of the html form so that it posts the form to the desired page instead of the current page.
Related
I have a Django project with an HTML file that lists all of the CSV files that have been uploaded to my Postgresql database and when you click on the CSV of interest a new page is rendered with the CSV model's basic information (name/time submitted).
This is the First Page:
{% for csv in Data_List %}
<button class="btn btn-primary" style = "font-size:1.2em;" >{{csv.name}}</button>
<br><br>
{% endfor %}
This is the second page:
<p>{{request.user.username}}'s Note
<h6>{{worklog.name}}
<br>
{{worklog.date}}
<br>
{{worklog.notes|safe}}
<br>
{{worklog.mycsv|safe}}
</h6>
</p>
However, my goal is that when you click the button a python VIEW will be passed (or just retrieve) the chosen posts primary key (or other information). I want to do this so the view can ask the database for the actual CSV and do some work.
How can I get a view.py to request a selected posts information such as primary key?
Thanks and Happy Coding
#The url
url(r'^anote/(?P<pk>\d+)$', views.A_Note, name = 'anote'),
#The view
def A_Note(request, pk):
#the rest of code here
return render(request, "some.html", {"thing":thing, "pk":pk, "etc":etc})
I learned that sharp brackets <> in a url passes the value through to the view where it can then be accessed as an argument when defining the view. From there it can easily be used in the code.
Thanks!
What i have:
In my Django configuration, I create a painting model in models.py
class Painting(models.Model):
Number = models.CharField(max_length=128)
When the right URL is called, views.py returns a HTML paged rendered with a SQL query.
def index(request):
painting_list = Painting.objects.order_by('id')[:5]
context_dict = {'paintings':painting_list}
return render(request, 'binaryQuestionApp/index.html', context=context_dict)
Now, I can access the Number fields in my HTML like so:
<div>
{% if paintings %}
<ul>
{% for painting in paintings %}
<li> {{ painting.Number }} </li>
{% endfor %}
</ul>
{% endif %}
</div>
What i want:
Next, I wish to be able to change the values on the website. Something like this
{{ painting.Number = 1}} // set the value to 1
{{ painting.Number += 1}} // or maybe take the current value and increment
Next, I would want to send the newly set values back to Django to update the Painings, probably with a POST request.
Question:
What is the best way to get the desired behavior?
You need to send a post request either with a form, which is probably the easiest, or javascript. Send the value that you want to change the Number field by and the pk of the model or something else which you can use to identify the model.
Then in that index view you can handle the post request.
def index(request):
painting_list = Painting.objects.order_by('id')[:5]
context_dict = {'paintings':painting_list}
if request.method == 'POST':
# You can add some validation here to check that the values aren't empty
pk = request.POST['pk'] # if this is what you posted
value = request.POST['value'] # value to change the number field by
painting = Painting.objects.get(pk=pk)
painting.Number += value
return render(request, 'binaryQuestionApp/index.html', context=context_dict)
You can also achieve this with a model form, but this method is probably the easiest one to go for right now.
P.S. I would suggest you make the number field an IntegerField instead of CharField if you want to change the value like this.
I think the best way is to create a new Post View with 2 fields, one for the painting id to modify and one for the new value.
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.
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 :)
I'm trying to build a simple website with login functionality very similar to the one here on SO.
The user should be able to browse the site as an anonymous user and there will be a login link on every page. When clicking on the login link the user will be taken to the login form. After a successful login the user should be taken back to the page from where he clicked the login link in the first place.
I'm guessing that I have to somehow pass the url of the current page to the view that handles the login form but I can't really get it to work.
EDIT:
I figured it out. I linked to the login form by passing the current page as a GET parameter and then used 'next' to redirect to that page. Thanks!
EDIT 2:
My explanation did not seem to be clear so as requested here is my code:
Lets say we are on a page foo.html and we are not logged in. Now we would like to have a link on foo.html that links to login.html. There we can login and are then redirected back to foo.html.
The link on foo.html looks like this:
<a href='/login/?next={{ request.path }}'>Login</a>
Now I wrote a custom login view that looks somewhat like this:
def login_view(request):
redirect_to = request.REQUEST.get('next', '')
if request.method=='POST':
#create login form...
if valid login credentials have been entered:
return HttpResponseRedirect(redirect_to)
#...
return render_to_response('login.html', locals())
And the important line in login.html:
<form method="post" action="./?next={{ redirect_to }}">
So yeah thats pretty much it, hope that makes it clear.
You do not need to make an extra view for this, the functionality is already built in.
First each page with a login link needs to know the current path, and the easiest way is to add the request context preprosessor to settings.py (the 4 first are default), then the request object will be available in each request:
settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
"django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.request",
)
Then add in the template you want the Login link:
base.html:
Login
This will add a GET argument to the login page that points back to the current page.
The login template can then be as simple as this:
registration/login.html:
{% block content %}
<form method="post" action="">
{{form.as_p}}
<input type="submit" value="Login">
</form>
{% endblock %}
To support full urls with param/values you'd need:
?next={{ request.get_full_path|urlencode }}
instead of just:
?next={{ request.path }}
This may not be a "best practice", but I've successfully used this before:
return HttpResponseRedirect(request.META.get('HTTP_REFERER','/'))
Django's built-in authentication works the way you want.
Their login pages include a next query string which is the page to return to after login.
Look at http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.decorators.login_required
I linked to the login form by passing the current page as a GET parameter and then used 'next' to redirect to that page. Thanks!
I encountered the same problem. This solution allows me to keep using the generic login view:
urlpatterns += patterns('django.views.generic.simple',
(r'^accounts/profile/$', 'redirect_to', {'url': 'generic_account_url'}),
)
In registration/login.html (nested within templates folder) if you insert the following line, the page will render like Django's original admin login page:
{% include "admin/login.html" %}
Note: The file should contain above lines only.
See django docs for views.login(), you supply a 'next' value (as a hidden field) on the input form to redirect to after a successful login.
You can also do this
<input type="hidden" name="text" value="{% url 'dashboard' %}" />