How to get WTF form object data without a POST request? - python

I have a template which allows user to enter search parameter (search.html)
{% from "_formhelpers.html" import render_field %}
<form method=”post”>
<dl>
{{ render_field(form. notificationId) }}
{{ render_field(form. recordName) }}
</dl>
<div id="searchResults" > </div>
</form>
Macro
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
And a template for search results(result.html)
{% for notification in content %}
Result:
content[notification]['type'] }}</td>
{% endfor %}
I am using the following ajax request to get search results and display in the above template.The ajax function is called on keyup of both the search fields
$.ajax(
{
url: ‘result’,
dataType: "html",
data:createQueryString(),// gets the value from the dataFields and creates a queryString
success: function(data)
{
$("# searchResults").html(data); } });
I also I have 2 views one for the searchcriteria section( the section containing the 2 search fields) and one for the search results section.
class NotificationSearchView(MethodView):
def get(self):
searchform = SearchForm()
return render_template("search.html”, searchform=searchform)
#classmethod
def registerSelf(cls, app):
NotificationSearchView.app = app
app.flaskApp.add_url_rule('/search ', view_func=NotificationSearchView.as_view(‘search’))
class NotificationResultView(MethodView):
def get(self):
searchform = SearchForm()
success, content=self.performSearch(searchform) //search method which takes the search parameters and performs the search
return render_template("result.html”, content=content)
#classmethod
def registerSelf(cls, app):
NotificationResultView.app = app
app.flaskApp.add_url_rule('/result', view_func= NotificationResultView.as_view(‘result’))
WTF form class
from wtforms import Form, TextField
class SearchForm(BaseForm):
notificationId = TextField(notificationId)
recordName = TextField(recordName)
The issue which I am facing is that the wtf form object isn’t populated when the ajax call is made to the NotificationResultView, which I believe is because there is no post request fired, but according to my design there is no need of a post request.
I have tried by making the ajax request as post request but even then the wtf form object comes back empty.
Now my only other option is if I pass the search criteria in the query string when making the ajax call, but not sure if that’s the best approach.Please suggest how to proceed in this case.

$.ajax(
{
url: ‘result’,
dataType: "html",
success: function(data)
{
$("# searchResults").html(data); } });
In your ajax function you aren't sending any data.
update : ok, so you are actually sending data and url is correct.
class NotificationResultView(MethodView):
def get(self):
searchform = SearchForm()
success, content=self.performSearch(searchform) //search method which takes the search parameters and performs the search
return render_template("result.html”, content=content)
from above your view function (which is routed to 'result'), your problem is that searchform = SearchForm() isn't filled with supplied GET queries.
To solve this you have to supply the values to searchform. there are many ways to do it(the most obvious one would be fiddling with form object creation, by searchform = SearchForm(formdata = request.args)), and it can be done easier by using flask extension flask-wtf.
here is one way to to do it(I'll provide my example since I don't know how you build your query string) :
in html:
query_obj = { foo : 'bar', bar : 'baz', answer : 42 };
$.params(query_obj); //foo=bar&bar=baz&answer=42
/* ajax using above params */
with assumption that parameters are successfully supplied to the view:
#app.route('/result', method=['GET'])
def fooview():
foo = request.args.get('foo', '')
bar = request.args.get('bar', '')
answer = request.args.get('answer', None, type=int)
..
hope that helps.

Related

CKAN: context not being passed to new template

In my CKAN extension, I'm adding a new tab to the user dashboard. I followed the procedure described in this answer and it seemed to work. In the controller for the new page, I set the template variables in the same way as they are set for the other pages on the dashboard. When I click on the tab and load the new page, though, I get UndefinedError: 'user_dict' is undefined. What's going wrong?
Here's the relevant part of my_extension/templates/user/dashboard.html where I add the tab:
{% ckan_extends %}
{% block page_header %}
<header class="module-content page-header hug">
<div class="content_action">
{% link_for _('Edit settings'), named_route='user.edit', id=user.name, class_='btn btn-default', icon='cog' %}
</div>
<ul class="nav nav-tabs">
{{ h.build_nav_icon('dashboard.index', _('News feed')) }}
{{ h.build_nav_icon('dashboard.datasets', _('My Datasets')) }}
{{ h.build_nav_icon('dashboard.organizations', _('My Organizations')) }}
{{ h.build_nav_icon('dashboard.groups', _('My Groups')) }}
{{ h.build_nav_icon('my_extension_dashboard_owned_datasets', _('My Owned Datasets')) }}
</ul>
</header>
{% endblock %}
Here's the new template so far, my_extension/templates/user/dashboard_owned_datasets.html:
{% extends "user/dashboard_datasets.html" %}
The relevant part of the plugin class definition:
class MyThemePlugin(plugins.SingletonPlugin, DefaultTranslation):
plugins.implements(plugins.IRoutes, inherit=True)
# IRoutes
def before_map(self, map):
with SubMapper(
map, controller="ckanext.my_extension.controller:MyUserController"
) as m:
m.connect(
"my_extension_dashboard_owned_datasets",
"/dashboard/owned_datasets",
action="dashboard_owned_datasets",
)
return map
And here's the new controller, in my_extension/controller.py:
# encoding: utf-8
import logging
import ckan.lib.base as base
from ckan.common import c
from ckan.controllers.user import UserController
log = logging.getLogger(__name__)
render = base.render
class MyUserController(UserController):
def dashboard_owned_datasets(self):
context = {"for_view": True, "user": c.user, "auth_user_obj": c.userobj}
data_dict = {"user_obj": c.userobj, "include_datasets": True}
self._setup_template_variables(context, data_dict)
log.critical(c.user_dict)
return render(
"user/dashboard_owned_datasets.html"
)
As you can see, I use the UserController's _setup_template_variables method, which is used in all the other dashboard actions in that controller. That method sets c.user_dict, among other things:
def _setup_template_variables(self, context, data_dict):
c.is_sysadmin = authz.is_sysadmin(c.user)
try:
user_dict = get_action('user_show')(context, data_dict)
except NotFound:
h.flash_error(_('Not authorized to see this page'))
h.redirect_to(controller='user', action='login')
except NotAuthorized:
abort(403, _('Not authorized to see this page'))
c.user_dict = user_dict
c.is_myself = user_dict['name'] == c.user
c.about_formatted = h.render_markdown(user_dict['about'])
I'm logging c.user_dict after setting it in the controller, and I see all the data I would expect to see there.
But when I click the tab and load http://localhost:5000/dashboard/owned_datasets, I get the error UndefinedError: 'user_dict' is undefined. What am I missing?
The answer is that the IRoutes plugin interface no longer works for what I want to use it for, due to the migration of CKAN from Pylons to Flask. The IBlueprint interface should be used instead. See https://github.com/ckan/ckan/wiki/Migration-from-Pylons-to-Flask#flask-views-blueprints-and-routing for more information on blueprints and how to convert an extension from using IRoutes to IBlueprint.

Flask-WTF: multiple forms of same class all returning the same data on submission

I'm generating multiple forms, one per image, and packaging the image and corresponding form as a tuple in a list.
This list is then passed to Jinja where each tuple is unpacked and each image and form are inserted into a list for voting via the form.
My issue is that clicking on any one of the specific forms causes all form to return as if that button was clicked.
So, in effect, up or down voting one image acts as if that button was clicked for all other images.
I know I am creating legitimate forms as I have tried printing the form and it's return data to console. When I do this, each form does have a unique address, and all forms show the same data (True / False) in the form.field.data attribute.
Can somebody help me discover what's going on here?
Form:
class VoteForm(FlaskForm):
upvote = SubmitField('vote up')
downvote = SubmitField('vote down')
Route:
#index_mod.route('/', methods = ['GET', 'POST'])
def index():
pics = Pic.select().order_by(Pic.score.desc())
pics_and_forms = []
for pic in pics:
voteform = VoteForm()
#tuple of pic and corresponding form
pics_and_forms.append( (pic, voteform) )
for pic, form in pics_and_forms:
if form.validate_on_submit():
if form.upvote.data:
pic.score += 1
pic.save()
if form.downvote.data:
pic.score -= 1
pic.save()
return render_template('index.html', pics_and_forms = pics_and_forms)
Jinja:
<ul>
{% for pic, form in pics_and_forms %}
<li>
<b>{{ pic.name }} </b>
<i>Submitted by {{ pic.user.username }}</i>
Score: {{ pic.score }}
<img src="/pic/get/{{ pic.uuid }}" style="width:128px;" >
<form method="post" action=" {{ url_for('index_mod.index') }}">
{{ form.csrf_token }}
{{ form.upvote }}
{{ form.downvote }}
</form>
</li>
{% endfor %}
</ul>
EDIT
So I'm figuring out that while I can embed as many forms onto the page as I want the returning post request doesn't specify which specific form was clicked.
Instead I'm planning to embed the details into a hidden field and then use the flask request object to retrieve that field from the hidden form.
I would rather use Flask-WTF fully for this but it seems like there's no elegant way to dynamically add multiple forms to a page and retrieve which form was actually clicked.
You're only ever submitting one form at a time, so you really only need to be dealing with one Form object. I think a better approach would be to POST to a URL which contains the ID of the Pic you're voting on, and the up/down vote is captured from the submit button clicked.
I've refactored your code to illustrate this:
app.py
from flask import Flask, render_template, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import SubmitField
app = Flask(__name__)
app.secret_key = 'secret'
class VoteForm(FlaskForm):
upvote = SubmitField('vote up')
downvote = SubmitField('vote down')
#app.route("/", methods=['GET'])
def index():
form = VoteForm()
pics = [
{
"name": "test",
"user": {"username": "test"},
"score": 1,
"uuid": 'test'
},
{
"name": "test2",
"user": {"username": "test"},
"score": 2,
"uuid": 'test2'
}
]
return render_template("index.html", form=form, pics=pics)
#app.route("/pic/<id>/vote", methods=['POST'])
def vote(id):
form = VoteForm()
if form.validate_on_submit():
if form.upvote.data:
print("Upvote for pic {}".format(id))
if form.downvote.data:
print("Downvote for pic {}".format(id))
return redirect(url_for('index'))
if __name__ == "__main__":
app.run(debug=True)
index.html
<ul>
{% for pic in pics %}
<li>
<b>{{ pic.name }} </b>
<i>Submitted by {{ pic.user.username }}</i>
Score: {{ pic.score }}
<img src="/pic/get/{{ pic.uuid }}" style="width:128px;" >
<form method="post" action="{{ url_for('vote', id=pic['uuid']) }}">
{{ form.csrf_token }}
{{ form.upvote }}
{{ form.downvote }}
</form>
</li>
{% endfor %}
</ul>

Django pagination of filtered results using AJAX

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]

How do I change my queryset using AJAX?

I want to be able to change my comments queryset without page refresh. Here are the querysets:
comment_list = Comment.objects.filter().order_by('-score__upvotes')
new_comments_list = Comment.objects.filter().order_by('-timestamp')
Then my template is
{% for comment in comment_list %}
{{ comment }}
...
Is there any way to change {% for comment in comment_list %} to {% for comment in new_comments_list %} using AJAX (no page refresh)?
Or possibly changing the value of comment_list to equal Comment.objects.filter().order_by('-timestamp')?
EDIT
view:
def new_comments(request):
if request.is_ajax():
print('ajax') #prints ajax
comment_list = Comment.objects.filter().order_by('-timestamp')
html = render_to_string('article.html', {'comment_list': comment_list})
return HttpResponse(html)
ajax call:
$('.comments_new').on('click', function() {
$.ajax({
type: 'GET',
url: '/new_comments/',
data: {
csrfmiddlewaretoken: $("input[name='csrfmiddlewaretoken']").val(),
},
success: function (data) {
console.log(data.comment_list); // undefined
}
})
});
What I guess, you are trying to use comment_list when page renders and new_comment_list using ajax without modifying
{% for comment in comment_list %}
{{ comment }}
{% endfor %}
The problem with your code is comment_list is a queryset and it is evaluated on the server side, when the page is rendered (after passing through Django template engine ).Javascript doesn't understand queryset. It understands HTML or JSON. So, you have to modify your script so that it return HTML or JSON for ajax request.
I would suggest you re-write your view like:
from django.template.loader import render_to_string
if request.is_ajax():
new_comments_list = Comment.objects.filter().order_by('-timestamp')
# you can keep your_div_template as a included template in your main template
html = render_to_string('your_div_template', {'comment_list': new_comments_list})
return HttpResponse(html)
And write your frontend to generate this HTML code.
here is a link that gives more better explanation of rendering using ajax: Returning Rendered Html via Ajax

How to use Django views and template tag simultanosly?

In my Django-template:
<div class="A">
{% url renders_data object.id %}
</div>
<div class="B">
{% render_data object.id %}
</div>
Div A is common way to call a method in views.py whereas Div B is for template tags.
User will open a link. Let's say: /myapp/test/ a page will open contain two template tag section at the page. Name of this tag is render_data I want to load the data into each template tag with Ajax. To work with it we need request.ajax:. That is why i thought to write views method. I thought to complete it with the following way:
I wrote the exact copy of template tag in views.py (renders_data with passing object_id parameter) and render it to the right template. When i open the maypp/test after removing the div A from template. It shows the URL (myapp/test/<object_id>) in each template tags section (each corner) except the data. Is their any possibility to show the context except this URL See the image when i choose this option
Second i also thought to import views method (renders_data) in template tag (render_data). So that data will display in each corner and request.Ajax: will also work. if this can be possible then how?
I am not able to solve this issue. please help me :(
See how the render_data looks like:
#register.simple_tag
def render_widget(object_id):
from myapp.plugins.model import Widgetsetting
widget_setting = Widetsetting.objects.get(id = object_id)
widget = widget_settings.get_widget()
template_name = widget.template_name
context = widget.context(widget=widget_settings)
t = get_template("widgets/%s" % template_name)
return t.render(Context(context))
From the code you've posted something like below should work...
in views.py:
from django.http import HttpResponse
from netadmin.plugins.templatetags.widgets import render_widget
def ajax_render_data(request, object_id):
return HttpResponse(render_widget(object_id))
in your django template:
(you said you want several of these on the page, so I'm going to use a loop in the django template, I don't know what your widget collection is called but I'm sure you can work it out for your particular case)
<div class="widgets-container">
{% for widget_settings in widgetsettings.objects.all %}
<div class="widget-content" id="widget-content-{{ widget_settings.pk }}">
not loaded yet
</div>
{% endfor %}
</div>
<script>
// here we use django to render the ajax urls into an object
// in javascript so we can use it on the client side
var ajax_urls = {
{% for widget_settings in widgetsettings.objects.all %}
"{{ widget_settings.pk }}: "{% url ajax_render_data widget_settings.pk %}"{% if not forloop.last %},{% endif %}
{% endfor %}
};
// (I'll assume you're using jQuery)
// start the ajax calls when the page is loaded:
$(document).ready(loadWidgets());
function loadWidgets() {
// loop over the urls we provided from django:
jQuery.each(ajax_urls, function(widget_id, url) {
// do the ajax call:
$.get(url, function(data) {
// put the content into the widget div:
$('#widget-content-'+widget_id).html(data);
});
});
}
</script>
in urls.py:
urlpatterns += patterns('netadmin.plugins.ajax_view',
url(r'^ajax/(?P<object_id>\d+)/$', 'ajax_render_data', name='ajax_render_data'),
)

Categories

Resources