Dynamic HTML content and Python variables with Flask - python

On the user end, say I have people selecting school records through a form to display in a table below.
For instance, they choose their name from a list of names, and that pulls their records from a database.
Dependent on their class year, they may have 1, 2, 3, or 4 years of records, so the data pulled will always look different.
After they submit the form, their records are stored in a variable containing all their records, and then each record is broken down into subtypes, for instance, all records in the English department. Those subtypes are stored in other lists--so there's a list for all English records. Let's call that variable english_records. I want to use these subtype variables to be able to present only the data users want to see, and to present all data in that particular list.
So using Flask's render_template function, I'm trying to send each of these records to an html template that will create a table cell for each record.
What I've been trying (that hasn't worked so far) is something like:
Python:
i = 0
def index():
for e in english_records:
english_records = [
{
'english': english_records[i]
}
]
i = i + 1
return render_template("index.html",
english_records = english_records)
And in HTML:
...table above...
{% for record in english_records %}
<td>
{{record.english}}
</td>
{% endfor %}
...table continues...
Thus far I've been getting table cells created for each record, but the records not being passed through. Anyone know how to do this?

Is there a reason why you're pre-processing the data? What does english_records look like? If my hunch is correct, then you shouldn't actually need the for loop at all.
There's also the issue that you're overwriting the initial english_records variable with english_records within the for loop, so the assignment, while legal in terms of syntax, is logically nonsensical.
Another issue is that, depending on the actual type of the first english_records, you shouldn't need to use a counter: if english_records is a list, then it'll contain the value you're looking for. If english_records is a dict, then enumerating it might look like for key, val in english_records.iteritems().

for e in english_records:
english_records = [
{
'english': english_records[i]
}
]
i = i + 1
This loop here is creating a list , but the members of those list do not have 'english' key ,
so in the template, the loop is executed, but there is no value for {{record.english}} , hence it is ignored.
All you will get is table rows without any data.

Related

How can I edit data fetched from db in html table ? Flask, Jinja2

I'm afraid the title isn't so clear as I want it to be, but I'll try to explain more detailed:
I had a db's table :
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
task = db.Column(db.String(10),index=True)
descr = db.Column(db.String(300),index=True,unique=True)
docs = db.relationship('Doc', backref='Task', lazy='dynamic'
this query docs = Task.query.all()
and some html page with a loop:
<table>
{% for doc in docs %}
<tr><td>{{doc.id}}</td><td>{{doc.task}}</td><td>{{doc.descr}}</td><td>
{% endfor %}
</table>
this all works well and looks like usual html table filled with data from my db (sqlite). Like this, but it's just another table, no matter.
Sometimes the data have missing values like the last column in this pic, it's normal because I want to fill that later. How can I implement such functionality ? I've tried many things.. The problem is that i don't know how to get another info from currently edited row to make correct record to database.
For example, lets's take the second row and add some data to last column -> submit. Now i must somehow get from this row '234' or 'sfdfs' to make
edit_ task = Task.query.filter_by(task=234).first() or
Task.query.filter_by(descr=sfdfs).first()
get the result of this query and simply add new info to unfilled column and than commit.
I can make different forms, requests and other things, but I just don't understand how to make this function in propper way? Shoul i use JS to get info from table in the template, than transfer it to form and than to python /route function? or there is another decision ? may be this html table must be some kind of WTFotms table.widget form? Any ideas would be greatly appreciated. Thx
http://www.blog.pythonlibrary.org/2017/12/14/flask-101-adding-editing-and-displaying-data/ the only way i managed to find. Just don't create an html-table mannualy with cycling , Table Class from flask_table can make it for you and there is also an oppurtunity to make edit link.
Just found this blog post about Grid.js. It makes it possible to edit table cells directly on a page without extra Edit links.

Retail-rendered deform Form (using form attribute) - processing of date is unreliable

Quick summary: How do I ensure correct order of values in request.POST?
I'm using a HTML table (augmented using DataTables to hide columns etc, with the only effect of that being that hidden columns are not included in the call to 'submit') and rendering one deform Form per row.
As deform does not directly support split forms using the form attribute I manually insert form=my_desired_unique_id into all relevant elements. This works fine for simple elements like <input> and <textarea>, the values show up in request.POST.items() and validate fine.
For the deform date-picker things are a bit more complicated though. form['datecolumn'].serialize will generate something like this:-
<input type="hidden" name="__start__" value="datecolumn:mapping"/>
<input type="date"
name="date"
value="2017-01-01"
id="deformField6" class=" form-control hasDatepicker"/>
<input type="hidden" name="__end__" value="datecolumn:mapping"/>
<script type="text/javascript">
deform.addCallback(
'deformField6',
function deform_cb(oid) {
if (!Modernizr.inputtypes['date'] ||"date" != "date" || window.forceDateTimePolyfill){
$('#' + oid).pickadate({"format": "yyyy-mm-dd", "selectMonths": true, "selectYears": true, "formatSubmit": "yyyy-mm-dd"});
}
}
);
</script>
There's 3 inputs there (and a fourth hidden one gets added in the rendered HTML with the name 'date_submit'). The visible input's value does not change when the user selects a new date, but the new hidden one's does. On a submit, somehow that value is passed on to the main 'date' input (some js which I don't have a handle on).
The resulting request.POST.items() contains these three elements from the date input:-
('__start__', 'datecolumn:mapping')
('date', '2017-02-24') (this is the newly selected date)
('__end__', 'datecolumn:mapping')
My issue here is that the order of these values is unreliable. On the same page and form, clicking submit multiple times will result in different orders of values in request.POST.items() (all values, not just the date related ones). If the resulting order is as above, things work fine, but if the order is different (say end is before start, or date is after or before both) then I either get a ValueError raised or a validation failure.
I could possibly iterate through request.POST.items() first and ensure the right arrangement, but I want two date pickers in this form, and I have no way of knowing which date field belongs to which pair of __start__ and __end__ markers.
Is there any way to control the order of elements in a POST before it's sent to my view code?
The hack I came up with (will accept any better answers that this!) involves two parts.
Tag each date element (I name all my date elements as *_date) uniquely in my template.
Use the template to re-generate the appropriate POST items.
Specifically, for step one I generate the serialized widget using this function:-
def serialize_with_formid(form, elem_name, formid):
retval = form[elem_name].serialize()
retval = retval.replace('<input type=', '<input form={} type='.format(formid))
retval = retval.replace('<textarea', '<textarea form={}'.format(formid))
if elem_name.endswith('date'): # 'Mark' datepick elements appropriately
retval = retval.replace('name="date"', 'name="{}"'.format(elem_name))
return retval
The form is the created form instance, elem_name ends with 'date' for datepicker elements, and formid is unique to each form on the page (of which there are many in my page). Lines 2 and 3 insert formid where appropriate, and the if marks datepicker elements.
For step 2 I first remove all __start__ and __end__ elements from request.POST, then find the elements in request.POST which end with date and use them to append (in the right order) elements to request.POST.
def fix_broken_POST(request):
'''
For some reason I'm getting POST elements out of order with retail rendered
form-attribute reliant forms. This reorders the crucial elements (date-related)
so that deform can then do validation etc.
'''
while True:
try: # Assumes same number of both
request.POST.pop('__start__')
request.POST.pop('__end__')
except KeyError as e:
break
list_of_date_keys = [i for i in request.POST.keys() if i.endswith('date')]
list_of_tuples = []
for key in list_of_date_keys:
value = request.POST.pop(key)
list_of_tuples.append(('__start__', '{}:mapping'.format(key)))
list_of_tuples.append(('date', value))
list_of_tuples.append(('__end__', '{}:mapping'.format(key)))
request.POST.extend(list_of_tuples)
Again, this is obviously a hack/workaround, so something a bit more... elegant would be appreciated.

python database table linking

I'm new to Python and I'm trying to make a simple bulletin board system app using web2py. I am trying to add a post into a certain board and I linked the post and board by including the following field in my post table: Field('board_id', db.board). When I try to create a post inside a particular board it gives me an error: "OperationalError: no such column: board.id". My code for create_posts:
def add_post():
board = db.board(request.args(0))
form = SQLFORM(db.post)
db.pst.board_id.default = db.board.id
if form.process().accepted:
session.flash = T('The data was inserted')
redirect(URL('default', 'index'))
return dict(form=form, board=board)
When I try to do {{=board}} on the page that shows the posts in a certain board, I get Row {'name': 'hi', 'id': 1L, 'pst': Set (pst.board_id = 1), 'description': 'hi'} so I know it's there in the database. But when I do the same thing for the "add post" form page, it says "board: None". I'm extremely confused, please point me in the right direction!
There appear to be several problems with your function. First, you are assigning the default value of the board_id field to be a Field object (i.e., db.board.id) rather than an actual id value (e.g., board.id). Second, any default values should be assigned before creating the SQLFORM.
Finally, you pass db.post to SQLFORM, but in the next line, the post table appears to be called db.pst -- presumably these are not two separate tables and one is just a typo.
Regarding the issue of {{=board}} displaying None, that indicates that board = db.board(request.args(0)) is not retrieving a record, which would be due to request.args(0) itself being None or being a value that does not match any record id in db.board. You should check how you are generating the links that lead to add_post and confirm that there is a valid db.board id in the first URL arg. In any case, it might be a good idea to detect when there is no valid board record and either redirect or display an error message.
So, your function should look something like this:
def add_post():
board = db.board(request.args(0)) or redirect(URL('default', 'index'))
db.pst.board_id.default = board.id
form = SQLFORM(db.pst)
if form.process(next=URL('default', 'index'),
message_onsuccess=T('The data was inserted'))
return dict(form=form, board=board)
Note, if your are confident that links to add_post will include valid board IDs, then you can eliminate the first line altogether, as there is no reason to retrieve a record based on its ID if the only field you need from it is the ID (which you already have). Instead, the second line could be:
db.pst.board_id.default = request.args(0) or redirect(URL('default', 'index'))

Generating random ID from list - jinja

I am trying to generate a random ID from a list of contacts (in Python, with jinja2) to display in an HTML template.
So I have a list of contacts, and for the moment I display all of them in a few cells in my HTML template by looping through the list of contacts:
# for contact_db in contact_dbs
<tr>
<td>{{contact_db.key.id()}}</td>
<td>{{contact_db.name}}</td>
<td>{{contact_db.phone}}</td>
<td>{{contact_db.email}}</td>
</tr>
# endfor
The view that renders the above is:
def contact_list():
contact_dbs, contact_cursor = model.Contact.get_dbs(
user_key=auth.current_user_key(),
)
return flask.render_template(
'contact_list.html',
html_class='contact-list',
title='Contacts',
contact_dbs=contact_dbs,
next_url=util.generate_next_url(contact_cursor),
)
Instead, I would like to display one contact, selected randomly by its ID, and it should display another contact with all its information every time the user refreshes the page (I am not dealing with displaying the same contact twice for now by the way).
I know that it is possible to use random in a python file to deal with random choices, so but not sure how it translates in jinja in the template.
Any help appreciated thanks!
There is a random filter in jinja2.
random(seq)
Return a random item from the sequence.
Use it like this:
{% set selected_contact = contact_dbs|random %}
note: I assumed contact_dbs is iterable.

Jinja2 Array Iteration Problems

I am getting some unusual behaviour with Jinja2 and iterating through an array which I can't quite put my finger on. I am passing a dictionary into Jinja2, one of the values of the dictionary is an array. A simple example of this works okay.
mydict = {
'testvar1': 'this is a test var 1',
'testvar2': 'this is a test var 2',
'testvar3': ['test','test','test','test','test'],
}
This is my Jinja template
{{ testvar1 }}
{{ testvar2 }}
{% for var in testvar3 %}
{{ var }}
{% endfor %}
Now this actually works, I get the below output
this is a testvar1
this is a testvar2
test
test
test
test
test
test
However, I am generating a dictionary using the CSV import utility. Now I am aware that this tool imports all values as strings, so I am separating the 'array field' with dashes so I can split them later on.
input_file = csv.DictReader(open("./my_path/import.csv"))
for row in input_file:
for key, value in row.iteritems():
if '-' in value:
value = value.split('-')
When I print both the array value from the dictionary created manually and the dictionary created by the csv import they look identical.
['test','test','test','test','test']
However when I generate the configuration using the dictonary populated by the csv import, the output I get is completely different.
testvar1
testvar2
t
e
s
t
t
e
s
t
And so forth...
Now it seems it is iterating over the values as if is were a simple string value, printing one character at a time for each iteration of the loop.
But it works perfectly when making the dictionary manually.
Any ideas? If there is a cleaner way to do this I'm all ears.
Thanks in advance.
My Problem was I had the wrong JSON syntax in my database.
{
"tags" : "[my, wrong, array]"
}
ended up beeing interpreted as unicode object from python which explains why iterating over it was iterating over every character.
the right syntax is:
{
"tags": [
"tech",
"python",
"flask"
]
}

Categories

Resources