Найти - Пользователи
Полная версия: [sqlalchemy] - общая диаграмма взаимодействия
Начало » Базы данных » [sqlalchemy] - общая диаграмма взаимодействия
1
demas
Хотелось бы помостреть пример законченного небольшого приложения с использованием 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 - для получения всех сущностей из СУБД или под каждую сущность создавтаь свой класс?
j2a
demas
Дальше сложнее. Насколько я понимаю, экземпляр ProductForm должен запросить список продуктов у какого то другого класса. Назовем его DatabaseEngine. Его задача - работать с СУБД возвращать для UI наборы данных, а также осуществлять данные в СУБД по запросу UI.
Что-то не внятно. Вообще, дело так: есть твой класс Product, как только ты маппишь его к (скажем) табличке, твой класс становится “волшебным” и он знает, как и что откуда брать. Engine - это типа connection pool. Как будешь с ним работать - твоё дело. Но общепринято одно приложение == один engine (опуская всякие специфичные ситуации, когда разработчик четко понимает, зачем ему более чем один engine). Не обязательно чтобы именно engine был глобальным. Обычно, если используют ORM, класс сессий (Session) делают глобальным, а к нему, либо к локальным сессиям (экземпляры Session), биндят нужный engine.

Но тогда возникает вопрос - либо DatabaseEngine у нас - синглетон и мы должны постоянно хранить экземпляр этого объекта в программе, дабы запрашивать у него данные. Либо постоянно создавать его?
Проведи аналогию с соединением к СУБД. Можно и так и так. Но разумнее использовать постоянное соединение aka один engine.

Второй вопрос - а когда надо делать все вот эти вещи:

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 - в его конструкторе ?
в) …..
создание meta, описание схемы, сопоставление классов и таблиц, создание глобальной сессии - один раз. Создание локальной сессии (session = Session()) - много раз, в локальном контексте.

Третий вопрос - а нужен ди один DatabaseEngine - для получения всех сущностей из СУБД или под каждую сущность создавтаь свой класс?
см. выше аналогию с соединением. SA дает возможность тебе для каждого запроса явно указывать engine, но нужно ли тебе это…
j2a
demas
Хотелось бы помостреть пример законченного небольшого приложения с использованием sql alchemy.
http://www.sqlalchemy.org/trac/wiki/SAApps
demas
Вот я тут повозился и породил вот такую конструкцию:

#! /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()
Замечания приветствуются. Что смущает в данном коде:

1. engine - экземпляр DatabaseEngine создается только один раз - в начале проекта. Но так как он нужен, чтобы порождать сессии - мне придется с этим экземпляром объекта носиться как с писанной торбой, передавая его в различные объекты, требующие работы с БД. Выход - делать синглетон?

2. Перед каждой операцией с субд мне приходится создавать сессию: session = self.engine.createSession(). То есть практически в каждом методе. Напрягает.
j2a
А зачем ты 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)
)
Классы для ORM и мапперы - в mappings.py:
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')
j2a
Да, глянь http://www.pyobject.ru/static/files/sqlalchemy-seminar.pdf , если поможет - чиркни письмо.
demas
Спасибо, огромное. Презентацию внимательно посмотрю вечером, а код уже посмотрел.
Если не против, несколько вопросов:

1. a) правильно ли я понимая, что при указании import module выполнится код из этого модуля (тот, что не является методами классов)?
б) правильно ли я понимаю, что когда ты пишешь from mappings import Session, Product ты делаешь эти созданные экземпляры объектов доступными в этом модуле? Сам код модуля повторно исполнен не будет?

2. Зачем класс Base ?
j2a
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
P.S. Судя по вопросам, с питоном не особо свободно работаешь. Возможно, стоит вначале поделать более простые вещи, чем сразу браться за pygtk и sqlalchemy?
demas
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
P.S. Судя по вопросам, с питоном не особо свободно работаешь. Возможно, стоит вначале поделать более простые вещи, чем сразу браться за pygtk и sqlalchemy?
Более простые вещи делать скучно ;) С python я вожусь для души, не по работе - поэтому ошибаться не страшно :)
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB