[SQLObject] Remarks to first experience with SQLObject
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
|
From: Michal K. <mi...@fr...> - 2003-04-13 14:44:39
|
Hi, Ian and all,
I am Mathematician, no CS man at all, but I like generality
and clarity. So, SQLObject captured my attention.
Now I am trying to rethink our system for student exams agenda
in terms of SQLObject (current implementation is in Python/Webware
with psycopg PostgreSQL driver).
First, my understanding of SQLObject internals is very limited.
Or, maybe, I am pushing SQLObject beyond its current limits...
But my current work with SQLObject is like "debug thrice, write once" :-)
Despite this, I like it. So here are my remarks:
1. From PostgreSQL 7.3 documentation:
"Note: Prior to PostgreSQL 7.3, serial implied UNIQUE. This is no longer
automatic. If you wish a serial column to be UNIQUE or a PRIMARY KEY it
must now be specified, same as with any other data type."
So, I think, it will be useful to add this information to SQLObject
documentation (Using SQLObject/Declaring the Class).
In DBConnection.py, it is already O.K.
2. I refactored database tables several times to make them more
"SQLObject compliant". In one of attempts I choosed column names
like "Passwd" and got db. column name: "_passwd", which I not liked :-(
Users should not be trapped like that, if they are strictly using
proposed naming conventions. But for me, this happens.
Maybe, splitWords from util.py can be written like that:
def splitWords(s):
res = _translateRE.sub(lambda x: '_%s' % x.group(0).lower(), s)
if s[0].isupper():
return res[1:]
return res
3. Say, we have the following code fragment:
# ------------------------------ here begins ------------------
from SQLObject import *
from md5 import md5
# ...
class Student(SQLObject):
_idName='student_id'
_cacheValues=False
_columns=['name',
'surname',
'group',
Col('passwd',default=''),
Col('contact',default='')]
def _set_passwd(self,pwstr):
# This fails, self has (as yet) no attribute "id",
# which I can understand, but don't like
#
#if not pwstr:
# print self.__dict__
# pstr=str(self.__dict__['id'])
cryptpwd=md5(pstr).hexdigest()
print cryptpwd # I see, it is O.K
self._SO_set_passwd(cryptpwd)
# If not defined, "Unreadable attribute" error occurs...
def _get_passwd(self):
self._SO_get_passwd()
# ---------------------------- here ends ----------------------
This code used to work for me as generator of all kinds of troubles...
----------------------------------------
A. Trouble with creating Student object:
SQL statement for creating table is:
CREATE TABLE "student" (
"student_id" SERIAL PRIMARY KEY,
"name" varchar(20) NOT NULL,
"surname" varchar(20) NOT NULL,
"group" varchar(7) NOT NULL),
"passwd" varchar(32) DEFAULT '' NOT NULL,
"contact" varchar(128) DEFAULT '');
This will implicitly create the sequence "student_student_id"
for primary key.
But DBConnection.py, line 439 (checked yesterday) states:
c.execute('SELECT nextval(\'%s_id_seq\')' % table)
Should be the idName not involved in that?
Maybe:
c.execute('SELECT nextval(\'%s_%s_seq\')' % (table,idName)) ?
In my case, student_id_seq was expected, but student_student_id_seq
was the reality.
---------------------------------
B. Troubles with passwd attribute.
Let us create the student:
>>> S=Student.new(name='Milan',surname='Frco',group='5Z023',passwd='frc')
But what we see is:
>>> S
<Student 15007 name='Milan' ... group='5Z023' passwd=None contact=''>
The resulting query seems pretty O.K.:
INSERT INTO student (passwd, meno, priezv, skupina, contact, student_id)
VALUES ('f964379c80b1ab8b890cf40876fbd0aa', 'Milan', 'Frco',
'5Z023', '', 15007).
But, it seems like S.passwd has nothing in common with attribute
generated by "_set_passwd" method.
Without "_get_passwd" defined, we get "Unreadable attribute" error.
If we execute:
>>> S.passwd="caramba"
(Query is O.K., in database it IS updated.)
But, we still have passwd=None in S (also retrieving as S.passwd).
How can it be, I wonder...
But, also if I retrieve (in another Python commandline session)
the Student object with student_id=15007,
>>> P=Student(15007)
I still get "None" for password, although corresponding query
(SELECT passwd FROM student WHERE student_id = 15007) in psql
gives the right, MD5-encrypted password.
I had a goodwill to follow the first example in docs
(Customizing the Objects/Overriding Column Attributes),
but somewhere, something, goes wrong.
(Also, maybe, in second example form docs,
there should be the call:
"self._SO_set_phoneNumber(value)"
instead of
"self._SO_set_phoneNumber(self, value)".)
I will be very grateful, if someone can explain this behaviour and
suggest some way of creating "Student" with MD5-encrypted passwd
(even better - with initial value as the MD5-encrypted string
representation of "student_id").
------------------------------------
C. What we find, trying "destroy"...
Now, we want to delete the abovementioned object S from database.
I suppose (looking into source :-), the method "destroy" can do this:
>>> S.destroy()
>>> S
AssertionError: Student with id 15007 has become obsolete
So far, so good.
>>> del S
>>> S = Student(15007)
>>> S
.... (most of Traceback removed)
File "/usr/local/lib/python2.2/site-packages/SQLObject/SQLObject.py",
line 652, in _SO_getValue
return results[0]
TypeError: unsubscriptable object
This is the behaviour (in PostgreSQL/psycopg) if we retrieve any object
with nonexisting "id". Maybe, it would be better to make assertion like:
---------------------
(file SQLObject.py, lines 651,...):
self._SO_writeLock.release()
assert results != None, "%s with id %s is not in the database" \
% (self.__class__.__name__, self.id)
return results[0]
---------------------
Now, accessing S gives:
"AssertionError: Student with id 15007 is not in the database."
which is more to my liking.
That's all for now. Sorry for long posting...
Mike
|