[orm-devel] Access to attributes before a query

Mike Watkins mw@mikewatkins.net
Sat, 16 Nov 2002 13:32:08 -0800 (PST)


Diedrich Vorberg said:
> I can see your point however, that this seems a little absurd
> considering that the class' (=the tables) attributes (columns) have been
> previously defined using the columns dict.

[Warning, half backed ideas coming]

Yes - that's where I'm coming from. It would be nice to extend the class
so that it can be used a little more naturally both before there's an
'instance' of it in the DB and afterwards (which ORM does beautifully).

> Two questions to consider:
>
>   o if you expect an unset attribute to contain something, what should
>     it contain? None, 0 or "" or a previously defined default value?

My superficial thinking was to run through the columns() and initialize
all as class attributes to None.

>   o another problem I have thought about: SQL allows for the
>     definition of default values. Fixed default values would be no
> problem to implement, but what about values that call a
>     SQL function like NOW() or NEXTVAL(some_sequence)? Should we make  a
> SELECT after each INSERT to retrieve this kind of data from the  db?
> We have to do a SELECT to get the NEXTVAL() for SERIAL or  AUTO
> INCREMENT columns anyway.
>

And carrying on from there, inserts would ignore all attributes->columns
which remain None.

Updates aren't an issue with the current scheme, anyway...

>># populate a form with instance defaults or data if editing an existing
>> record
>>form.add_widget('hidden', 'id'  , value=group_id)
>>form.add_widget("string", "code", title="Code", value=g.code, size=20,
>> required=1)
>>self.add_widget("string", "name", title="Group name", value=g.name,
>> size=20, required=1)
>>self.add_widget("checkbox", "is_login_allowed",
>>value=num_bool(g.is_login_allowed), title="Allow login?")
>
> I've run accross the same problem. This is how my code looks:
>
> if item.oid(): # the oid() function returns a PostgreSQL OID value or
>                # simply 1 if we don't use PostgreSQL. The function  #
> should probably be renamed/aliased isStored() or so. # If
> the dbobj has not been inserted, yet, it will  # return
> None.
>   title = item.title
>   description = item.description
>   price = "%5.2f" % item.price
> else:
>   title = ""
>   description = ""
>   price = "0.0"
>
> # create form...

Yes that's certainly one option although it sure adds a lot of code. It
would be great to use the class whether its new (never stored) or an
existing object. That would feel more pythonic.

> Also, bear in mind that
>
>    g = Groups()
>    ds.insert(g)
>
> results in an SQL query as
>
>    INSERT INTO item () VALUES ()
>
> which simply won't work. You need to populate the instance (dbobj) with
> some data to get a valid query. The reason I did it this way is, we
> can't just insert NULL for an unset column, because there is a
> difference in inserting NULL and insering nothing. Take a relation like
> this for instance (this is PostgreSQL btw):
>
>    CREATE TABLE TEST
>    (
>      id SERIAL,
>      test VARCHAR(20),
>      created TIMESTAMP DEFAULT NOW()
>    );
>
> and then do
>
>    INSERT INTO test (test, created) VALUES (NULL, NULL);
>    INSERT INTO test (test) VALUES (NULL);
>
> you'll get
>
>    test=# select * from test;
>     id | test |            created
>    ----+------+-------------------------------
>      3 |      |
>      4 |      | 2002-11-16 18:00:08.602113+01
>    (2 rows)
>
> the first created an all-empty column, the latter did what we wanted.
>
> Any comment or suggestions on how to deal with this kind of problem  is
> very welcome!

I agree - still thinking that attributes set to None would be ignored -
maybe we need an ORM "null" type

eg
g.name = 'foo'
g.access = orm.NULL
(and the other attributes g.created, g.modifed would be still set to None,
and therefore skipped on the initial insert, re-read at that point where
they would inherit whatever the DB record actually reflects then)

?