How use Flask LinkCol on database query data results? - python

I have been working with Flask for a little while and developed a database manager application for managing internal data for the department I work in (I am not a "real" developer but have learned by doing/necessity). The application works great as a basic CRUD app. In it I have used Flask forms and tables including the LinkCol column for linking to different functions within the app. In those cases, I am using an 'Edit' or 'Delete' clickable link; meaning every column in the table has an extra cell with that word. Clicking either of those takes the user to the corresponding page where they can take appropriate action on that item.
Currently I am trying to use LinkCol so that data returned from the database can be the clickable link that links to another function rather than every table having an additional column (for example, "Go to Account"). So, when my table populates with an account number, the user can click the actual account number in the table to go to a different page to view additional data about that specific account. I read the docs and looked at the examples on the creators page regarding overwriting things in the classes but haven't been able to figure it out. I have written a few of my own classes, but am not an expert on the matter by any means. I also have been unable to find anything else that might help including on SO which is always my go to resource for figuring things out.
Here is my table definition using a LinkCol that renders incorrectly and puts 'Account #' in each cell instead of the dynamic data I would like. Changing to Col gives correct display but I want it to be clickable:
class AccountsResults(MainTable):
AccNo = LinkCol('Account #','dbhome_bp.route.account_detail', url_kwargs=
dict(AccNo='AccNo',StatusCode='Active',ServiceAddress1=\
'ServiceAddress1'))
TnCount = Col('Total TNs')
ServiceAddress1 = Col('Service Address')
ServiceCity = Col('Service City')
ServiceZip = Col('Service Zip Code')
MasterServiceDate = Col('Master Service Date')
Active = Col('Currently Active')
Here is an example of what I know I can do versus what I would like to do.
Can do ('Go To Account' is clickable in the Go To Account column):
Go To Account
Account #
Service Address
Etc.
Go To Account
12345678
1234 Anywhere
Yup
Go To Account
12345679
5678 Somewhere
Nah
Would like to do (Account numbers are clickable in Account # column ):
Account #
Service Address
Etc.
12345678
1234 Anywhere
Yup
12345679
5678 Somewhere
Nah
Does anyone know how to make the dynamic data returned from a query the clickable link for a LinkCol in Flask Table?
Any help is greatly appreciated.

Assuming an Sqlalchemy model Account with PK column id and a route defined as follows:
accounts = Blueprint('accounts', __name__)
#accounts.route("/accounts/<int:account_id>")
def detail(self, account_id):
_account = Account.get_or_404(account_id)
# show account details
return render('account-details.html', account=_account)
Define your account link column as follows:
account = LinkCol(
name='Account #',
attr='id',
endpoint='accounts.detail',
url_kwargs=dict(account_id='id'),
)
The parameter meanings are as follows:
name # the column header text
attr # the name of the attribute of the account object to render as the text in the <td> cell
endpoint # the endpoint to link to
url_kwargs # the arguments that get passed to the url_for function along with the endpoint
The links would be constructed as if you had done a url_for for each row in the table as follows:
_row_account_detail_url = url_for('accounts.detail', account_id=row_id)
Example Table definition:
class AccountTable(Table):
account = LinkCol(
name='Account #',
attr='id',
endpoint='accounts.detail',
url_kwargs=dict(account_id='id'),
)
service_address = Col(
'Service Address',
attr='service_address'
)
# other columns

Related

Flask: when to use session vs. storing user data in db?

I have a Flask application and need to store users' place when they navigate the content.
For example, I have a route like this: #main_bp.route('/articles/<category>/<article_number>', defaults={'category': 'new'})
The content is organized such that you page through articles under a category: starting at 0, then 1, and so forth. The URL for article number 3 would look like: articles/<category>/3
I'd like to save users' place so that if they leave the site after visiting article 3, when they navigate to the articles page they'll land on articles/<category>/3, rather than articles/<category>/0.
What is the best way to achieve this? Currently, I've modeled the data in the database so there is a column that looks like category_article_last_visited (integer). I'm able to store this data as a user browses the site, but I'm not sure how to retrieve it when they return to the articles page.
What I've tried:
#main_bp.route('/articles/<category>/<article_number>', defaults={'category': 'new', 'article_number':current_user.category_article_last_visited}), but I get an error that there is no such attribute.
Checking current_user.category_article_last_visited in the routes function and using the article number. This renders the correct content, but doesn't change the URL, which won't work.
Redirecting users if they have a value for current_user.category_article_last_visited. This doesn't seem to yield any change.
I am curious if storing in the db (assigning the value, db.commit(), etc.) is the right path, or if I should explore flask-sessions more. I need this information to persist across sessions, so that if a user logs out, clears cookies, uses a different device, etc. it is still available. I may also perform analytics on these values in the future.
Is the method I've described above the best way to achieve this? Is flask-sessions or something else preferable?
If the method outlined above is best, how do I correctly route this information so that users are directed to the page they left off, and the URL is changed appropriately?
Thanks
I would go with the redirect solution, it is more clear.
I would add an if statement at the beginning of the route-function and if there is data for this user, i would redirect to that page. For example:
#main_bp.route('/articles/<category>/<article_number>', defaults={'category': 'new'})
def routefunc():
if current_user.category_article_last_visited !=0: #or whatever your column keeps for empty data
return redirect ('/articles/'+yourcategory +'/'+ current_user.category_article_last_visited #as string
This must be combined with some other functionality, to avoid infinitive redirection to this route:
Option 1:
You can add another variable in the route that will have specific value on these redirections and will ignore this if statement. For example:
#main_bp.route('/articles/<category>/<article_number>/<check>', defaults={'category': 'new'})
def routefunc():
if current_user.category_article_last_visited !=0 and check!=1: return redirect ('/articles/'+yourcategory +'/'+ current_user.category_article_last_visited+'/1')
However in this case you must add this variable (with some other value different from 1) to all of your urls-hrefs etc and it will make your urls more "dirty". It would be effective for a small app, but i would avoid it for a big app/website with multiple internal links.
Option 2:
You could add one more column in your database table that will be 1/0 depending on when user visitis this route, directly or from redirection. In this case you must add a couple of queries to check and/or update this value before-after redirection.
Option 3:
You could create another similar route that will only handle redirections, and produce the same results (same html) but without the if statement. For example:
#main_bp.route('/articles/<category>/<article_number>', defaults={'category': 'new'})
def routefunc():
if current_user.category_article_last_visited !=0: #or whatever your column keeps for empty data
return redirect ('/articles2/'+yourcategory +'/'+ current_user.category_article_last_visited #as string
#main_bp.route2('/articles2/<category>/<article_number>', defaults={'category': 'new'})
def routefunc():
return ('yourhtml.html')
***Session based approach is not good here, as you want a long term solution.
As you probably have many categories, articles, users, you would better create a separate table specifically for this
I don't know what is the best way to achieve what you want but here's what you could try. Assuming you want to perform some analytics on the data you might want to store it in a database.
You could have a route designed to create a user cookie when a new user visits your page and redirects him to the articles page with the new cookie set:
#main_bp.route('/articles/set_cookie', "GET"])
def set_article_cookie():
sessionserializer = securecookiesessioninterface().get_signing_serializer(main_bp)
tempcookie = sessionserializer.dumps(dict(session))
resp = make_response(redirect('/articles'))
resp.set_cookie("user", tempcookie)
return resp
And your existing route in which you check if the user has already visited the page. In which case you will want to check in the database what was the last article he read and redirect him accordingly:
#main_bp.route('/articles/<category>/<article_number>', defaults={'category': 'new'})
def articles(category, article_number):
# If the user cookie is already set, check if there is some data is the database and redirect to his last article visited
cookie = request.cookies
if "user" in cookie:
# Retreive the user cookie value and check the database for this value
return redirect('/articles/' + last_article_visited)
# Else redirect the user to set_article_cookie
else:
return redirect("/set_article_cookie")
OK, here is the solution I decided on:
I update the paths of nav links throughout the site, so instead of /articles/<category>/0 it's /articles/<category>/current_user.article_number_last_visited
Since not all users have visited articles in every category, I added default routing logic, similar to:
#main_bp.route('/articles/<category>/', defaults={'article_number': 0})
#main_bp.route('/articles/<category>/<article_number>', methods=['GET', 'POST'])
This routes users correctly even if current_user.article_number is null.
I believe this will also work if the user is not logged in (and therefore there will be no article_number attribute). I haven't checked this case out thoroughly though because in my use case users have to be logged in to view content.

How do I make buttons with identical text in flask table?

I have the following table set up using flask-table
class ScreeningTable(Table):
timedate = DatetimeCol('Date & Time', datetime_format='YYYY-MM-d, HH:MM')
tickets = Col('Tickets available')
I want to add a ButtonCol using a screeningID variable, where every button has the same text, but sends the user to a link based on the value of screeningID for each line of the table. How do I do that?
This is the line I came up with:
screeningID = ButtonCol("Buy Tickets", "purchase", url_kwargs=dict(id='screeningID'))
To clarify, the first variable is the title of the header, which will also be the text on the button. The second one is the endpoint, and the third is the keyword argument, what gets pass over as part of the URL.
The same rules apply with LinkCols, as they were written to work identically.

Stripe API: filter for Payout ID when I have destination ID (bank ID)

I'm using python. I am trying to retrieve all payouts to a certain destination.
The code for retrieving everything is:
stripe.Payout.list(limit=3)
How do I add a parameter containing the destination ID? (Destination ID is in this form: ba_XXXXXXXXXXXXXXXXX)
I actually figured it out. This is not included in stripe API documentation. Just add a field like this:
stripe.Account.retrieve(destination = "ba_XXXXXXXXXXXXXXXX")
The only problem is this doesn't allow NOT filters. I can't do !=. It has to be = only. I haven't tried with > or <. Can anyone help me with this?

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'))

Get by name in google app engine

Instead of using get_by_id() method for getting the id of a specific entry and print the content of this entry from the google datastore, i am trying to get the name of the url and print the content. For example:
print all the content that have this specific name(may have more than one rows of content with this name)
print the content of the specific id
i am using get_by_id(long(id)) to get the id in the second part of my example, and its working. I am trying to use get_by_key_name(name) but it does not working. any ideas on that? thank you.
sorry, but since i couldn't leave a comment, i am editing my question. Basically, since now i can get all the name of animals from my datastore and i have made them clickable using an html code in template file. In the datastore, there are entries with the same name of animal more than one times (e.g. name= duck, content= water and name=duck, content=lake). Now, when i am clicking into every name of animals(i have use the DINSTINCT in my gql query to print redundant elements(e.g. duck) only one time).Since the name=duck has two contents, when i am clicking on the name of the duck i want to see both of the contents. My problem is if i am using get_by_id(long(id)) i get the unique id of every element. But this will not print me both of the content of the name=duck because every entry has a unique id. But i want all the content of the entries with the same name. I am trying the following but it does not working.
msg = MODEL.Animals.get_by_key_name(name)
self.response.write("%s" % msg.content)
With get_by_id() you can get entity only if you know this ID. This operations named "Small operations" in quota and they are cheaper than datastore reads, but to get list of entities filtered by indexed property - you should use filters.
query = MODEL.Animals.query()
query = query.filter(MODEL.Animals.name == 'duck')
ducks = query.fetch(limit=100) # limit number of returned animals
for duck in ducks:
self.response.write('%s - %s' % (duck.name, duck.content))
By default, all string properties are indexed, so you will be able to do such requests.

Categories

Resources