Форум сайта python.su
Хотелось бы помостреть пример законченного небольшого приложения с использованием sql alchemy.
Почитал tutorial и разобрался с примерами и вроде все понятно, но когда пытаюсь разбить все по класса - начинаются затруднения.
Простейший пример - у меня есть табличка где лежат наименования продуктов и есть форма, где я хочу их отображать.
Ок:
1. class Product - это сам класс продукта (который, кстати, с помощью mapper отображается на табличку СУБД)
2. class ProductForm - форма (gtk) - UI - представление данных пользователю
Дальше сложнее. Насколько я понимаю, экземпляр ProductForm должен запросить список продуктов у какого то другого класса. Назовем его DatabaseEngine. Его задача - работать с СУБД возвращать для UI наборы данных, а также осуществлять данные в СУБД по запросу UI.
Но тогда возникает вопрос - либо DatabaseEngine у нас - синглетон и мы должны постоянно хранить экземпляр этого объекта в программе, дабы запрашивать у него данные. Либо постоянно создавать его?
Второй вопрос - а когда надо делать все вот эти вещи:
self.engine = create_engine('sqlite:///database.db')
self.metadata = MetaData()
self.product_table = Table('product', self.metadata,
Column('name', String(30), primary_key = True),
Column('category', String(30)),
Column('fat', Integer),
Column('protein', Integer),
Column('carbo', Integer)
)
self.metadata.create_all(self.engine)
mapper(Product, self.product_table)
Session = sessionmaker(bind = self.engine)
self.session = Session()
а) один раз - при запуске программы?
б) каждый раз при обращении к DatabaseEngine - в его конструкторе ?
в) …..
Третий вопрос - а нужен ди один DatabaseEngine - для получения всех сущностей из СУБД или под каждую сущность создавтаь свой класс?
Офлайн
demasЧто-то не внятно. Вообще, дело так: есть твой класс Product, как только ты маппишь его к (скажем) табличке, твой класс становится “волшебным” и он знает, как и что откуда брать. Engine - это типа connection pool. Как будешь с ним работать - твоё дело. Но общепринято одно приложение == один engine (опуская всякие специфичные ситуации, когда разработчик четко понимает, зачем ему более чем один engine). Не обязательно чтобы именно engine был глобальным. Обычно, если используют ORM, класс сессий (Session) делают глобальным, а к нему, либо к локальным сессиям (экземпляры Session), биндят нужный engine.
Дальше сложнее. Насколько я понимаю, экземпляр ProductForm должен запросить список продуктов у какого то другого класса. Назовем его DatabaseEngine. Его задача - работать с СУБД возвращать для UI наборы данных, а также осуществлять данные в СУБД по запросу UI.
Но тогда возникает вопрос - либо DatabaseEngine у нас - синглетон и мы должны постоянно хранить экземпляр этого объекта в программе, дабы запрашивать у него данные. Либо постоянно создавать его?Проведи аналогию с соединением к СУБД. Можно и так и так. Но разумнее использовать постоянное соединение aka один engine.
Второй вопрос - а когда надо делать все вот эти вещи:создание meta, описание схемы, сопоставление классов и таблиц, создание глобальной сессии - один раз. Создание локальной сессии (session = Session()) - много раз, в локальном контексте.
self.engine = create_engine('sqlite:///database.db')
self.metadata = MetaData()
self.product_table = Table('product', self.metadata,
Column('name', String(30), primary_key = True),
Column('category', String(30)),
Column('fat', Integer),
Column('protein', Integer),
Column('carbo', Integer)
)
self.metadata.create_all(self.engine)
mapper(Product, self.product_table)
Session = sessionmaker(bind = self.engine)
self.session = Session()
а) один раз - при запуске программы?
б) каждый раз при обращении к DatabaseEngine - в его конструкторе ?
в) …..
Третий вопрос - а нужен ди один DatabaseEngine - для получения всех сущностей из СУБД или под каждую сущность создавтаь свой класс?см. выше аналогию с соединением. SA дает возможность тебе для каждого запроса явно указывать engine, но нужно ли тебе это…
Офлайн
demashttp://www.sqlalchemy.org/trac/wiki/SAApps
Хотелось бы помостреть пример законченного небольшого приложения с использованием sql alchemy.
Офлайн
Вот я тут повозился и породил вот такую конструкцию:
#! /usr/bin/python
# -*- coding: utf-8 -*-
from sqlalchemy import create_engine
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
from sqlalchemy.orm import mapper
from sqlalchemy.orm import sessionmaker
class DatabaseEngine(object):
def sync(self):
self.engine = create_engine('sqlite:///database.db')
self.metadata = MetaData()
product_table = Table('product', self.metadata,
Column('name', String(30), primary_key = True),
Column('category', String(30)),
Column('fat', Integer),
Column('protein', Integer),
Column('carbo', Integer)
)
self.metadata.create_all(self.engine)
mapper(Product, product_table)
def createSession(self):
Session = sessionmaker(bind = self.engine)
session = Session()
return session
class Product(object):
def __init__(self, name, category, fat, protein, carbo):
self.name = name
self.category = category
self.fat = fat
self.protein = protein
self.carbo = carbo
def __repr__(self):
return "<Product(%s, %s, %s, %s, %s)>"%(self.name, self.category, self.fat, self.protein, self.carbo)
class ProductManager(object):
def __init__(self, engine):
self.engine = engine
def appendProduct(self, product):
session = self.engine.createSession()
session.save(product)
session.commit()
def printProductList(self):
session = self.engine.createSession()
for each_product in session.query(Product):
print each_product.name
engine = DatabaseEngine()
engine.sync() # вызывать один раз при старте программы
manager = ProductManager(engine)
manager.appendProduct(Product('Mill', 1, 2, 3, 4))
manager.printProductList()
Офлайн
А зачем ты DatabaseEngine класс сделал? Я не улавливаю, зачем нужно делать такую прослойку перед SA.
ну опиши схему таблиц в отдельном модуле, скажем, schema.py:
from sqlalchemy import MetaData, Table, Column, String, Integer,
meta = MetaData()
products = Table('products', meta,
Column('name', String(30), primary_key = True),
Column('category', String(30)),
Column('fat', Integer),
Column('protein', Integer),
Column('carbo', Integer)
)
from yourapp import schema
import sqlalchemy.orm as orm
Session = orm.scoped_session(orm.sessionmaker(autoflush=False))
class Base(object):
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
class Product(Base):
pass
Session.mapper(Product, schema.products)
import sqlalchemy as sa
from yourapp.mappings import Session, Product
from yourapp.schema import meta
def print_products(products):
for p in products:
print p
def bind_engine(engine):
meta.bind = engine
Session.configure(bind=engine)
def main(dburi):
engine = sa.create_engine(dburi)
bind_engine(engine)
print_products(Product.query.all())
if __name__ == '__main__':
main('sqlite:///yourapp.db')
Отредактировано (Сен. 16, 2008 17:33:02)
Офлайн
Да, глянь http://www.pyobject.ru/static/files/sqlalchemy-seminar.pdf , если поможет - чиркни письмо.
Офлайн
Спасибо, огромное. Презентацию внимательно посмотрю вечером, а код уже посмотрел.
Если не против, несколько вопросов:
1. a) правильно ли я понимая, что при указании import module выполнится код из этого модуля (тот, что не является методами классов)?
б) правильно ли я понимаю, что когда ты пишешь from mappings import Session, Product ты делаешь эти созданные экземпляры объектов доступными в этом модуле? Сам код модуля повторно исполнен не будет?
2. Зачем класс Base ?
Офлайн
demasНу при импорте выполняется последовательно код модуля. Если это объявление класса, функции, метода, то выполняется объявление этих объектов, сами они, естественно, не выполняются при этом.
Спасибо, огромное. Презентацию внимательно посмотрю вечером, а код уже посмотрел.
Если не против, несколько вопросов:
1. a) правильно ли я понимая, что при указании import module выполнится код из этого модуля (тот, что не является методами классов)?
б) правильно ли я понимаю, что когда ты пишешь from mappings import Session, Product ты делаешь эти созданные экземпляры объектов доступными в этом модуле? Сам код модуля повторно исполнен не будет?В данном случае Session и Product - это классы. Session - класс, созданный фабрикой sessionmaker; Product - наш класс, сопоставленный таблице products. Код модуля в питоне исполняется при первом импорте. Далее, он запоминает, что модуль импортирован и при последующих импортах код модуля не выполняется.
2. Зачем класс Base ?Ведь наверняка у тебя не одна таблица и не один замаппенный класс. Это удобный конструктор, чтобы не писать каждый раз
class Some(object):
def __init__(self, var1, attr2):
self.var1 = var1
self.attr2 = attr2
Офлайн
j2aДа, действительно. Спасибо.
Далее, он запоминает, что модуль импортирован и при последующих импортах код модуля не выполняется.
demas@laptop:~/sources/study/python/modules$ cat m1.py
print "1"
demas@laptop:~/sources/study/python/modules$ cat m2.py
import m1
print "2"
demas@laptop:~/sources/study/python/modules$ cat m3.py
import m1
import m2
print "3"
demas@laptop:~/sources/study/python/modules$ python m3.py
1
2
3
j2aБолее простые вещи делать скучно ;) С python я вожусь для души, не по работе - поэтому ошибаться не страшно :)
P.S. Судя по вопросам, с питоном не особо свободно работаешь. Возможно, стоит вначале поделать более простые вещи, чем сразу браться за pygtk и sqlalchemy?
Офлайн