Re: _guess_columns patch
Jacob Smullyan <[email protected]> Tue, 17 May 2005 13:50:51 -0400
| Newsgroups | gmane.comp.web.skunkweb |
|---|---|
| Message-ID | <[email protected]> |
Jonathan --
This looks terrific, thanks! I'll look at it more closely later
tonight (I hope). In particular, I need to think about how such a
feature would and should play with inheritance, projections, etc.
j
On Tue, May 17, 2005 at 10:49:41AM -0600, Jonathan Ellis wrote:
> Here is a patch that adds a _guess_columns field to PyDO; when True, it
> reads column/unique/seq info from the database. Only implemented for
> postgresql right now; figured the rest could wait until Jacob signs off on
> the approach.
>
> Patch is against 2.0a2 since SF.net anon CVS doesn't seem to be working.
>
> -Jonathan
>
> diff -ru PyDO-2.0a2-orig/src/PyDO2/ PyDO-2.0a2/src/PyDO2/ | grep -v "Only
> in"
> diff -ru PyDO-2.0a2-orig/src/PyDO2/base.py PyDO-2.0a2/src/PyDO2/base.py
> --- PyDO-2.0a2-orig/src/PyDO2/base.py 2005-05-12 10:48:32.000000000 -0400
> +++ PyDO-2.0a2/src/PyDO2/base.py 2005-05-18 00:52:11.271722536 -0400
> @@ -46,7 +46,32 @@
> """
>
> def __init__(cls, cl_name, bases, namespace):
> + if cls._guess_columns:
> + cls._fields, cls._unique =
> cls.getDBI().describeTable(cls.getTable(
> ))
> + else:
> + # initializes cls._fields, cls._unique
> + cls._init_fields(bases, namespace)
> +
> + # We now have all the inherited declarations; figure out
> + # sequences and additional unique constraints.
> + cls._sequenced={}
> + for f in cls._fields.itervalues():
> + if f.sequence:
> + cls._sequenced[f.name]=f.sequence
> + if f.unique:
> + cls._unique.add(f.name)
> +
> + # this doesn't need to be
> + cls._unique=frozenset(cls._unique)
> +
> + # add attribute access to fields
> + if cls.use_attributes:
> + for name in cls._fields:
> + if not hasattr(cls, name):
> + # a field is also a descriptor
> + setattr(cls, name, cls._fields[name])
>
> + def _init_fields(cls, bases, namespace):
> # create Field objects declared locally in this class
> # and store them in a temporary dict
> fielddict={}
> @@ -74,7 +99,7 @@
> revbases=[x for x in bases[::-1] if x not in (object, dict)]
>
> cls._fields={}
> - uniqueset=set()
> + cls._unique=set()
> for b in revbases:
> flds=getattr(b, '_fields', None)
> if flds:
> @@ -89,7 +114,7 @@
> uniq=set(_setize(x) for x in uniq)
> if cls._is_projection:
> uniq=_restrict(fielddict, uniq)
> - uniqueset.update(uniq)
> + cls._unique.update(uniq)
>
> # If a field is declared upstream and you redeclare it in a
> # subclass as a simple field (just a fieldname), then the
> @@ -98,26 +123,8 @@
> updatefields=((x, y) for x, y in fielddict.iteritems() \
> if not (x in cls._fields and x in simplefields))
> cls._fields.update(updatefields)
> - uniqueset.update(_setize(x) for x in namespace.get('unique', ()))
> -
> - # We now have all the inherited declarations, and figure out
> - # sequences and additional unique constraints.
> - cls._sequenced={}
> - for f in cls._fields.itervalues():
> - if f.sequence:
> - cls._sequenced[f.name]=f.sequence
> - if f.unique:
> - uniqueset.add(f.name)
> + cls._unique.update(_setize(x) for x in namespace.get('unique', ()))
>
> - # this doesn't need to be
> - cls._unique=frozenset(uniqueset)
> -
> - # add attribute access to fields
> - if cls.use_attributes:
> - for name in cls._fields:
> - if not hasattr(cls, name):
> - # a field is also a descriptor
> - setattr(cls, name, cls._fields[name])
>
> class PyDO(dict):
> """ Base class for PyDO data classes."""
> @@ -125,6 +132,7 @@
> __metaclass__=_metapydo
> _is_projection=False
> _guess_tablename=True
> + _guess_columns=False
> mutable=True
> use_attributes=True
> connectionAlias=None
> @@ -328,6 +336,8 @@
> @classmethod
> def getDBI(cls):
> """return the database interface"""
> + if not cls.connectionAlias:
> + raise KeyError, "class %s must declare a DBI alias" %
> cls.__name__
> conn=getConnection(cls.connectionAlias)
> return conn
>
> diff -ru PyDO-2.0a2-orig/src/PyDO2/dbi.py PyDO-2.0a2/src/PyDO2/dbi.py
> --- PyDO-2.0a2-orig/src/PyDO2/dbi.py 2005-05-05 12:05:04.000000000 -0400
> +++ PyDO-2.0a2/src/PyDO2/dbi.py 2005-05-18 01:49:00.521437800 -0400
> @@ -171,9 +171,11 @@
> raise NotImplementedError
>
> def describeTable(self, table):
> - """returns a table description for the given table.
> - The description is a 3-tuple of fields, unique constraints,
> - and sequences/auto_increment for the table in question"""
> + """
> + for the given table, returns a 2-tuple of
> + (dict of Field objects keyed by name,
> + list of multicolumn unique constraints (sets of Fields))
> + """
> raise NotImplementedError
>
>
> diff -ru PyDO-2.0a2-orig/src/PyDO2/drivers/psycopgconn.py
> PyDO-2.0a2/src/PyDO2/d
> rivers/psycopgconn.py
> --- PyDO-2.0a2-orig/src/PyDO2/drivers/psycopgconn.py 2005-05-05
> 12:02:14.0000
> 00000 -0400
> +++ PyDO-2.0a2/src/PyDO2/drivers/psycopgconn.py 2005-05-18
> 01:28:43.565443232 -0
> 400
> @@ -12,6 +12,7 @@
> from PyDO2.operators import BindingConverter
> from PyDO2.dbtypes import DATE, TIMESTAMP, BINARY, INTERVAL, \
> date_formats, timestamp_formats
> +from PyDO2.field import Field
>
> import time
> import datetime
> @@ -19,10 +20,10 @@
> import psycopg
>
> if psycopg.__version__[:3] >= '1.9':
> - #psycopg version two.
> - psycopg_version=2
> + #psycopg version two.
> + psycopg_version=2
> else:
> - psycopg_version=1
> + psycopg_version=1
>
> try:
> import mx.DateTime
> @@ -147,5 +148,65 @@
> raise PyDOError, "could not get value for sequence %s!" % name
> return res[0]
>
> + def describeTable(self, table):
> + sql = """
> + SELECT a.attname, a.attnum
> + FROM pg_catalog.pg_attribute a
> + WHERE a.attrelid = %s::regclass
> + AND a.attnum > 0
> + AND NOT a.attisdropped
> + ORDER BY a.attnum
> + """
> + fields = {}
> + cur = self.conn.cursor()
> + if self.verbose:
> + debug("SQL: %s", (sql,))
> + cur.execute(sql, (table,))
> + for row in cur.fetchall():
> + if self.verbose:
> + debug("Found column %s" % list(row))
> + fields[row[1]] = Field(row[0])
> +
> + sql = """
> + SELECT indkey
> + FROM pg_catalog.pg_index i
> + WHERE i.indrelid = %s::regclass
> + AND i.indisunique
> + """
> + unique = set()
> + if self.verbose:
> + debug("SQL: %s", (sql,))
> + cur.execute(sql, (table,))
> + for row in cur.fetchall():
> + L = [int(i) for i in row[0].split(' ')]
> + if self.verbose:
> + debug("Found unique index on %s" % L)
> + if len(L) == 1:
> + fields[L[0]].unique = True
> + else:
> + unique.add(frozenset([fields[i] for i in L]))
> +
> + sql = """
> + SELECT relname
> + FROM pg_class
> + WHERE relname like '%s_%%_seq'
> + AND relkind = 'S'
> + """ % table
> + if self.verbose:
> + debug("SQL: %s", (sql,))
> + cur.execute(sql)
> + for row in cur.fetchall():
> + maybecolname = row[0][len(table) + 1:-4]
> + for field in fields.values():
> + if field.name == maybecolname:
> + if self.verbose:
> + debug("Found sequence %s on %s" % (row[0],
> field.name))
> + field.sequence = row[0]
> + break
> +
> + cur.close()
> + d = {}
> + for f in fields.values():
> + d[f.name] = f
> + return (d, unique)
>
> -
>
>
> -------------------------------------------------------
> This SF.Net email is sponsored by Oracle Space Sweepstakes
> Want to be the first software developer in space?
> Enter now for the Oracle Space Sweepstakes!
> http://ads.osdn.com/?ad_id=7412&alloc_id=16344&op=click
> _______________________________________________
> Skunkweb-list mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/skunkweb-list
>
--
Jacob Smullyan
signature.asc
(application/pgp-signature, 189 B)
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (GNU/Linux) iD8DBQFCii77uqamFyFXXLIRAhD/AKCfkIPI6LOML39PN/etDpRVZbKb6gCfcdyc u3kQc0gX6A/fbKSSVV+20L4= =Na3f -----END PGP SIGNATURE-----