I am trying to write a controller method and a corresponding view which will call the controller on web2py using Ajax. The idea is to make a small update on the database and return a simple updated message on a target div using Ajax. Below is contoller method:
def deleteajax():
recid1 = request.vars.recid
reptype1 = request.vars.reptype
if recid1 == None:
out = 'Missing Param : recid'
return out
if reptype1 == None:
reptype1 = 'pr'
if reptype1 == 'pr':
row = db2(db2.prs_mailed.id==recid1).select().first()
return str(row.id)
elif reptype1 == 'tb':
row = db2(db2.tbs_mailed.id==recid1).select().first()
else:
return 'Please provide the parameter : rep'
if row['action'] == 'D':
out = 'Already deleted'
return out
else:
row.update_record(action='D')
out = 'Deleted Successfully!'
return out
and this is how I am calling the same from view:
<form>{{
response.write('<input type="hidden" name="recid" value="'+str(response._vars['prs']['id'][k])+'"/>',escape=False)}}
<input type ='button' name="del" value = "D" onclick="ajax('deleteajax', ['reid'], 'target')" />
<div id="target"></div>
</form>
I have tested the controller individually using a POST call and that works. Even the AJAX call works and displays error messages like 'Missing Param : recid' on the target div. I even tried modifying the controller to show more messages after finishing each statement. However, post any database operation, no commands from the controller are getting executed, nor is anything showed on the target div. Where am I going wrong?
First, instead of this:
{{response.write('<input type="hidden" name="recid" value="' +
str(response._vars['prs']['id'][k])+'"/>',escape=False)}}
Just do this:
<input type="hidden" name="recid" value="{{=prs['id'][k])}}"/>
There's no need to use response.write or to access the "prs" object through response._vars (all the items in response._vars are available globally in the view environment).
Regarding the Ajax problem, your input element name is "recid", but your ajax() call refers to "reid". Change the latter to "recid" and see if it works.
UPDATE:
To create multiple unique recid's, you could do:
name="{{='recid%s' % prs['id'][k]}}"
Then, in the controller, check for request.vars that start with "recid":
recid = [v for v in request.post_vars if v.startswith('recid')]
if recid:
recid = int(recid[0][5:])
[code to delete record with id==recid]
Related
I 'ld like for a user to drop a random search in a search form for a book title and find get results if the book is in the db. Below is part of the code block.
I 'm having some issues searching for a single document in my mongodb using a search field and search strings. Below is the code. I'm trying to get the search result via the find_book route.
The code above with the /find_book/<book_id> returns errors.
Below is a part of my code in the app.py file and the search form.
I get the following errors.
werkzeug.routing.BuildError
werkzeug.routing.BuildError: Could not build url for endpoint 'find_book'. Did you forget to specify values ['book_title']?
Traceback (most recent call last)
# create an instance of py_mongo with app as argument
mongo = PyMongo(app)
#app.route('/')
def home():
return render_template('home.html')
# define the various menu options
#app.route('/get_books')
def get_books():
return render_template('books.html', books=mongo.db.books.find())
# Add a book
#app.route('/add_book')
def add_book():
return render_template('add_book.html',
faculties=mongo.db.faculties.find())
# Add submit button for Books
#app.route('/insert_book', methods=['POST'])
def insert_book():
book = mongo.db.books
book.insert_one(request.form.to_dict())
return redirect(url_for('get_books'))
# wire the edit button
#app.route('/edit_book/<book_id>')
# description task, name, due date, is urgent fields will be
# pre-populated based on the information returned in the task.
def edit_book(book_id):
a_book = mongo.db.books.find_one({"_id": ObjectId(book_id)})
# category names will be prepolulated based on the collection
# # of categories returned in the categories cursor
all_faculties = mongo.db.faculties.find()
return render_template('edit_book.html',
book=a_book, faculties=all_faculties)
#app.route('/update_book/<book_id>', methods=['POST'])
def update_book(book_id):
# access the database collection
book = mongo.db.books
# call the update function, specify an id
book.update({'_id': ObjectId(book_id)},
{
'faculty_name': request.form.get('faculty_name'),
'subject_name': request.form.get('subject_name'),
'book_title': request.form.get('book_title'),
'book_author': request.form.get('book_author'),
'book_description': request.form.get('task_description'),
'lender_name': request.form.get('lender_name'),
'due_date': request.form.get('due_date'),
'is_available': request.form.get('is_urgent')
})
return redirect(url_for('get_books'))
# specify the form fields to match the keys on the task collection
# delete a book
#app.route('/delete_book/<book_id>')
def delete_book(book_id):
mongo.db.books.remove({'_id': ObjectId(book_id)})
return redirect(url_for('get_books'))
# find a book by text search
#app.route('/find_book/<book_title>', methods=['GET'])
def find_book(book_title):
book_title = mongo.db.books
book_title.find_one(
{
'book_title': request.form.get('book_title'),
})
return render_template('find.html', book_title=book_title)
# categories function
#app.route('/get_faculties')
def get_faculties():
return render_template('faculties.html',
faculties=mongo.db.faculties.find())
if __name__ == '__main__':
app.run(host=os.environ.get('IP'),
port=int(os.environ.get('PORT')),
debug=True)
<form action="{{ url_for('find_book') }}" method="GET">
<input type="text" placeholder="Book Title" id="book_title" name="book_title" >
<button type="submit"><i class="fa fa-search">Search</i></button>
</form>
Your find_book route is expecting an argument book_title
But you are not passing that in {{ url_for('find_book') }}
You could just change this route to #app.route('/find_book') and get the value from request.form or if you are using this route in another place of your application you could use the approach from this question and use this way:
#app.route('/find_book/', defaults={'book_title': None})
#app.route('/find_book/<book_title>')
def find_book(book_title):
books = mongo.db.books
if book_title is None:
book_title = request.form.get('book_title')
book = books.find_one({
'book_title': book_title
})
return render_template('find.html', book=book)
I could not run this snippet of code now, so let me know if dont work.
I have this app with a profile and I want to update only a specific row based on its account id. Inserting the data works, but updating a specific row doesn't and I'm not sure which part is wrong with my code.
#app.route('/edit_parent/<int:acc_id>', methods=['GET','POST'])
def edit_parent(acc_id):
myParent = Parent.query.filter_by(acc_id=int(acc_id)).first()
if request.method == "POST":
myParent.fname_p = request.form['fname_p']
myParent.lname_p = request.form['lname_p']
myParent.bday_p = request.form['bday_p']
myParent.add_p = request.form['add_p']
db.session.commit()
print "hello success"
return redirect(url_for('parent', acc_id=int(acc_id)))
if request.method == "GET":
return render_template('edit_p.html', acc_id=int(acc_id))
It prints the "hello success" and redirects to the parent url but returns an error 302 and still no changes in the db.
I don't think you are updating a specific row at all, but instead you are just inserting new one each time with:
myParent = Parent(request.form['fname_p'], request.form['lname_p'],
request.form['bday_p'], request.form['add_p']).where(acc_id=acc_id)
db.session.add(myParent)`
So, what you are supposed to do instead is:
myParent = Parent.query.filter_by(acc_id=acc_id)
assuming your Parent db has the following attributes:
myParent.fname = request.form['fname_p']
myParent.lname = request.form['lname_p']
myParent.bday = request.form['bday_p']
myParent.add = request.form['add_p']
db.session.commit()
solved it by adding:
myParent = db.session.merge(myParent)
this way it merges the current session with the previous one. It still returns a 302 but the data on the db has been successfully updated.
I've one problem here on elif statement.
time and visual are my checkbox value.Click here for more understanding
On if statement is working if user select visual in checkbox it will display the output that I read from text file.
When comes to elif statement if check only time it will display nothing
I want user to have an option want to display the first statement, second statement or both of it
THIS views.py
token = request.GET.get('token')
context = {}
data = {}
prev_key = ''
with open(path) as input_data:
for line in input_data:
if (token == 'visual'):
if line.startswith('2_visualid_')
prev_key = line.lstrip('2_visualid_').rstrip()
data.update({line.lstrip('2_visualid_').rstrip(): []})
elif (token == 'time'):
if search_string in line:
if prev_key in data:
data[prev_key].append
(next(input_data).lstrip('2_mrslt_').rstrip())
context = {'output': data}
return render(request, 'Output.html', context)
Form HTML
<form action="">
  <input class="regular-checkbox" type="checkbox" name="token" value="visual"><b>   Visual ID</b><br>
  <input class="regular-checkbox" type="checkbox" name="token" value="time"><b>   Time Delay Index</b>
</form>
Note
Hope you can understand be pleased to comment on my explanation don't just downvote it because I learn nothing from it. Thank you.
Ok your problem is that 1. you're only retrieving one single value for the token key (HTTP querystrings and forms can have multiple values for a same key) and 2. you're only testing against one single value.
The first problem is easily solved by using request.GET.getlist("token") to get all values (instead of only the last one), as documented here.
The second problem is easily solved too: now that you have a list instead of a single value, just test for containmenent (if xxx in values) instead of equality (if value == xxx) and replace the elif with a if:
tokens = request.GET.getlist('token')
# ...
with open(path) as input_data:
for line in input_data:
if 'visual' in tokens and line.startswith('2_visualid_'):
prev_key = line.lstrip('2_visualid_').rstrip()
data.update({prevkey: []})
# you may want to continue to next line here - or not
# continue
if 'time' in tokens:
if search_string in line and prev_key in data:
data[prev_key].append(next(input_data).lstrip('2_mrslt_').rstrip())
I am trying to return a python dictionary to the view with AJAX and reading from a JSON file, but so far I am only returning [object Object],[object Object]...
and if I inspect the network traffic, I can indeed see the correct data.
So here is how my code looks like. I have a class and a method which based on the selected ID (request argument method), will print specific data. Its getting the data from a python discretionary. the problem is not here, have already just tested it. But just in case I will link it.
# method to create the directionary - just in case #
def getCourselist_byClass(self, classid):
"""
Getting the courselist by the class id, joining the two tables.
Will only get data if both of them exist in their main tables.
Returning as a list.
"""
connection = db.session.connection()
querylist = []
raw_sql = text("""
SELECT
course.course_id,
course.course_name
FROM
course
WHERE
EXISTS(
SELECT 1
FROM
class_course_identifier
WHERE
course.course_id = class_course_identifier.course_id
AND EXISTS(
SELECT 1
FROM
class
WHERE
class_course_identifier.class_id = class.class_id
AND class.class_id = :classid
)
)""")
query = connection.engine.execute(raw_sql, {'classid': classid})
for column in query:
dict = {
'course_id' : column['course_id'],
'course_name' : column['course_name']
}
querylist.append(dict)
return querylist
my jsonify route method
#main.route('/task/create_test')
def get_courselist():
#objects
course = CourseClass()
class_id = request.args.get('a', type=int)
#methods
results = course.getCourselist_byClass(class_id)
return jsonify(result=results)
HTML
and here is how the input field and where it should link the data looks like.
<input type="text" size="5" name="a">
<span id="result">?</span>
<p>click me
and then I am calling it like this
<script type=text/javascript>
$(function() {
$('a#link').bind('click', function() {
$.getJSON("{{ url_for('main.get_courselist') }}", {
a: $('input[name="a"]').val()
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
but every time I enter a id number in the field, i am getting the correct data. but it is not formatted correctly. It is instead printing it like [object Object]
source, followed this guide as inspiration: flask ajax example
The data return by your server is like: {result: [{course_id: 'xxx', course_name: 'xxx'}]}, in which data.result is a JS Array.
when you set it to $("#result").text(), JS convert a array to string, so the result is [object Object].
You should iterate over the array to construct a string, then set the string in DOM, like:
courseStr = data.result.map(function(course) {return course.course_id + '-' + course.course_name; }).join(',');
$("#result").text(courseStr);
The API description for flask.json.jsonify indicates it's expecting keyword parameters. What you actually want to do seems to be serialize a list object containing dictionaries, have you tried flask.json.dumps instead? Assuming you've got the dumps symbol imported, instead of your jsonify call you can try:
return dumps(results)
I'm attempting to do something simple and documented well, except for that it's not working on my web app.
essentally i want to save some extra attributes for the uploaded files, like original filename, email of user and also the upload date.
Now following the web2py documentation i've created this submit view. It is almost word for word copied from the documentation section here
I have a controller data.py
def submit():
import datetime
form = SQLFORM(db.uploads, fields=['up_file'], deletable=True)
form.vars.up_date = datetime.datetime.now()
form.vars.username = auth.user.email
if request.vars.up_file != None:
form.vars.filename = request.vars.up_file.filename
if form.process().accepted:
redirect(URL('data', 'index'))
elif form.errors:
response.flash = "form has errors"
and my db.py excerpt:
db.define_table('uploads',
Field('username', 'string'),
Field('filename', represent = lambda x, row: "None" if x == None else x[:45]),
Field('up_file', 'upload', uploadseparate=True, requires=[IS_NOT_EMPTY(), IS_UPLOAD_FILENAME(extension=ext_regex)]),
Field('up_date', 'datetime'),
Field('up_size', 'integer', represent= lambda x, row: quikr_utils.sizeof_fmt(x) ),
Field('notes', 'text'))
Currently the validation doesn't appear to do anything, when I submit my function, the filename isn't getting saved for some reason, and i get an error elsewhere because the value is None
You need to do something like this :
DB :
db.define_table('t_filetable',
Field('f_filename', type='string', label=T('File Name')),
Field('f_filedescription', type='text',
represent=lambda x, row: MARKMIN(x),
comment='WIKI (markmin)',
label=T('Description')),
Field('f_filebinary', type='upload', notnull=True, uploadseparate=True,
label=T('File Binary')),
auth.signature,
format='%(f_filename)s',
migrate=settings.migrate)
Controller : (default.py)
#auth.requires_login()
def addfile():
form = SQLFORM(db.t_filetable, upload=URL('download'))
if form.process(onvalidation=validate_filename).accepted:
response.flash = 'success'
elif form.errors:
response.flash = 'form has errors'
return dict(form=form)
def validate_filename(form):
if form.vars.f_filename == "":
form.vars.f_filename = request.vars.f_filebinary.filename
Function validate_filename is called AFTER the form has been validated, so form.vars should be available to use here. Function validate_filename checks if form.vars.f_filename has any value other than "" (blank) ; if not, it reads the filename from the request.vars.f_filebinary and assigns it to the form.vars.f_filename . This way you can allow users to provide an optional field for filename. If they leave it blank, and just upload the file, the f_filename in DB will be the original filename.
I tried your pasting your code into web2py to see where it goes wrong and it actually worked for me (at least the file names saved). Maybe the problem is elsewhere?