Kent wrote:
> I am on version 0.5.8.

part of your issue is this:

line1.lineid = '15'

should be this:

line1.lineid = 15

This because the value comes back from the DB as a numeric, not a string,
producing the wrong identity key ( (<class '__main__.OrderDetail'>,
('0206001A134', '15')) vs (<class '__main__.OrderDetail'>,
(u'0206001A134', 15)) ).


The merge then issues the eager load for the lead order + 1 detail, and a
second select for the additional detail.





>
> As far as how upset it is making me: well, I certainly have no right
> to demand this very nice, free software be enhanced or changed: I'm
> just grateful for it.
>
> We will be supporting clients on webservers that are removed by a long
> distance from the database server, so I would like to limit the round
> trips as much as is feasible...
>
> I've taken out most everything and left the logic in a simple case to
> create the behavior.  Here is the script that will demonstrate:
>
> =============================================================================
>
> from sqlalchemy import *
> from sqlalchemy.orm import *
>
> engine = create_engine('postgres://dbuser:dbu...@localhost:5444/
> dbuser',echo=True)
> metadata = MetaData()
> Session = sessionmaker(bind=engine)
> session = Session()
>
> order_table = Table("orders", metadata,
>     Column("orderid", Unicode, primary_key=True)
> )
>
> orderdetail_table = Table("orderdetails",metadata,
>     Column("orderid", Unicode, ForeignKey('orders.orderid'),
> primary_key=True),
>     Column("lineid", Integer, primary_key=True),
>     Column("saleprice", Numeric, nullable=False)
> )
>
> class Order(object):
>     pass
>
> class OrderDetail(object):
>     pass
>
> order_mapper = mapper(Order, order_table,
>         properties=dict(orderdetails=relation(OrderDetail,
>                         cascade='all,delete-orphan',
>                         single_parent=True,
>                         lazy=False,
>                         backref=backref('parentorder',
>                                 cascade='refresh-expire,expunge'))))
>
> orderdetail_mapper = mapper(OrderDetail, orderdetail_table)
>
> metadata.create_all(engine)
>
> o=Order()
> o.orderid = '0206001A134'  #this order exists in the database - You'll
> need to set add it to the DB
>
> line1=OrderDetail()         #line exists in database - You'll need to
> set add it to the DB
> line1.orderid = '0206001A134'
> line1.lineid = '15'
>
> line2=OrderDetail()         #new line does not exist in database
> line2.orderid = '0206001A134'
>
> o.orderdetails = [line1, line2]
>
> #
> #
> # Question a above - the following merge results in 3 SELECT
> statements, but the first
> # is an eagerly loaded query joined with orderdetails.  So, unless the
> JOIN returned fewer rows
> # (for example, an inner join instead of outer was used), all the
> orderdetails should
> # already be in existence as persistent objects:
> merged=session.merge(o)
>
>
> merged in session.new    #this order exists in the database
>
> merged.orderdetails[0]
> merged.orderdetails[0] in session.new  # already in database (in new =
> False)
>
> merged.orderdetails[1]
> merged.orderdetails[1] in session.new  # not yet in database (in new =
> True)
>
> #
> # Question b:
> # Why does this issue another select?  The object should have been
> eagerly loaded,
> # but even if not that, it was later reSELECTED during the merge()
> merged.orderdetails[0].saleprice
>
>
> #
> # Question c:
> # Are there databases that allow part of a primary key to be undefined
> (None)?
> # That is a foreign concept to me, so I expected this object would
> realize it
> # needn't query the database.
> merged.orderdetails[1].saleprice
>
> =============================================================================
>
>
> Thanks in advance,
>
> Kent
>
>
>
>
>
> On Feb 8, 9:57 pm, Michael Bayer <mike...@zzzcomputing.com> wrote:
>> On Feb 8, 2010, at 4:12 PM, Kent wrote:
>>
>> > Ok, here are my questions:
>>
>> > a) The merge eagerloads the order, along with its lines, but then,
>> > directly afterward, issues two additional SELECT statements for the
>> > two lines, even though these were already eagerly-loaded.  That
>> > surprised me.  Why is that occurring?
>>
>> I dont know.  I would need more than code fragments to reproduce your
>> behavior.   (nor do I know what version you're on).   It doesn't
>> reproduce with a simple test.
>>
>>
>>
>> > b) When I ask for the property .saleprice on the order line, another
>> > SELECT statement is issued.  Why does that occur when it was eagerly
>> > loaded already?
>>
>> Same.  If the value is in __dict__ it would not issue another load.
>>
>>
>>
>> > c) In the case of line2, can SQLAlchemy be made to realize that part
>> > of the primary key is not set and therefore there is no reason to
>> > attempt a fetch from the database?  It already detected this was a new
>> > record during the merge.
>>
>> the "fetch for None", meaning issuing a fetch when the primary key was
>> completely None, was resolved in a recent 0.5 version, probably 0.5.8.
>>  However, a partial primary key is considered to be valid.   There is a
>> flag on the mapper() called allow_null_pks=True which in 0.5 is set to
>> False by default - it means that partial primary key is not valid.  
>>  That flag is not in fact checked by merge() in this case, which is
>> because the flag was already being removed in 0.6 by the time this fix
>> went into place.    The flag only led to confusion over and over again
>> when users mapped to outerjoins, and didn't receive rows.   Whereas
>> nobody ever complained about merge issuing a load for None as a key -
>> the issue was fixed because I noticed it myself.   So you're the first
>> person to ever complain about it, which is unfortunate since it may have
>> led to a different path for the allow_null_pks flag.   I dont like the
>> flag very much since its obscure and nobody really ever used it unless I
>> told them to directly.   The only other way would be for the mapper to
>> "guess" if nulls are allowed in the primary key based on the type of
>> table/join structure its mapped to.  So it depends on how upset this
>> behavior is making you if I really need to find some way for it to
>> differentiate between None-capable primary keys or not.
>>
>>
>>
>> > Thanks in advance.
>>
>> > Kent
>>
>> > --
>> > You received this message because you are subscribed to the Google
>> Groups "sqlalchemy" group.
>> > To post to this group, send email to sqlalch...@googlegroups.com.
>> > To unsubscribe from this group, send email to
>> sqlalchemy+unsubscr...@googlegroups.com.
>> > For more options, visit this group
>> athttp://groups.google.com/group/sqlalchemy?hl=en.
>>
>>
>
> --
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To post to this group, send email to sqlalch...@googlegroups.com.
> To unsubscribe from this group, send email to
> sqlalchemy+unsubscr...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/sqlalchemy?hl=en.
>
>

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

Reply via email to