Webpy For Simplest Thing
Trying WebPy For Simplest Thing, coming from Pyramid For Simplest Thing.
May06'2012: Install into same VirtualEnv. Also easy_install pysqlite.
- take copy of
mf.dbcreated by Django. - run
code.pyHello World fine (May07)- also fine once tweak for template
try db section, using SQLite
- get unintelligible error
- realize I'm using wrong table name (users instead of Django-created
auth_user), switch to using tabletasks - same error:
<type 'exceptions.KeyError'> at /tasks - hit SQLite command-line, sniff around. Doh! That table is named
family_task. Tweak just the mapping of table-name, leave everything else as plaintasks. - still get same error - looking more closely it's
KeyError: u'/tasks' - doh! Problem was in my
urlslist! Fixed that, now works! - also map
/userstoauth_usertable - listing also works - next: figure out how to list query-results (
from family_task where parent_id is null)- done, just use
db.selectwithwhereargument
- done, just use
Next: combine some lists into an integrated page for my family-dashboard...
- getting single-record - use
db.select()[0] - do simple
db.selectto include multiple related lists (May08) - next: isolate base layout template bits
- first line of specific template needs to be the
$def with, then 2nd line can be$var title:(May13)
- first line of specific template needs to be the
Next: work in SQLite to figure out proper join to generate key list for that page
- this is going to be a self-outer-join!
- overall master tasks: {{{ SELECT id, name, description, sort_order FROM family_task WHERE master_id IS NULL AND parent_id IS NULL }}}
- status from family-specific list: {{{ SELECT id, master_id, status FROM family_task WHERE parent_id IS NULL AND family_id = $family_id }}}
- These 2 lists will intersect, but there can be exceptions on both sides. So will want a
FULL OUTER JOIN - SQLite does not support a
FULL OUTER JOIN! - So it looks like I need to do a UNION ALL or UNION.
- Actually, there are enough weird cases I'm thinking it makes sense to just do a couple separate queries and then use code to combine the results.
- hmm, in building up custom result-set list, discover
IterBetter instance has no attribute 'append'. So immediately convertdb.select()result into a list.- then when rendering an item in layout, have to use
obj["col"]instead ofobj.col - should I improve consistency by building a list of objects instead of a list of dictionaries? Nah.
- then when rendering an item in layout, have to use
Next: bring in BootStrap CSS, then figure out how to generate Drop Down of status options for each goal and have changes do AJAX update.
- make a
staticfolder, copy BootStrapassetsfolder inside it - copy top and bottom bits of BootStrap
starter-template.htmlintolayout.html, tweak some paths to/assets - looks a little nicer. Going to delay Drop Down plus more cosmetic stuff until after getting authentication stuff in.
Next: user authentication (Web Authentication), and maybe sessions.
- yikes, this hasn't been written yet.
- seems like a number of people are using jpscalleti's code so I'll give that a try. Of couse I still have auth-related tables in db from starting with Django For Simplest Thing. So I guess it's time to drop those tables and bring in the new ones...
- dump
mf.dbtomf.sql, edit sql file to remove Django bits. Also removefamily_prefix from every table (and tweak code that references those tables). - jpscaletti's schema generates some errors, so have to tweak
AUTOINCREMENTinstead ofAUTO_INCREMENT- add
AUTOINCREMENTto otheridfields
- add
- remove comments
- restore using
sqlite3 mf.db < mf.sql
- stick his code lines up where they belong. Launch. (May14)
- error
name 'app' is not definedbecause ofmysession = web.session.Session(app, web.session.DiskStore('sessions'))- need the rest of the sessions stuff. Copy from here, now existing pages load fine. - trying hitting
/loginURL - get 404 (not found) - try adding param to
settingsdict, no change. Setsettingsback to empty dict - try adding
@auth.protected()to a page method, hit that URL. It redirects me to/loginreturning 404 again. - try copying
login.htmltemplate to my app's templates directory - no difference. - I'm not the only one who's had this issue.
- emailed jpscaletti to ask for help. Try mucking around with
pdbbut can't figure out what to do, so just going to wait...
- dump
Back to some BootStrap CSS work...
- Copy header ribbon from
fluidexample. - Copy
sidebar-navsection fromfluid - Use
<table class="table table-striped">, addthead/tbodybits - next: status Drop Down-s - ideally want to do AJAX db update when user changes the drop-down status value for any given Goal. (May15)
- Looking through Head First Jquery, p427 (p465 of the PDF).
- but going to revisit user-authentication first...
User authentication - continuing from above. (May15)
- Haven't heard from jpscaletti.
- David Montgomery, who had similar problems last year, never got them solved.
- I'm going to try a couple hacks and see if they work...
- though maybe I could a map for
DBAuth.logininurlsbutDBAuthgets called afterweb.application(urls...)- maybe someone smarter can make that work... - so try more-manual approach
- add
loginmap tourlsto my ownloginclass, have that render my local copy of thelogin.htmltemplate. Result: yes, now get a nice login form. But submitting it does nothing. - smells like a slippery slope of copying and pasting huge amounts of stuff.
- add
- I guess I'll just have to start from scratch (and then steal tiny-bits-at-a-time from that code).
- I can pass an object or a dictionary to a template, but it seems like I can't use that in the base-layout template.
- When I tried passing a dictionary, then referencing
$content.user_block["user_name"]inlayout.html, I got error<type 'exceptions.TypeError'> at / - string indices must be integersand found local varscontent = <TemplateResult: {'__body__': u'\n<h1>Welcome</h1>\n<p>Sorry</p>\n', 'user_block': u'{'user_name': 'Sorry'}', 'title': u'Personal Finance for Busy Parents'}> - When I tried turning this into an object instead and referenced
$content.user.user_nameI got error<type 'exceptions.AttributeError'> at / - 'unicode' object has no attribute 'user_name'with local vars<TemplateResult: {'__body__': u'\n<h1>Welcome</h1>\n<p>Sorry</p>\n', 'user': u'<code.User instance at 0x101e2e290>', 'title': u'Personal Finance for Busy Parents'}> - but wiki example shows that base layout can have logic, so I don't have to be totally stupid about it.
- yes now have some toggling logic in the base layout.html (May16)
- When I tried passing a dictionary, then referencing
- so, to have the login/register logic bits in
layout.html, it looks like (May17)- every view-generating method needs to
- be defined like
def GET(self, user_name = None):(to set default user_name) - include {{{ if session.has_key("user_name"): user_name = session.user_name }}}
- be defined like
- the template must start with something like
$def with (form, user_name)so it can pass that user_name to the baselayout.html - the base
layout.htmlthen usescontent.user_name
- every view-generating method needs to
- or maybe I can simplify by copying this approach
- before doing all that decide to revive my use of Mercurial. Do init/add/commit of this directory. (May19)
- Move
if session.has_key("user_name")to top level, moverender = web.template.render('templates/', base='layout', globals={'user_name': user_name})right after that, take anydef(user_name)out of templates, change base template to use$user_nameinstead of$content.user_name. Looks like it works! (May23)
- I can pass an object or a dictionary to a template, but it seems like I can't use that in the base-layout template.
- Playing with
registerform.- Now that I'm controlling this more directly, what should the user think of as his ID? Probably email address. (If I wanted to be fancy, I'd let a user associate multiple email/password pairs with his account, since users tend to forget what address they used...)
- realize that
dbauthlibrary didn't have the form/wrapper to call thecreateUser()function, so start creating HttpPost target. - read some Jeff Atwood security notes (see OWASP page), decide to use BCrypt method from
dbauth - made bunch of changes. Now when hit any page get
<type 'exceptions.TypeError'> at / - 'dict' object is not callabletriggered byapp = web.application(urls, globals())- is that an issue related to setting aglobalsdictionary to be used by base layout?- hrm, like this guy, when I restarted the server the problem went away! (May24)
- except that every time I have some other little error, when I fix and then reload a page, I get that same error message and have to restart the server. Maybe something with session?
- hrm, like this guy, when I restarted the server the problem went away! (May24)
- progress - current status
- need to recheck whether I'm handling sessions right for reloading/debug mode.
- yes, I'm doing that fine. Though I was concerned about possible confusion between
web.configused by sessions code vsconfigused by dbauth stuff I copied, to I changed the latter toconfig_auth. Still get that dict-not-callable error after fixing some other error.
- yes, I'm doing that fine. Though I was concerned about possible confusion between
- I know I'm not actually checking for the CSRF token.
- Urgh, first had to fix example to have form include straight param by name, not as function call.
<input type="hidden" name="csrf_token" value="$csrf_token"/> - submits always getting rejected because
session.pop('csrf_token',None)always gives None. - But if I take it out and just look for
session.csrf_tokenthen when there isn't one I get a<type 'exceptions.AttributeError'> at /register'Threaded Dict' object has no attribute 'csrf_token'- so I definitely need a safe wrapper for that get, so I put back in thesession.popbit (behaving same as when it was there before) - decide to take the debug/reload bit out of the equation, so put in a
web.config.debug = False- still getting same error!
- Urgh, first had to fix example to have form include straight param by name, not as function call.
- if try to insert dupe, get error msg - need to pass it to user
- even though (supposedly) adding user_name to session, it's not appearing anywhere. Is there a way to display the session data?
- need to recheck whether I'm handling sessions right for reloading/debug mode.
Feeling like solving too many generic problems here. Raise the idea of taking an existing sample app and adding lots of these cookbook pieces, hosted on GitHub for moving forward and sharing. (May28) So go on tangent: Extending Webpy Blog App With Cookbook Features.
- May30: hit wall with CSRF just like for my own app. Ask people for help over there.
So on this side, turn off the CSRF protection for the form. (Also have the session-with-reload stuff commented out, using debug=False instead.) Confirm can now do a register.
- May31 update: CSRF resolved - put back in.
But user_name still never showing up anywhere. Let's try something simpler: modifying the /hello/{user_name} handler to set session.user_name.
- playing around gets me toward thinking I'm assuming that the main body of code gets executed for every hit, but that's probably a big mistake....
- change from {{{ globals = {'user_name': user_name, 'csrf_token': csrf_token()} render = web.template.render('templates/', base='layout', globals=globals) }}}
- to {{{ render = web.template.render('templates/', base='layout', globals={'user_name': user_name, 'csrf_token': csrf_token()}) }}}
- this makes no difference
- (update: changed call again based on CSRF changes above - still no change in outcome)
- even simpler case: have a
/testhandler which outputs viareturnwithout a template. Have that includesession.user_nameand it properly outputs the value I set with/hello/Bob
Jun13: integrating learnings from WebpyForSimplestThing re sessions, CSRF, etc.
Jun23: moving register/login logic forward
- hmm, user_password doesn't look like it has the salt stored in it
- Jul22: hmm even weirder it doesn't seem like the expected BCrypt function is even being called! (Something weird in the indirection being used from dbauth.) So call that
hashBcrypt()function directly, and get an error atimport bcrypt! So have to figure out where that is.- find bcrypt folder inside
cryptaculardirectory, but it's possible I downloaded that last week - really not sure. But it's not getting found either way. - ah, looks like I got
cryptacularwith/because-of theshootoutdemo app for WebPy. - clearly the dbauth code wasn't using cryptacular. So changing the stuff I stole to use cryptacular.
- find bcrypt folder inside
registeris now storing correct-looking hash value in db for user_password- and
loginis now working right. Also displaying error messages. But those messages aren't red, and it's not doing abackto the form which would save my values. So no ideal, but ok for now. - next: back to actual app logic. (Getting this auth stuff put back into Extending Webpy Blog App With Cookbook Features will have to wait.)
- also need to work on some authorization code!
Jul24: app logic
db.select()returns anIterBetterobject, which has nolen()- if you just want to check where there are any rows returned or not, test
bool(rows) - but if you want to check for some number of rows other than 0 or 1, then you need to convert with
rows = list(rows)thennum = len(rows).
- if you just want to check where there are any rows returned or not, test
Jul30 - rendering some textarea/blog user-data with Mark Down, following notes here, though it looks like safemarkdown is in web.utils. Hitting a wall, [posted](https://groups.google.com/forum/?fromgroups#!topic/webpy/Qeg Ng Jy Iv7c) a question to the forum.
- update: got answer that works. You should use
$:safemarkdown(note.description)-$fooalways escapes the value.$:foorenders it verbatim.
Aug01: form.fill(record) fills in current record values for an Edit form.
Aug02: by default, WebPy forms do an HttpPost back to the same page/form URL, because they have no action specified. This becomes an issue when you want to have more than 1 form on a page, since they both end up posting to the same URL. But actually, this is just a convention, because the Form code doesn't generate the form container tag at all! So by convention people don't put any action in the tag, so it falls back to the page URL. So you can easily manually add an action pointing elsewhere for 1 of those forms.
(Have been working on a lot of app-domain-specific stuff, so nothing bloggable.)
Sept25: using an "ApplicationProcessor" to check for all my non-static URLs that user is logged in.
Sept28: generating/sending HtmlEmail
- make a
render_email()variety that doesn't use the background template wrapper - remember that any links need an absolute reference
- very simple to use GMail to send the emails.
Oct12 - make forms look nice by modifying WebPy's /web/form.py to use BootStrap styles.
Edited: | Tweet this! | Search Twitter for discussion

Made with flux.garden