Уведомления

Группа в Telegram: @pythonsu

#1 Май 19, 2008 01:50:27

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Транслятор имён в SQLAlchemy.

Привет всем!
Изучаю SQLAlchemy. Всё замечательно.
Xочу на её уровне организовать трансляцию имён из имён базы данных, в имена сервера/клиента. Т.е. существует отображение имён DB к именам программы, назовём его TransDict, и мне нужно, чтобы работал следующий код:


# -*- coding: utf-8 -*-

from sqlalchemy import *
from sqlalchemy.orm import *

metadata = MetaData(“sqlite:///:memory:”)
metadata.bind.echo = True

UsersTable = Table(“users”, metadata,
Column(“user_id”, Integer, primary_key = True, nullable = False),
Column(“login”, String(32), nullable = False)
)

metadata.create_all()


class DbObject(object):
TransDict = {}
def __getattr__(self, attr):
if attr in self.TransDict: return getattr(self, self.TransDict)
else:
try: return self.__dict__
except KeyError: raise AttributeError(attr)

def __setattr__(self, attr, value):
if attr in self.TransDict: return setattr(self, self.TransDict, value)
else:
self.__dict__ = value


class User(DbObject):
TransDict = { ## Собственно отображение
“UserId”: “user_id”,
“Login”: “login”
}

def __init__(self, user_id, login):
self.user_id = user_id
self.login= login

def __repr__(self):
return “<User('%s','%s')>” % (self.UserId, self.Login)


mapper(User, UsersTable)

Session = create_session(autoflush=True, transactional=True)

root = User(1, “root”)
Session.save(root)

User2 = User(2, “User2”)
Session.save(User2)

Session.commit()
print Session.query(User).all()

User2.Login = “User22”
Session.commit()
print Session.query(User).all()

## До этого момента всё Ok.
print Session.query(User).filter(User.UserId > 1).all()



Я прекрасно понимаю почему оно не работает (это описывать не надо).
Нутром чую, что решение заключается в создании своего матакласса, но работал с ними настолько мало, что даже на такую проблему меня не хватило.
Фактически зачача не связана с алхимией. Нужно обработать __getattr__ и __setattr__ в подклассе от DbObject, да так, чтобы иметь доступ к TransDict… Очень бы хотелось помощи.

P.S. Для чего это нужно? Для удобства. Дело в том, что клиент у меня на PyQt4 и я пытаюсь сохранить qt'шную стилистику и одно пространство имён как для клиента, так и для сервера. Я намеренно отказался от PEP8. В тоже время, в DB хотелось бы выдержать стиль, принятый в базах данных. Вот и приходится извращатся с, так сказать, шлюзом.



Офлайн

#2 Май 19, 2008 02:57:17

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Транслятор имён в SQLAlchemy.

Пытаюсь решить самостоятельно.

class Meta(type):
def __new__(cls, name, bases, dct):
return type.__new__(cls, name, bases, dct)

def __init__(cls, name, bases, dct):
type.__init__(cls, name, bases, dct)

def __getattr__(cls, attr):
if attr in cls.TransDict:
try: return cls.__dict__[cls.TransDict]
except KeyError: raise AttributeError(cls.TransDict)
else:
try: return cls.__dict__
except KeyError: raise AttributeError(attr)

def __setattr__(cls, attr, value):
if attr in cls.TransDict:
cls.__dict__[cls.TransDict] = value
else:
cls.__dict__ = value


class DbObject(object):
__metaclass__ = Meta
TransDict = {}
def __getattr__(self, attr):
if attr in self.TransDict:
try: return self.__dict__[self.TransDict]
except KeyError: raise AttributeError(self.TransDict)
else:
try: return self.__dict__
except KeyError: raise AttributeError(attr)

def __setattr__(self, attr, value):
if attr in self.TransDict:
self.__dict__[self.TransDict] = value
else:
self.__dict__ = value


class User(DbObject):
TransDict = { ## Собственно отображение
“UserId”: “user_id”,
“Login”: “login”
}

def __init__(self, user_id, login):
self.user_id = user_id
self.login= login

def __repr__(self):
return “<User('%s','%s')>” % (self.UserId, self.Login)
Теперь, если делаем `print User.Login`, то матерится на отсутствие “login”, т.е. работает правильно.
А вот если print `User.Login` = u“чё-нить”, то мат смещается в сторону невозможности изменить объект типа dictproxy в последней строке __setattr__ метакласса. Т.е __dict__ у классов не изменяем…

Как сломать?

P.S. Практически, это первая серьёзная проба создания метаклассов… Так что уж сильно не смейтесь…
P.P.S. А теперь спать. Продолжу ломать мозг завтра.



Офлайн

#3 Май 19, 2008 04:26:13

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Транслятор имён в SQLAlchemy.

Не пошёл я спать.
Стоило мне задуматься о том, что я изобретаю очередной велосипед…

mapper(User, UsersTable, properties={
‘UserId’: UsersTable.c.user_id,
‘Login’: UsersTable.c.login,
})


Идиотизм, однако, но мне всё-таки интересно разобраться с первым направлением поиска. Для того, чтобы разобраться.



Офлайн

#4 Май 19, 2008 08:09:15

j2a
От:
Зарегистрирован: 2006-06-29
Сообщения: 869
Репутация: +  1  -
Профиль   Отправить e-mail  

Транслятор имён в SQLAlchemy.

Ну создай просто преобразование TransDict->properties_dict:

TransDict = {'UserID': ‘user_id’, ‘Login’: ‘login’}
users_table = sa.Table(…)
class User((object): pass

properties_dict = dict((key, getattr(users_table.c, field)) for key, field in TransDict.items())
sa.mapper(User, users_table, properties=properties_dict)


p.s. в SQLAlchemy принято, что классы таблиц в нижнем_регистре, а классы-маппинги - в CamelCase



Отредактировано (Май 19, 2008 08:11:04)

Офлайн

#5 Май 19, 2008 23:12:15

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Транслятор имён в SQLAlchemy.

Я, по-началу, тоже об этом подумал, но… На самом деле всё ещё круче!

UsersTable = Table(“users”, metadata,
Column(“user_id”, Integer, primary_key = True, nullable = False, key = “UserId”),
Column(“login”, String(32), nullable = False, key = “Login”)
)

Таким образом преобразование имён происходит ещё во время создания таблицы. Об этом можно только мечтать! Так что реальная проблема решена лучше, чем я расчитывал.

Осталось лишь желание разобраться с метаклассами… Так, для спортивного инетереса…

j2a
p.s. в SQLAlchemy принято, что классы таблиц в нижнем_регистре, а классы-маппинги - в CamelCase
Учтём.



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version