Auth User split by tables

115 views
Skip to first unread message

rajmat...@gmail.com

unread,
Feb 1, 2016, 12:30:04 AM2/1/16
to web2py-users

Hello web2py community. I am a new user and I have to say, web2py saves the day by saving so much time over a full stack framework which requires so much work. Anyway, going to ask a question tothe community. How to custom create auth_user so instead of adding extra fields to the auth_user we can have multiple tables for the profile with various attributes. I am creating an application for gym membership and I need to have the user register based on their job, current address, prior fitness routines, current fitness routine, activities they are interested...etc. Put all in one tables with various fields seems make a table disproportionately long. Would be nice to split them to different tables and user can pick and choose which tables they like to fill up or they don't.

Ron Chatterjee

unread,
Feb 1, 2016, 12:14:30 PM2/1/16
to web...@googlegroups.com
I don't what exactly you are trying to do...but one thing you can do is create independent tables and process them in def user(): and create views accordingly at user.html. You probably want to "reference auth_user" based on your application. Others may have better suggestions though. 

Anthony

unread,
Feb 1, 2016, 12:25:43 PM2/1/16
to web2py-users
If there aren't too many fields, it might just be easiest to put them all in auth_user and just make some conditionally readable/writable depending on the type of user. Otherwise, you could create separate profile tables and have them reference auth_user. In that case, you would have to create your own register/profile actions, as web2py Auth won't handle the separate tables for you.

Finally, it might be possible for you to conditionally change the names of the Auth tables based on user type: http://web2py.com/books/default/chapter/29/09/access-control#Renaming-Auth-tables. However, you would have to be able to identify the user type prior to registration/login (e.g., based on some identifier in the URL), as the Auth table names must be determined in order to process any Auth actions.

Anthony


On Monday, February 1, 2016 at 12:30:04 AM UTC-5, rajmat...@gmail.com wrote:

Ron Chatterjee

unread,
Feb 1, 2016, 1:33:54 PM2/1/16
to web...@googlegroups.com
Thank you Anthony,

Very good discussion. Just one question regarding when you say we "have to create own register/profile"...

Let's say my model is:

#----------------------------------------------------------------------------------------------
db.define_table("Education",
                Field("education_of", 'reference auth_user', widget=SQLFORM.widgets.options.widget, requires= IS_EMPTY_OR(IS_IN_DB(db,db.auth_user.id))),
                Field("Title", "string", label='Education Title', requires=IS_NOT_EMPTY(),default=None))
#----------------------------------------------------------------------------------------------
controller:

  form = auth();
    form2 = SQLFORM(db.Education).process()
    if form2.accepted:
        db.Education.education_of.default = db.auth_user.id
    return dict(form=form, form2 = form2)

#-------------------------------------------------------------------------------------------------------------------------


We can make the education_of  as readable = False. So, it gets updated/inserted as auth_user.id

Do I still need to code up profile separately?  We can add other tables the same way. No?

Anthony

unread,
Feb 1, 2016, 1:58:41 PM2/1/16
to web2py-users
I just meant you won't be able to use the built-in auth.profile() functionality -- your example is custom code.

Anthony


On Monday, February 1, 2016 at 1:33:54 PM UTC-5, Ron Chatterjee wrote:
Thank you Anthony,

Very good discussion. Just one question regarding when you say we "have to create own register/profile"...

Let's say my model is:

#----------------------------------------------------------------------------------------------
db.define_table("Education",
                Field("education_of", 'reference auth_user', widget=SQLFORM.widgets.options.widget, requires= IS_EMPTY_OR(IS_IN_DB(db,db.auth_user.id))),
                Field("Title", "string", label='Education Title', requires=IS_NOT_EMPTY(),default=None))
#----------------------------------------------------------------------------------------------
controller:

  form = auth();
    form2 = SQLFORM(db.Education).process()
    if form2.accepted:
        db.Education.education_of.default = db.auth_user.id
    return dict(form=form, form2 = form2)

#-------------------------------------------------------------------------------------------------------------------------

And in view we add the education as a folding or something:

<div class="container">
  <button type="button" class="btn btn-info" data-toggle="collapse" data-target="#demo">Add Education</button>
  <div id="demo" class="collapse">{{=form2}}</div>
</div>
    

Ron Chatterjee

unread,
Feb 1, 2016, 3:47:54 PM2/1/16
to web2py-users
Thanks for the help Anthony. I appreciate that.

For some reason this below don't work:

 form = auth();
    form2 = SQLFORM(db.Education).process()
    if form2.accepted:
        db.Education.insert(education_of = auth.user.id)
    return dict(form=form, form2 = form2)


Or even if I do this:
db.Education.update_or_insert(education_of = auth.user.id) #basically updating the education with the auth.user.id (it shows up as None)

But strange enough, if I insert the Education.education_of from the table itself (from the admin), it works just fine.

Anthony

unread,
Feb 1, 2016, 3:53:30 PM2/1/16
to web2py-users
Calling .process will already do an insert into the db.Education table -- your code is inserting an additional record. Instead, before calling .process, set db.Education.education_of.default = auth.user_id. Also, the education_of field should not be readable or writable.

Anthony

Ron Chatterjee

unread,
Feb 1, 2016, 4:27:46 PM2/1/16
to web2py-users
Yes Anthony. Thank you again! It works just like you explained. 

Ron Chatterjee

unread,
Feb 1, 2016, 9:40:07 PM2/1/16
to web2py-users
Come to think Anthony, the best way to do it will be similar to this. Having hidden field and tables and once selected the user_type, all the other fields or tables shows up.

https://stackoverflow.com/questions/17380476/web2py-authenticating-two-types-of-profile

But the implementation is wrong.

hidden_fields = (user_extra_fields if request.args(1) == 'tutor') 

request.args(1) will never get satisfied like that. Need to process the table and then set the argument. I am thinking...but I could be wrong.


Anthony

unread,
Feb 2, 2016, 10:32:34 AM2/2/16
to web2py-users
On Monday, February 1, 2016 at 9:40:07 PM UTC-5, Ron Chatterjee wrote:
Come to think Anthony, the best way to do it will be similar to this. Having hidden field and tables and once selected the user_type, all the other fields or tables shows up.

https://stackoverflow.com/questions/17380476/web2py-authenticating-two-types-of-profile

But the implementation is wrong.

hidden_fields = (user_extra_fields if request.args(1) == 'tutor') 

request.args(1) will never get satisfied like that. Need to process the table and then set the argument. I am thinking...but I could be wrong.


In that particular question, the premise was that the user type would be specified in the URL (in request.args(1)) -- so I believe the proposed implementation would work in that case. If you cannot determine user type until after login, then yes, you would need an alternative setup.

Anthony

Ron Chatterjee

unread,
Feb 2, 2016, 2:43:17 PM2/2/16
to web...@googlegroups.com
So, In my application. I have two user type. User and Tutor.

Once they register, if user_type = 'User', I want it to go through the registration process and create a user_profile by redirecting to "user_profile". If user_type = 'Tutor', create a tutor_profile and get redirected

in my default controller, user(): I have:

form = auth();
   
    if form.accepted:
        if request.args(0) in ['register']:
            if (db.auth_user.user_type == 'User'):
                redirect(URL('user_profile'))
            else:
                redirect(URL('teacher_profile'))

But this don't work. It register and gets redirected to index.

I believe the problem is in using 

form = auth().

If I do:

form = SQLFORM(db.auth_user).process();

works fine but I loose all of my auth attributes.

Any idea how I can do both? And also make the new profile to work like auth_user means can be editable and updated? In other words, bake the cake and eat it too!?

Anthony

unread,
Feb 2, 2016, 3:45:12 PM2/2/16
to web2py-users
auth.register() automatically does a redirect after registration, so to do your own redirect, you should either pass a callback as the onaccept argument to auth.register() or specify the callback via auth.settings.register_onaccept.

Also, this condition:


if (db.auth_user.user_type == 'User'):

will not do what you want. Because db.auth_user.user_type is a Field object, the above is simply a Query object (and since a Query object is not falsey in Python, the above will always evaluate to True).

Instead, assuming you use the onaccept callback, note that the registration Form object gets passed to the callback, so you could do:

def after_registration(form):
   
if form.vars.user_type == 'User':
        redirect
(...)
    etc
.

Anthony

Ron Chatterjee

unread,
Feb 2, 2016, 4:45:17 PM2/2/16
to web...@googlegroups.com
You are the best Anthony! I did this:

auth.settings.register_onaccept = lambda form: after_registration(form) in my model 

in my controller worked fine.

Now all I want to do is append my menu with user profile (see attached). But user profile will have the same attributes like regular profile. Means if the user profile has already been created it will have all the inserted texts and user can modify/edit and then do "apply changes". Rather than having another link to "update profile". Wish you can point me somewhere . lol. 

I guess I can do this:
But that kills all the style. Then I got to custom style it.


Thank you for all the help! Appreciate it. 
menu.png

Anthony

unread,
Feb 2, 2016, 6:29:19 PM2/2/16
to web2py-users
I guess I can do this:
But that kills all the style. Then I got to custom style it.

Just look at the HTML generated by auth.navbar() and replicate that HTML, just making the changes you need in the links.

Anthony

Ron Chatterjee

unread,
Feb 5, 2016, 10:44:58 AM2/5/16
to web2py-users
This may be trivial but how do I get the html code out of auth.navbar()? It shows up as a gluon object.

Anthony

unread,
Feb 5, 2016, 11:19:29 AM2/5/16
to web2py-users
In the shell, just print it, pass it to str(), or call its .xml() method.

Or just load it in the browser and "view source".

Anthony

Ron Chatterjee

unread,
Feb 5, 2016, 11:58:23 AM2/5/16
to web2py-users
yes got it. Thank you. sorry. lol

Ron Chatterjee

unread,
Mar 6, 2016, 6:59:20 PM3/6/16
to web...@googlegroups.com
I have the auth_user split up between employer and employee

After the profile is created I want to be able to modify the employee profile.


So, I have this in my model

auth.settings.profile_onaccept = lambda form: after_profile(form)

In my controller:

def after_profile(form):
    if form.vars.user_type == 'employee':
        record = db(db.employee.employee_profile== auth.user_id).select()
        form = SQLFORM(db.employee,record[0].id,deletable=True, submit_button='Update Profile').process()
        if form.accepted:
            session.flash = T(profile is modified')
            redirect(URL('default','index'))
    else:
        redirect(URL('default','index'))
    return dict(form = form)

request.args(0) is 'profile'.

How to make the SQLFORM to make it editable for that profile/user_id? It doesn't work the way it is.

Anthony

unread,
Mar 7, 2016, 8:26:03 AM3/7/16
to web2py-users
This doesn't quite make sense. You are processing one form, and in the callback, you overwrite the "form" variable with an entirely new form object, attempting to process it immediately without the user having seen or submitted it. I think you need a different strategy.

Two other tips:
  • If your callback already has the proper signature, there is no need to wrap it in a lambda -- just do auth.settings.profile_onaccept = after_profile.
  • If you have the Row object of a record, just pass that as the "record" argument to SQLFORM -- if you pass the id, SQLFORM will then do an additional query to get the Row object.

Anthony

Ron Chatterjee

unread,
Mar 8, 2016, 7:17:27 AM3/8/16
to web2py-users
This works fine.

def after_profile(form):
    if form.vars.user_type == 'Job_Seeker':
        redirect(URL('default','edit_profile'))
    return locals()

def edit_profile():
    db.employee.employee_profile.default = auth.user_id

    record = db(db.employee.employee_profile== auth.user_id).select()
    form = SQLFORM(db.employee,record[0].id,deletable=True, submit_button='Update Profile').process()
    return dict(form = form)


But I agree with you Anthony. Its probably overkill since I already for the signature. I will also try the other you suggested. You are the best Anthony!
Reply all
Reply to author
Forward
0 new messages