Crazy behavior of `Making A “User Object” Available as a Request Attribute`

32 views
Skip to first unread message

neurino

unread,
Oct 17, 2011, 9:41:10 AM10/17/11
to pylons-discuss
I implemented the procedure [explained here][1] to always have a User
Object available as a request attribute.

It always worked good in development until I deployed it with Ubuntu -
MySQL

Using SQLAlchemy User object has a `items` relationship like this:

mapper(User, users,
properties={
'items': relationship(Item, backref='user')
})

The crazy thing is that if I delete one or more `user.items` elements
in a view I get it back refreshing the page even if item has been
actually removed from db.

Digging a bit and printing hex(id(request.user)) in templates I can
see there are 2 or more instances of User where only in one item has
been removed while it's still present in others.

I can't understand where's the problem and how to solve it.

Any help is really much appreciated.

Thanks for your support
neurino

[1] http://docs.pylonsproject.org/projects/pyramid_cookbook/dev/authentication.html

neurino

unread,
Oct 17, 2011, 10:17:05 AM10/17/11
to pylons-discuss
Well seems adding

DBSession.refresh(request.user)

at the beginning of each view relying on `request.user` voids the
problem.

Now I wonder what's the benefit form having such attribute rather than
querying each time for it in views.

Thanks for your support
neurino


> [1]http://docs.pylonsproject.org/projects/pyramid_cookbook/dev/authentic...

Parnell Springmeyer

unread,
Oct 17, 2011, 12:43:24 PM10/17/11
to pylons-...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I'm personally not a big fan of this solution; I always store the bare
minimum in my sessions (makes it much easier to scale particularly if
you're throwing sessions into memcached if you have to load balance your
application) - if I *must* get the raw user object, I make an actual
request for it but typically I don't need it.

My user objects are simple: id, email, password, date_created,
date_modified and a few other flags. The user id, and email including
some basic AUTH information are stuffed into the session.


This way if I'm doing something specific in the app I can take advantage
of Traversal and make the object we are working on a resource (which
gives it to you as a context object) - then, using SQLAlchemy's
relationships and lazy loading I can get any dependent objects.

neurino <neu...@gmail.com> writes:

- --
Parnell "ixmatus" Springmeyer (http://ixmat.us)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
Comment: GPGTools - http://gpgtools.org

iQEcBAEBAgAGBQJOnFssAAoJEPvtlbpI1POLE3AH/1WZf8XPocprcBzX/Yfp6K+X
7MngqDLulUCqCLdNnnloiwEKIc2VTGdKlzxviT5BnQs9b19tgm0yILXeeceVZZxt
uXNiByK1hGY3fs0btpRwLB7Ulv6Rdu88coNQRHiSM0ZTDoawLAOk+MGuNt72E0B5
JnHWtcg8TG4oityd6j/fyyUzNWQerS4VFIV5CncOx9pScSiSi13wddkdAmUoTbJx
C9lJPxPkTOQZF28ZFj3OYfe7LauGELcJW3KO70eX3AbDOtaVMZQUc23MSu2UVKTn
tqJapNB6cd2dJIzMe0fjGgYoPja1BFPxcsE0XbAaHYRcwZFptJqYqZZcyzA7UVs=
=mz/z
-----END PGP SIGNATURE-----

Michael Merickel

unread,
Oct 17, 2011, 1:24:23 PM10/17/11
to pylons-...@googlegroups.com
Okay, I can't tell if you are misunderstanding that cookbook recipe, or if you made the decision independent of that to store the user object in your *session*. That is not what the recipe is advocating. It advocates a mechanism to query the user the first time you access that property of the request object and cache the result in memory for the lifetime of that request.

Now if you decided that instead of querying the user from a database you would get it from a session then that's fine, but you need to understand that you are serializing a sqlalchemy object into a cookie. Thus when you go back to load that object from the cookie, you need to reconnect it with the database to make sure that it didn't change. This is done via the DBSession.refresh(). The point here is that either way is valid, but you *will* need to talk to the database once per-request if you want to use that object with SQLAlchemy.

Another option is to only serialize the properties of the user that you care about, then you can use those directly without having to talk to the database. Then you're making the assumption that those properties didn't change in the background.


--

Michael

neurino

unread,
Oct 17, 2011, 3:53:01 PM10/17/11
to pylons-...@googlegroups.com
What I guessed is my user attribute could be queried *once* *each time* a page is _requested_.

What I find is this does not happen as reloading a page — which happens to be returned by a different thread than the one that operated on the collection — user attribute is steady at a precedent state and *not retrieved again* upon new request.

Maybe I misunderstood the recipe but _why make a user available that way_ also for *any* attribute that can be edited?
As far as I know usually only userid is immutable and it's already available.
Every other property could lead to my same problem: user changes email then he finds it unchanged in next profile page...

So, is there an option for getting a new fresh user queried on each _page request_ (honestly I thought it was the same as _request_ but I see it's not)?

@Parnell: I *do need* full user data in each page as I need to show user items in sidebar. Choices.

Thank you for your support
neurino


--
You received this message because you are subscribed to the Google Groups "pylons-discuss" group.
To post to this group, send email to pylons-...@googlegroups.com.
To unsubscribe from this group, send email to pylons-discus...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/pylons-discuss?hl=en.

Parnell Springmeyer

unread,
Oct 17, 2011, 4:20:04 PM10/17/11
to pylons-...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Pyramid has an awesome event system - you could easily setup an observer
for any event after a request but before rendering that can query the DB
and provide the user object.

Or you can go the base class view setup, I make all of my views classes
that extend a base view class - the base view, on every request, gets
the user's id from the session and provides a user object "self.user"
that all extending classes have access to... You could even put it in a
context object if you wanted.

Michael said it right, re-read that email.

neurino <neu...@gmail.com> writes:

- --

Parnell "ixmatus" Springmeyer (http://ixmat.us)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
Comment: GPGTools - http://gpgtools.org

iQEcBAEBAgAGBQJOnI34AAoJEPvtlbpI1POLXuoIAJ2QojCcV4VDh0Dm0WAZoavk
2owq3lQPZ4LUA6Ucsznv9opmHRYWJqQ4vbrEZ74H3+MYtLnWMAdxFB3q2Bq5NpK3
q5vtjUlmBJN3TpXjqryGJ5SFfk3oUS556GyHBYiYVRsLnOgVLgsj0uuv4zqKTapF
HzpymB9AYw/FAA0p7mHMi5vtaOiHGgVAXilLtlgSyM1z9Ne+rmCCArQ4PMGJLVmW
u5+qx46iJMna0sLXF/PxB35UJZfAAYzQa5VScaqbozxxF422lHddB8sXRCCfTbbB
iSuzUWoSsxf5+ellOTnPzqKNpUo8PqwDE1unkJ86zl8nrhbO5giWkHkSblRgvkw=
=QM6x
-----END PGP SIGNATURE-----

neurino

unread,
Oct 17, 2011, 4:32:35 PM10/17/11
to pylons-...@googlegroups.com
I already set up some template helpers on before render, I could add user there but having it as a request attribute makes it available in all views too.

I'm not using views as classes but defs, I'd prefer not rewriting them all as classes

I'm reading Michael mail again and I assume one more time I was wrong thinking the lifetime of a request expires when the page is rendered and delivered and my request.user instance with it.

Do you confirm I was wrong? Any documentation to suggest to better understand how it works?

Thanks for your support
neurino

neurino

unread,
Oct 17, 2011, 4:42:02 PM10/17/11
to pylons-...@googlegroups.com
Reading one more time Michael mail about the recipe:


> It advocates a mechanism to query the user the first time you access that property of the request object and cache the result in memory for the lifetime of that request

*That's right the way I wanted it to work!*

So why if I reload a page my user is not queried again and stills to an older state?

I *don't want* at all to have session variables, I want a brand new queried request.user attribute each time the user loads its profile page.


Thanks for your support
neurino



On Mon, Oct 17, 2011 at 7:24 PM, Michael Merickel <mmer...@gmail.com> wrote:

Michael Merickel

unread,
Oct 17, 2011, 5:11:48 PM10/17/11
to pylons-...@googlegroups.com
You would have to paste code to explain why your user object isn't re-queried between requests, because that makes no sense. All the @reify decorator does is cache the object "within a single request", it doesn't affect other threads or other requests at all. All I can think of is that you are either looking at the output wrong or you are sharing a database session across threads.

--

Michael

neurino

unread,
Oct 17, 2011, 5:30:26 PM10/17/11
to pylons-...@googlegroups.com
Ok Michael,

thanks for confirming there's something wrong and simply I did not misunderstood.

The problem is that, in a single thread, `request.user` *survives from request to request* and is not affected by changes (like items deletion) occurring in other threads until a `DBSession.refresh(request.user)` is issued in that thread.

Here is my code:

#my session
DBSession = scoped_session(
    sessionmaker(extension=ZopeTransactionExtension()))

#my request class
class RequestWithUserAttribute(Request):
    @reify
    def user(self):
        userid = unauthenticated_userid(self)
        if userid is None:
            return None
        else:
            return DBSession.query(User) \
                .filter(User.userid==userid) \
                .first()

#set my custom request which provide user property
def main(global_config, **settings):
    ...
    config.set_request_factory(RequestWithUserAttribute)

============

My [apache modwsgi setup][1] where 4 threads are set.
Reducing to 1 thread (obviously) the problem disappears.

<VirtualHost *:80>
    ...
    # Setup mod_wsgi
    # Use only 1 Python sub-interpreter.  Multiple sub-interpreters
    # play badly with C extensions.
    WSGIApplicationGroup %{GLOBAL}
    WSGIPassAuthorization On
    WSGIDaemonProcess pyramid \
       user=your_username group=your_username \
       processes=1 \
       threads=4 \
       python-path=/home/your_username/bmh/env/lib/python2.6/site-packages
    WSGIScriptAlias / /home/your_username/bmh/env/pyramid.wsgi
    <Directory /home/your_username/bmh/env>
        WSGIProcessGroup pyramid
        Order deny,allow
        Allow from all
    </Directory>
</VirtualHost>

[1] http://docs.pylonsproject.org/projects/pyramid/1.0/tutorials/modwsgi/

I'll post any other code you may need.

Thanks for your attention
neurino




--

Michael

--

Rob Miller

unread,
Oct 17, 2011, 5:37:40 PM10/17/11
to pylons-...@googlegroups.com
On 10/17/11 1:42 PM, neurino wrote:
> Reading one more time Michael mail about the recipe:
>
> > It advocates a mechanism to query the user the first time you access
> that property of the request object and cache the result in memory for
> the lifetime of that request
>
> *That's right the way I wanted it to work!*
>
> So why if I reload a page my user is not queried again and stills to an
> older state?

Um, is it possible that you're just getting the same page from your
browser cache, and therefore you're not hitting your server at all?
Have you tried shift-reload to see if that changes the behaviour?

-r

>
> I *don't want* at all to have session variables, I want a brand new
> queried request.user attribute each time the user loads its profile page.
>
> Thanks for your support
> neurino
>
>
>
> On Mon, Oct 17, 2011 at 7:24 PM, Michael Merickel <mmer...@gmail.com
> <mailto:mmer...@gmail.com>> wrote:
>
> It advocates a mechanism to query the user the first time you access
> that property of the request object and cache the result in memory
> for the lifetime of that request
>
>

neurino

unread,
Oct 17, 2011, 5:51:11 PM10/17/11
to pylons-...@googlegroups.com
It has been the first thing I thought but then I added in my template:

    ${hex(id(request.user))}

near items <ul> and it showed there was 2 threads returning contents alernatively with a User instance each always with the same id.

In other words 2 User instances surviving through requests, each one managed by a single thread with its own persisting items collection.

Thanks for your attention.
neurino

On Mon, Oct 17, 2011 at 11:37 PM, Rob Miller <r...@kalistra.com> wrote:
On 10/17/11 1:42 PM, neurino wrote:
Reading one more time Michael mail about the recipe:

 > It advocates a mechanism to query the user the first time you access
that property of the request object and cache the result in memory for
the lifetime of that request

*That's right the way I wanted it to work!*

So why if I reload a page my user is not queried again and stills to an
older state?

Um, is it possible that you're just getting the same page from your browser cache, and therefore you're not hitting your server at all? Have you tried shift-reload to see if that changes the behaviour?

-r


I *don't want* at all to have session variables, I want a brand new
queried request.user attribute each time the user loads its profile page.

Thanks for your support
neurino



On Mon, Oct 17, 2011 at 7:24 PM, Michael Merickel <mmer...@gmail.com
<mailto:mmer...@gmail.com>> wrote:

   It advocates a mechanism to query the user the first time you access
   that property of the request object and cache the result in memory
   for the lifetime of that request


--
You received this message because you are subscribed to the Google
Groups "pylons-discuss" group.
To post to this group, send email to pylons-discuss@googlegroups.com.

To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/pylons-discuss?hl=en.
--
You received this message because you are subscribed to the Google Groups "pylons-discuss" group.
To post to this group, send email to pylons-discuss@googlegroups.com.
To unsubscribe from this group, send email to pylons-discuss+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages