Cetus Group
Cetus Logo
Информационные системы и программное обеспечение



Файлы проекта

Поддержка

 
Object Relation Mapper (ORM) в Python
Размещено 13.11.2007 : Дополнено 16.11.2007
А.Г. © Часть 1 - "Проба ORM Storm"

При разработке программ на ОО-языках программирования для работы с БД, в качестве которых обычно применяют реляционные СУБД, возникают проблемы несоответствия структуры и интерфейса классов программы структуре и отношениям таблиц РСУБД. Одним из решений этой проблемы являются ORM-системы, которые предоставляют объектно-ориентированный интерфейс к таблицам реляционных БД, поддерживают структуру таблиц и отношения между таблицами. Для работы с ORM Storm понадобится работающий сервер баз данных MySQL с базой данных "test", модуль MySQLdb и дистрибутив Storm. Пример выполнен в последней версией Storm - 0.11.

1. Устанавливаем Storm. Для установки достаточно поместить каталог Storm-"версия" в /usr/local/lib и добавить в "/ваш/каталог/пайтон/site-packages" ссылку на Storm-"версия"/storm.

2. Создаем базу данных. В настоящее время Storm не позволяет создавать таблицы БД на основе описания моделей, поэтому создадим таблицы в БД 'test' выполнением двух запросов (в примере использован переработанный код сравнительного тестирования Storm, SQLAlchemy, and Geniusql):

DROP table if exists Animal;
CREATE TABLE Animal (
    ID INT4 NOT NULL auto_increment primary key,
    MotherID INT4,
    ZooID INT4,
    PrefFoodID INT4,
    AltFoodID INT4,
    LastEsc TIMESTAMP,
    Name VARCHAR(100),
    Age INT(2),
    Lifespan FLOAT4,
    PrevZoos TEXT,
    Legs INT4 DEFAULT 4,
    Species VARCHAR(100)
);

DROP table if exists Zoo;
CREATE TABLE Zoo (
    ID INT4 auto_increment primary key,
    Name VARCHAR(255),
    Admission DECIMAL(6, 2),
    Founded DATE,
    LastEsc TIMESTAMP,
    Opens TIME
);

3. Создаем описание двух моделей для тестирования:

import storm
from storm.locals import *


class Zoo(Storm):
    __storm_table__ = 'Zoo'
    ID = Int(primary=True)
    Name = Unicode()
    Founded = Date()
    Opens = Time()
    LastEsc = DateTime()
    Admission = Float()

    def __init__(self, **kwargs):
        for k, v in kwargs.iteritems():
            setattr(self, k, v)


class Animal(Storm):
    __storm_table__ = 'Animal'
    ID = Int(primary=True)
    MotherID = Int()
    ZooID = Int()
    zoo = Reference("ZooID", "Zoo.ID")
    Name = Unicode()
    Species = Unicode()
    Legs = Int(default=4)
    LastEsc = DateTime()
    Lifespan = Float()
    mother = Reference(MotherID, ID)
    PrefFoodID = Int()
    AltFoodID = Int()

    def __init__(self, **kwargs):
        for k, v in kwargs.iteritems():
            setattr(self, k, v)

4. Выполняем простой пример, в котором:
-- создаем объекты класса Zoo и отображаем их состояние до сохранения (секция --1-),
--     и после сохранения, обращаем внимание на установленные СУБД первичные ключи для объектов (--2-),
-- создаем объекты класса Animal и отображаем их состояние после сохранения (--3-),
-- выполняем запрос по двум типам объектов для отображения перечня зоопарков и находящихся в них животных (--4-).

#!/usr/bin/python
#-*- coding: utf-8 -*-

from model1 import *
import datetime


# Create/open database
db = create_database('mysql://mysql:@localhost/test')
conn = db.connect()

store = Store(db)

# Create Zoo objects
wap = Zoo(Name=u'Wild Animal Park',
          Founded=datetime.date(2000, 1, 1),
          Opens=datetime.time(8, 15, 59),
          LastEsc=datetime.datetime(2004, 7, 29, 5, 6, 7),
          Admission=4.95
    )
store.add(wap)

sdz = Zoo(Name = u'San  Diego  Zoo',
          Founded = datetime.date(1835, 9, 13),
          Opens = datetime.time(9, 0, 0),
          Admission = 0
    )
store.add(sdz)

print "--1- Zoo.ID, Zoo.Name before flush:"
print wap.ID, "\t", wap.Name
print sdz.ID, "\t", sdz.Name

store.flush()
store.commit()

print
print "--2- Zoo.ID, Zoo.Name after flush:"
for zoo in store.find(Zoo):
    print zoo.ID, "\t", zoo.Name.encode("latin1")

store.add(Animal(Species='Леопард', Lifespan=73.5, ZooID=sdz.ID))
store.add(Animal(Species='Тигр', Legs=1, Lifespan=.75, ZooID=sdz.ID))
store.add(Animal(Species='Медведь', ZooID=wap.ID))
store.add(Animal(Species='Лев', ZooID=wap.ID))

store.flush()
store.commit()

print
print "--3- Animal.ID, Animal.Species after flush:"
for animal in store.find(Animal):
    print animal.ID, "\t", animal.Species.encode("latin1")

print
print "--4- Zoo.Name, Animal.Species after search:"
for zoo in store.find((Zoo,Animal),"Zoo.ID=Animal.ZooID"):
    print zoo[0].Name.encode("latin1"), "\t", zoo[1].Species.encode("latin1")


store.close()

5. Получаем результаты:

--1- Zoo.ID, Zoo.Name before flush:
None    Wild Animal Park
None    San  Diego  Zoo

--2- Zoo.ID, Zoo.Name after flush:
1       Wild Animal Park
2       San  Diego  Zoo

--3- Animal.ID, Animal.Species after flush:
1       Тигр
2       Медведь
3       Лев
4       Леопард

--4- Zoo.Name, Animal.Species after search:
San  Diego  Zoo         Тигр
Wild Animal Park        Медведь
Wild Animal Park        Лев
San  Diego  Zoo         Леопард

К сожалению оригинальная сборка Storm изначально не позволила работать с кириллицей, что потребовало косметической правки в инсталляции Storm. Кроме этого, если при создании объекта в имени атрибута базы данных будет допущена ошибка - такой атрибут не только не будет инициализирован (что понятно), но и не будет выдано сообщение об ошибке (что плохо). Тем не менее, несмотря на эти "шероховатости" и раннюю версию, ORM Storm имеет хорошо продуманный интерфейс, интересные возможности формирования сложных запросов при работе с хранилищем, высокую скорость работы.


Copyright©2005, Cetus Group . All rights reserved.