Реализация шаблона репозитория в Python? - PullRequest
22 голосов
/ 14 марта 2012

В основном из любопытства, я ищу Python-фреймворк или пример для Repository Pattern для отделения логики персистентности от логики предметной области.

Название «Шаблон репозитория» появляется в посте « Распутать домен и логика постоянства с куратором » (Ruby), идея взята из раздела «Проекта, управляемого доменом» Книга и Мартин Фаулер . Класс модели не содержит логики персистентности, скорее, приложение объявляет подклассы репозитория, экземпляры которых действуют как коллекции экземпляров модели в памяти. Каждый репозиторий сохраняет модель по-разному, например, в SQL (различные соглашения о схемах), в Riak или другом noSQL и в памяти (для кэширования). В соответствии с рамочными соглашениями подклассы репозитория обычно требуют минимального кода: простое объявление подкласса «WidgetRepository» в SQLRepository обеспечит коллекцию, которая сохранит виджет модели в таблице БД с именем «widgets» и сопоставит столбцы с атрибутами Widget.

Отличия от других моделей:

Шаблон активной записи : например, Django ORM. Приложение определяет только класс модели с доменной логикой и некоторыми метаданными для сохранения. ORM добавляет логику персистентности к классу модели. Это смешивает домен и постоянство в одном классе (нежелательно согласно посту).

Благодаря @marcin я вижу, что когда Active Record поддерживает разные бэкэнды и функцию .save (using = "other_database"), это дает мульти-бэкэнд-преимущество шаблона репозитория.

Таким образом, в некотором смысле шаблон репозитория подобен Active Record с логикой постоянства, перемещенной в отдельный класс.

Шаблон сопоставления данных : например, Классические сопоставления SQLAlchemy. Приложение определяет дополнительные классы для таблиц базы данных и преобразователя (ей) данных от модели к таблице (ам). Таким образом, экземпляр модели может быть отображен в таблицы несколькими способами, например, поддерживать устаревшие схемы. Не думайте, что SQLAlchemy предоставляет сопоставители для хранилищ, отличных от SQL.

Ответы [ 2 ]

14 голосов
/ 22 ноября 2012

У меня в голове:

Я определяю два примера доменов, User и Animal, базовый класс хранения Store и два специализированных класса хранения UserStore и AnimalStore.Использование диспетчера контекста закрывает соединение с БД (для простоты я использую sqlite в этом примере):

import sqlite3

def get_connection():
    return sqlite3.connect('test.sqlite')

class StoreException(Exception):
    def __init__(self, message, *errors):
        Exception.__init__(self, message)
        self.errors = errors


# domains

class User():
    def __init__(self, name):
        self.name = name


class Animal():
    def __init__(self, name):
        self.name = name


# base store class
class Store():
    def __init__(self):
        try:
            self.conn = get_connection()
        except Exception as e:
            raise StoreException(*e.args, **e.kwargs)
        self._complete = False

    def __enter__(self):
        return self

    def __exit__(self, type_, value, traceback):
        # can test for type and handle different situations
        self.close()

    def complete(self):
        self._complete = True

    def close(self):
        if self.conn:
            try:
                if self._complete:
                    self.conn.commit()
                else:
                    self.conn.rollback()
            except Exception as e:
                raise StoreException(*e.args)
            finally:
                try:
                    self.conn.close()
                except Exception as e:
                    raise StoreException(*e.args)


# store for User obects
class UserStore(Store):

    def add_user(self, user):
        try:
            c = self.conn.cursor()
            # this needs an appropriate table
            c.execute('INSERT INTO user (name) VALUES(?)', (user.name,))
        except Exception as e:
            raise StoreException('error storing user')


# store for Animal obects
class AnimalStore(Store):

    def add_animal(self, animal):
        try:
            c = self.conn.cursor()
            # this needs an appropriate table
            c.execute('INSERT INTO animal (name) VALUES(?)', (animal.name,))
        except Exception as e:
            raise StoreException('error storing animal')

# do something
try:
    with UserStore() as user_store:
        user_store.add_user(User('John'))
        user_store.complete()

    with AnimalStore() as animal_store:
        animal_store.add_animal(Animal('Dog'))
        animal_store.add_animal(Animal('Pig'))
        animal_store.add_animal(Animal('Cat'))
        animal_store.add_animal(Animal('Wolf'))
        animal_store.complete()
except StoreException as e:
    # exception handling here
    print(e)
2 голосов
/ 14 марта 2012

Возможно, вы захотите взглянуть на проект DictShield Джеймса Денниса

"DictShield - это система моделирования, независимая от базы данных. Она предоставляет способ моделирования, проверки и изменения данныхлегко. Все без какой-либо конкретной базы данных. "

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...