Re: [sqlalchemy] history_meta.py: IndexError when creating a new object

2015-08-24 Thread Alex Fraser
Hi Michael,

On Monday, 24 August 2015 12:31:20 UTC+10, Michael Bayer wrote:

 yes and no.  Yes, if there's no added history, that should be skipped as 
 you're doing, but no in that that particular line of code is not called if 
 an object is being saved for the first time, only on an update, and then 
 only if that attribute actually had a change, which you are saying this 
 attribute did not.

 so if you can please share: 1. a mapping and an exact usage that 
 illustrates how this is happening 2. what exact version of SQLAlchemy are 
 you using and 3. have you modified the history_meta.py recipe in any way?


Oops, sorry for leaving that out the first time. I'm using SQLAlchemy 1.0.8 
on Python 3. I have modified *history_meta.py* for my app, but if I revert 
the changes the problem is still there.

It turns out that the problem happens when the session gets flushed twice. 
For example:

document = Document()
self.session.add(document)
self.session.flush()
document.name = 'Foo'
self.session.flush()
# IndexError: tuple index out of range

In my app I think I need to call flush several times, because I'm building 
a tree and I need to know the parent IDs. Perhaps I could rearrange my code 
to not need to do this.

Additionally, if I set a different name for a column than the attribute 
name (as shown below), the value doesn't get propagated to the history 
table.

description_ = Column('description', String, nullable=True)

See here for unit tests for both of these issues. It uses an unmodified 
*history_meta.py*.

https://github.com/z0u/satest/blob/master/test_versioned.py

Cheers,
Alex

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] history_meta.py: IndexError when creating a new object

2015-08-24 Thread Mike Bayer



On 8/24/15 10:15 AM, Alex Fraser wrote:

Hi Michael,

On Monday, 24 August 2015 12:31:20 UTC+10, Michael Bayer wrote:

yes and no.  Yes, if there's no added history, that should be
skipped as you're doing, but no in that that particular line of
code is not called if an object is being saved for the first time,
only on an update, and then only if that attribute actually had a
change, which you are saying this attribute did not.

so if you can please share: 1. a mapping and an exact usage that
illustrates how this is happening 2. what exact version of
SQLAlchemy are you using and 3. have you modified the
history_meta.py recipe in any way?


Oops, sorry for leaving that out the first time. I'm using SQLAlchemy 
1.0.8 on Python 3. I have modified /history_meta.py/ for my app, but 
if I revert the changes the problem is still there.


It turns out that the problem happens when the session gets flushed 
twice. For example:


|
document =Document()
self.session.add(document)
self.session.flush()
document.name ='Foo'
self.session.flush()
# IndexError: tuple index out of range
|

In my app I think I need to call flush several times, because I'm 
building a tree and I need to know the parent IDs. Perhaps I could 
rearrange my code to not need to do this.


Additionally, if I set a different name for a column than the 
attribute name (as shown below), the value doesn't get propagated to 
the history table.


|
description_ =Column('description',String,nullable=True)
|

See here for unit tests for both of these issues. It uses an 
unmodified /history_meta.py/.


https://github.com/z0u/satest/blob/master/test_versioned.py
great, thank you, these issues are both repaired as of 
d57e5edbcdf915168c613, the diff for his section is:


diff --git a/examples/versioned_history/history_meta.py 
b/examples/versioned_history/history_meta.py

index 6d7b137..866f2d4 100644
--- a/examples/versioned_history/history_meta.py
+++ b/examples/versioned_history/history_meta.py
@@ -210,13 +210,13 @@ def create_version(obj, session, deleted=False):
 a, u, d = attributes.get_history(obj, prop.key)

 if d:
-attr[hist_col.key] = d[0]
+attr[prop.key] = d[0]
 obj_changed = True
 elif u:
-attr[hist_col.key] = u[0]
-else:
+attr[prop.key] = u[0]
+elif a:
 # if the attribute had no value.
-attr[hist_col.key] = a[0]
+attr[prop.key] = a[0]
 obj_changed = True

 if not obj_changed:



Cheers,
Alex
--
You received this message because you are subscribed to the Google 
Groups sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send 
an email to sqlalchemy+unsubscr...@googlegroups.com 
mailto:sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com 
mailto:sqlalchemy@googlegroups.com.

Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


--
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Is it possible to set the constraint unique = True except for empty string into SQLAlchemy?

2015-08-24 Thread Jonathan Vanasco
If you're using Postgres, you actually wouldn't even need to use a partial 
index if you can save NULL values instead of empty strings -- a unique 
constraint would work as intended because Postgres does not compare NULL 
values to one another.

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] converting date @ hybrid property

2015-08-24 Thread Mehdi
After reading more documents i ended up using comparator for my hybrid 
property instead of expression. like:
class JalaliDateComparator(Comparator):

def operate(self, op, other):
input_date = self.__clause_element__()
g_date = other.togregorian()

return op(input_date, g_date)

So far it seems working, but i'll consider your solution too. i'm not sure 
which way is best practice but i'm happy that i have two solutions :)
Thanks for your reply.


On Monday, August 24, 2015 at 7:24:26 AM UTC+4:30, Michael Bayer wrote:


 On 8/22/15 4:30 PM, Mehdi wrote:

 Hi
 I'm using latest sqlalchemy with an Oracle db backend.
 I have a Date column which presents a *Gregorian date* in db. but i want 
 to query my table by a *Jalali date*.
 So the hybrid_property to convert gregorian date into jalali date should 
 be like this, i think:
 @hybrid_property
 def jalali_date(self):
 return jdatetime.date.fromgregorian(year=self.input_date.year,
 month=self.input_date,
 day=self.input_date.day)


 i've tried different ways, but all of them ends with errors like:
 AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' 
 object associated with MeasureData.input_date has an attribute 'year'
 or
 TypeError: an integer is required (got type InstrumentedAttribute)

 So i guess i have to get access to *instance value* of 
 *MeasureDate.input_date* at hybrid_property definition, but How?
 Do i have to completely change my approach?


 the hybrid allows a Python representation at the object level and a SQL 
 representation at the class level.  Your error message involves the term 
 InstrumentedAttribute, which suggests you are attempting to use this 
 hybrid at the class level (please provide full stack traces to make this 
 clearer). Your hybrid as defined only illustrates instance-level 
 conversions and there's not a simple way to do this conversion at the SQL 
 level especially in Oracle, unless you had some stored procedure which does 
 so. 

 Therefore, you are probably looking for an in-Python value of a Jalali 
 date to be converted from the alternate calendar to the gregorian 
 (SQL-persisted) calendar *in python*, before it is sent to the database, 
 and converted back to Jalali *in Python* after being received from the 
 database as a result.  For this, you want to build a custom type similar to 
 the MyEpochType illustrated at 
 http://docs.sqlalchemy.org/en/rel_1_0/core/custom_types.html#augmenting-existing-types
  
 - using this type will cause expressions like equality comparisons to 
 coerce the value side of the expression, e.g. the raw datetime object, as 
 according to your rules.  You can then cause this particular type to be 
 used with your hybrid using type_coerce.Here's an example using most 
 features of hybrids:

 from sqlalchemy import *
 from sqlalchemy.orm import *
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy.ext.hybrid import hybrid_property
 import jdatetime
 import datetime

 Base = declarative_base()


 class JDate(TypeDecorator):
 impl = Date

 def process_bind_param(self, value, dialect):
 if value is not None:
 value = jdatetime.date.togregorian(value)
 return value

 def process_result_value(self, value, dialect):
 if value is not None:
 value = jdatetime.date.fromgregorian(
 year=value.year,
 month=value.month,
 day=value.day)
 return value


 class A(Base):
 __tablename__ = 'a'
 id = Column(Integer, primary_key=True)

 input_date = Column(Date)

 @hybrid_property
 def jalali_date(self):
 return jdatetime.date.fromgregorian(
 year=self.input_date.year,
 month=self.input_date.month,
 day=self.input_date.day)

 @jalali_date.expression
 def jalali_date(cls):
 return type_coerce(cls.input_date, JDate)

 @jalali_date.setter
 def jalali_date(self, value):
 self.input_date = jdatetime.date.togregorian(value)


 e = create_engine(postgresql://scott:tiger@localhost/test, echo=True)
 Base.metadata.drop_all(e)
 Base.metadata.create_all(e)


 a1 = A(jalali_date=jdatetime.date(1380, 8, 2))
 a2 = A(input_date=datetime.date(2001, 10, 24))

 s = Session(e)
 s.add_all([a1, a2])
 s.commit()
 s.close()

 a1, a2 = s.query(A).filter_by(jalali_date=jdatetime.date(1380, 8, 2)).all()

 print a1.jalali_date, a2.jalali_date
 print a1.input_date, a2.input_date



 in the output, we can see that jalali_date and input_date are consistent 
 from both ways of setting, both ways of matching:

 1380-08-02 1380-08-02
 2001-10-24 2001-10-24








 Thanks.
 -- 
 You received this message because you are subscribed to the Google Groups 
 sqlalchemy group.
 To unsubscribe from this group and stop receiving emails from it, send an 
 email to 

Re: [sqlalchemy] history_meta.py: IndexError when creating a new object

2015-08-24 Thread Alex Fraser
Hi Mike,

On Tuesday, 25 August 2015 00:56:03 UTC+10, Michael Bayer wrote:

 great, thank you, these issues are both repaired as of 
 d57e5edbcdf915168c613, the diff for his section is:


Thanks very much! That works perfectly. By the way, you asked if I had 
changed *history_meta.py* at all: I added some properties on *Versioned* to 
allow conditional versioning:

--- src/app/server/history_meta_orig.py 2015-08-25 10:21:57.555888454 +1000
+++ src/app/server/history_meta.py  2015-08-25 10:23:08.342461090 +1000
@@ -157,6 +157,28 @@
 return mp
 return map
 
+@property
+def version_on_update(self):
+try:
+return self._version_on_update
+except AttributeError:
+return True
+
+@version_on_update.setter
+def version_on_update(self, enabled):
+self._version_on_update = enabled
+
+@property
+def version_on_delete(self):
+try:
+return self._version_on_delete
+except AttributeError:
+return True
+
+@version_on_delete.setter
+def version_on_delete(self, enabled):
+self._version_on_delete = enabled
+
 
 def versioned_objects(iter):
 for obj in iter:
@@ -249,7 +271,8 @@
 @event.listens_for(session, 'before_flush')
 def before_flush(session, flush_context, instances):
 for obj in versioned_objects(session.dirty):
-create_version(obj, session)
+if obj.version_on_update:
+create_version(obj, session)
 for obj in versioned_objects(session.deleted):
-create_version(obj, session, deleted=True)
-
+if obj.version_on_delete:
+create_version(obj, session, deleted=True)

The use case being that I only want to create a new version if the user 
saving an object is different from the user who last saved it. I also 
thought about adding an explicit *Versioned.create_version* method, but 
this suffices for now.

Cheers,
Alex

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


[sqlalchemy] history_meta.py: foreign keys in history table, and relationships

2015-08-24 Thread Alex Fraser
Hi Mike,

Thanks for your help with my other problem. I have a couple of other 
questions / a wishlist:

Is there a declarative way to add foreign keys to the history table when 
using history_meta.py? In the app I'm making the user can view old versions 
of a document, and I want to make sure e.g. the user that created the old 
version can't be deleted while their old versions exist.

Finally, it would be nice if other attributes could be carried over to the 
history mapper. If they had a similar API, the history objects could be 
worked with (e.g. serialised to JSON) in the same way as the non-history 
objects. Perhaps this could be done by carrying over methods decorated with 
@declared_attr? Something like this (untested):

def test_history_relationships(self):
class User(self.Base):
__tablename__ = 'appuser'
id = Column(Integer, primary_key=True)
name = Column(String)

class Document(Versioned, self.Base):
__tablename__ = 'document'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey(appuser.id))
contents = Column(String)

@declared_attr
def user(cls):
return relationship(AppUser)

self.create_tables()

user = User(name=Fred)
self.session.flush()

document = Document(user_id=user.id)
document.contents = foo
self.session.flush()

document.contents = bar
self.session.flush()

DocumentHistory = Document.__history_mapper__.class_
v2 = self.session.query(Document).one()
v1 = self.session.query(DocumentHistory).one()
self.assertEqual(v2.user.name, Fred)
self.assertTrue(hasattr(v1, 'user'))
self.assertEqual(v1.user.name, Fred)

Although it would be nice if it worked with other attributes too, such as 
hybrid properties. Maybe they would need to be specially decorated for use 
only by the history mapper, so that relationships weren't made 
bidirectional.

Currently I'm doing this by manually querying the related tables - but I 
feel this is prone to error, because code for doing the join is duplicated. 
E.g. instead of doing *user_name = v1.user.name*, I do:

user = self.session.query(User).filter_by(id=v1.user_id).one()
user_name = user.name

I feel like I'm doing something wrong; any suggestions for improving this 
would be appreciated.

Cheers,
Alex

SQLAlchemy 1.0.8, Python 3, PostgreSQL 9.4

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


[sqlalchemy] mapper_configured event, class fully configured?

2015-08-24 Thread Douglas Russell
Hi again,

I'm trying to track an issue making use of Marshmallow-SQLAlchemy (I've 
also seen what looks like a very similar problem in ColanderAlchemy, but I 
haven't dug into that yet). I think it might have something to do with the 
mapper_configured event and whether the class is truly fully configured at 
that point. Possibly I'm misunderstanding what that means.

If I do something like this:

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker, relationship
from sqlalchemy import event
from sqlalchemy.orm import mapper

engine = sa.create_engine('sqlite:///:memory:')
session = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()

class Author(Base):
__tablename__ = 'authors'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String)

def __repr__(self):
return 'Author(name={self.name!r})'.format(self=self)

class Book(Base):
__tablename__ = 'books'
id = sa.Column(sa.Integer, primary_key=True)
title = sa.Column(sa.String)
author_id = sa.Column(sa.Integer, sa.ForeignKey('authors.id'))
author = relationship(Author, backref='books')

def __repr__(self):
return 'Book(title={self.title!r})'.format(self=self)

Base.metadata.create_all(engine)

def print_stuff(mapper, class_):
print getattr(class_, 'books')

event.listen(mapper, 'mapper_configured', print_stuff)

author = Author(name='Chuck Paluhniuk')

When I run this code, the result is that when Author is first used, the 
'mapper_configured' event will fire and my print_stuff function callback 
will run.

The results is this:

Traceback (most recent call last):
  File marshmallow_experiment.py, line 37, in module
author = Author(name='Chuck Paluhniuk')
  File string, line 2, in __init__
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/instrumentation.py,
 
line 347, in _new_state_if_none
state = self._state_constructor(instance, self)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py,
 
line 747, in __get__
obj.__dict__[self.__name__] = result = self.fget(obj)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/instrumentation.py,
 
line 177, in _state_constructor
self.dispatch.first_init(self, self.class_)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/event/attr.py,
 
line 258, in __call__
fn(*args, **kw)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py,
 
line 2792, in _event_on_first_init
configure_mappers()
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py,
 
line 2691, in configure_mappers
mapper, mapper.class_)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/event/attr.py,
 
line 218, in __call__
fn(*args, **kw)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/events.py,
 
line 527, in wrap
fn(*arg, **kw)
  File marshmallow_experiment.py, line 33, in print_stuff
print getattr(class_, 'books')
AttributeError: type object 'Author' has no attribute 'books'

Should not the backref be configured at that point? What I'm trying to do 
is use the mapper_configured event in order to autocreate 
marshamallow-sqlalchemy schemas, which works fine except that because books 
doesn't exist at the point when the callback function runs, the resulting 
schema is also missing the books part.

If I do this instead:

author = Author(name='Chuck Paluhniuk')
print_stuff(None, Author)

Then the result is:

Author.books

I.e. It is now there.

Thanks again for your continued excellent support.

Douglas

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] mapper_configured event, class fully configured?

2015-08-24 Thread Mike Bayer



On 8/24/15 9:21 PM, Mike Bayer wrote:



On 8/24/15 7:41 PM, Douglas Russell wrote:


|
def print_stuff(mapper, class_):
print getattr(class_, 'books')

event.listen(mapper, 'mapper_configured', print_stuff)

author = Author(name='Chuck Paluhniuk')
|

When I run this code, the result is that when Author is first used, 
the 'mapper_configured' event will fire and my print_stuff function 
callback will run.


The results is this:

|
Traceback (most recent call last):
  File marshmallow_experiment.py, line 37, in module
author = Author(name='Chuck Paluhniuk')
  File string, line 2, in __init__
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/instrumentation.py, 
line 347, in _new_state_if_none

state = self._state_constructor(instance, self)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py, 
line 747, in __get__

obj.__dict__[self.__name__] = result = self.fget(obj)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/instrumentation.py, 
line 177, in _state_constructor

self.dispatch.first_init(self, self.class_)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/event/attr.py, 
line 258, in __call__

fn(*args, **kw)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py, 
line 2792, in _event_on_first_init

configure_mappers()
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py, 
line 2691, in configure_mappers

mapper, mapper.class_)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/event/attr.py, 
line 218, in __call__

fn(*args, **kw)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/events.py, 
line 527, in wrap

fn(*arg, **kw)
  File marshmallow_experiment.py, line 33, in print_stuff
print getattr(class_, 'books')
AttributeError: type object 'Author' has no attribute 'books'
|

Should not the backref be configured at that point? What I'm trying 
to do is use the mapper_configured event in order to autocreate 
marshamallow-sqlalchemy schemas, which works fine except that because 
books doesn't exist at the point when the callback function runs, the 
resulting schema is also missing the books part.


If I do this instead:

|
author = Author(name='Chuck Paluhniuk')
print_stuff(None, Author)
|

Then the result is:

|
Author.books
|
so you can see what's happening here.   Author.books is being set up, 
but not within the event.  In this case Author is being configured 
before Book, and as far as it knows, that's it, it's done.  The event 
is called.  Book hasn't been touched so therefore neither has 
books.   Only outside of the whole thing, where Book also got set 
up, does your inspection work.


The solution is to either use back_populates instead of backref, which 
is probably the only way the ORM would have done it today if starting 
again, or use the after_configured() event 
http://docs.sqlalchemy.org/en/rel_1_0/orm/events.html#sqlalchemy.orm.events.MapperEvents.after_configured 
which is intentionally here so that you can do things that require all 
known mappers to be fully set up, which is the case here.


As is so often the case I'm horrified the API docs don't have cross 
linking here.   doing that..
I've pushed up new descriptions and full cross-linking between 
configure_mappers(), mapper_configured(), before_configured() and 
after_configured(), for 0.9, 1.0, 1.1, master.  Should be up on the site 
in a little while.


--
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] mapper_configured event, class fully configured?

2015-08-24 Thread Mike Bayer



On 8/24/15 7:41 PM, Douglas Russell wrote:


|
def print_stuff(mapper, class_):
print getattr(class_, 'books')

event.listen(mapper, 'mapper_configured', print_stuff)

author = Author(name='Chuck Paluhniuk')
|

When I run this code, the result is that when Author is first used, 
the 'mapper_configured' event will fire and my print_stuff function 
callback will run.


The results is this:

|
Traceback (most recent call last):
  File marshmallow_experiment.py, line 37, in module
author = Author(name='Chuck Paluhniuk')
  File string, line 2, in __init__
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/instrumentation.py, 
line 347, in _new_state_if_none

state = self._state_constructor(instance, self)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py, 
line 747, in __get__

obj.__dict__[self.__name__] = result = self.fget(obj)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/instrumentation.py, 
line 177, in _state_constructor

self.dispatch.first_init(self, self.class_)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/event/attr.py, 
line 258, in __call__

fn(*args, **kw)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py, 
line 2792, in _event_on_first_init

configure_mappers()
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py, 
line 2691, in configure_mappers

mapper, mapper.class_)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/event/attr.py, 
line 218, in __call__

fn(*args, **kw)
  File 
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/events.py, 
line 527, in wrap

fn(*arg, **kw)
  File marshmallow_experiment.py, line 33, in print_stuff
print getattr(class_, 'books')
AttributeError: type object 'Author' has no attribute 'books'
|

Should not the backref be configured at that point? What I'm trying to 
do is use the mapper_configured event in order to autocreate 
marshamallow-sqlalchemy schemas, which works fine except that because 
books doesn't exist at the point when the callback function runs, the 
resulting schema is also missing the books part.


If I do this instead:

|
author = Author(name='Chuck Paluhniuk')
print_stuff(None, Author)
|

Then the result is:

|
Author.books
|
so you can see what's happening here.   Author.books is being set up, 
but not within the event.  In this case Author is being configured 
before Book, and as far as it knows, that's it, it's done.  The event is 
called.  Book hasn't been touched so therefore neither has books.   
Only outside of the whole thing, where Book also got set up, does your 
inspection work.


The solution is to either use back_populates instead of backref, which 
is probably the only way the ORM would have done it today if starting 
again, or use the after_configured() event 
http://docs.sqlalchemy.org/en/rel_1_0/orm/events.html#sqlalchemy.orm.events.MapperEvents.after_configured 
which is intentionally here so that you can do things that require all 
known mappers to be fully set up, which is the case here.


As is so often the case I'm horrified the API docs don't have cross 
linking here.   doing that..




I.e. It is now there.

Thanks again for your continued excellent support.

Douglas
--
You received this message because you are subscribed to the Google 
Groups sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send 
an email to sqlalchemy+unsubscr...@googlegroups.com 
mailto:sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com 
mailto:sqlalchemy@googlegroups.com.

Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


--
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.