Публикации с меткой «sqlalchemy»

Блог python на хабрахабре

Python / [Перевод] SQLAlchemy для Django

Я не люблю Django. Я не люблю Django ORM, Django templates, Django forms и еще множество вещей в Django. Но у Django есть определенное преимущество — многие используют Django, практически любой python-программист знаком с джанго. Поэтому приходится мириться с недостатками этого фреймворка, но никто не мешает облегчать себе жизнь, используя действительно хорошие python библиотеки, например SQLAlchemy.

Что в SQLAlchemy лучше чем в Django ORM? Это главный вопрос, который я слышу от Django guys1, и у меня есть на него ответ — SQLAlchemy может выразить любой SQL запрос (ну 80-90%), в отличие от Django ORM, в котором можно выразить только весьма простые вещи.

TONY.SU

Twisted + SqlAlchemy

Так уж получается, что пишу посты исключительно по просьбе знакомых, либо когда меня много раз просят объяснить или показать одну и ту же вещь. На сей раз описываю вариант взаимодействия twisted и sqlalchemy. Сразу говорю что изначально авторство не моё, мне подсказали подобный вариант использования на IRC канале #python в сети freenode.net, к сожалению ника подсказавшего уже не помню.

Для начала немного теории, как можно использовать реляционную базу данных асинхронно с применением twisted:

  • Можно использовать twisted.enterprise.adbapi однако подружить с SqlAlchemy его достаточно сложно. Конечно, можно использовать только конструктор запросов из SqlAlchemy и запускать их напрямую в adbapi.
  • Драйвер psycopg2 поддерживает неблокируемые запросы.
  • Когда-то существовал проект SaSync который на данный момент находится в неживом состоянии.
  • Использовать свою обертку для SqlAlchemy и Twisted.

Больше чем уверен, есть еще много других способов, но здесь я опишу последний.

Перед тем как начать, я опишу значение декоратора toThread, использование которого будет ниже:

from twisted.internet import threads

def toThread(f):
    def wrapper(*args, **kwargs):
        return threads.deferToThread(f, *args, **kwargs)
    return wrapper

Декоратор оборачивает функцию в deferToThread и делает её неблокируемой, соответственно возвращает Deferred.

Теперь сама функция-декоратор которая позволяет делать неблокируемые запросы к базе данных:

from sqlalchemy import create_engine, pool 
from sqlalchemy.orm import sessionmaker

from twisted_decorators import toThread

class DBDefer(object):

    def __init__(self, dsn, poolclass=pool.SingletonThreadPool):
        self.engine = create_engine(dsn, poolclass=poolclass)

    def __call__(self, func):

        @toThread
        def wrapper(*args, **kwargs):
            session = sessionmaker(bind=self.engine)()
            try:
                return func(session=session, *args, **kwargs)
            except:
                session.rollback()
                raise
            finally:
                session.close()

        return wrapper

Пример использования:

from twisted.web import xmlrpc, server
from twisted.internet import reactor
from twisted.internet.defer import Deferred

import sqlalchemy as sa

from sa_decorators import DBDefer

dbdefer = DBDefer('postgres://postgres@localhost/mytestdb')
metadata = sa.MetaData(dbdefer.engine)

userTable = sa.Table('user', metadata,
        sa.Column('user_id', sa.Integer(11), primary_key=True),
        sa.Column('login', sa.String(30), unique=True),
        sa.Column('first_name', sa.String(30), nullable=True),
        sa.Column('last_name', sa.String(30), nullable=True),
)

class User(object):

    def __init__(self, login, first_name, last_name):
        self.login = login
        self.first_name = first_name
        self.last_name = last_name

    def __iter__(self):
        return (t for t in self.__dict__.iteritems() if not t[0].startswith('_'))

sa.orm.mapper(User, userTable)

@dbdefer
def createUser(login, first_name, last_name, session=None):
    user = User(login=login, first_name=first_name, last_name=last_name)
    session.add(user)
    session.commit()
    new_user = session.query(User).filter_by(user_id=user.user_id).first()
    session.flush()
    session.close()
    return new_user

@dbdefer
def getUser(user_id, session=None):
    return session.query(User).filter_by(user_id=user_id).first()

class UserService(xmlrpc.XMLRPC):

    def xmlrpc_create_user(self, login, first_name, last_name):
        return createUser(login, first_name, last_name).addCallback(dict)

    def xmlrpc_get_user(self, user_id):
        d = Deferred()
        def _gotResult(_user):
            if _user is None:
                d.errback('No such user')
            else:
                d.callback(dict(_user))
            return _user

        getUser(user_id).addCallbacks(_gotResult, d.errback)
        return d

def main():

    userService = UserService()
    reactor.listenTCP(8000, server.Site(userService))
    reactor.run()

if __name__ == '__main__':
    main()

Исходные коды доступны в репозитарии bitbucket.

Блог python на хабрахабре

Язык программирования Python / [Ссылка] Вышел SQLAlchemy 0.6

После года плодотворного труда обновился SQL toolkit и ORM — SQLAlchemy. Из основных изменений следует выделить поддержку py3k и новых баз данных.

Дневник одного змеевода

Автоматическая фильтрация публичных данных в SQLAlchemy

В CMS в таблице для (почти) каждой сущности обычно добавляется поле-флаг, определяющее, должна ли данная сущность показываться на сайте. В коде сайта, соответвенно, необходимо не забывать добавлять соответствующее условие в каждый запрос. При использовании ORM мы автоматичеси получаем связанные сущности, для которых запрос генерируется автоматически. Это удобно, но теперь нам ещё нужно проверять, нужно ли показывать каждый из связанных объектов. Есть ещё множество ситуаций, когда такие проверки или добавление дополнительных условий также необходимы. Шансы, что в большом проекте где-то об этом забудут, близки к 100%. Поэтому очень желательно процесс фильтрации непубличных данных автоматизировать. В django для этих целей используют специально написанный менеджер. В древней библиотеки QPS с некоторым подобием ORM сделано даже лучше: можно для разных тегов выборки определить разные правила формирования запроса и даже правила переноса тегов на связанные обекты.
Как же быть с решением проблемы автоматической фильтрации в SQLAlchemy? Существует возможность при создании сессии подставить свой конструктор запроса через атрибут query_cls в sessionmaker. Но предлагаемые в архивах Google-группы sqlalchemy решения уже не работают, так как метод Query.get() теперь не предназначен для объектов с условием. Я написал свою реализацию метода, без этого ограничения. Вот что получилось в результате:
class HackedQuery(Query):

    def get(self, ident):
        # Use default implementation when there is no condition
        if not self._criterion:
            return Query.get(self, ident)
        # Copied from Query implementation with some changes.
        if hasattr(ident, '__composite_values__'):
            ident = ident.__composite_values__()
        mapper = self._only_mapper_zero(
                    "get() can only be used against a single mapped class.")
        key = mapper.identity_key_from_primary_key(ident)
        if ident is None:
            if key is not None:
                ident = key[1]
        else:
            from sqlalchemy import util
            ident = util.to_list(ident)
        if ident is not None:
            columns = list(mapper.primary_key)
            if len(columns)!=len(ident):
                raise TypeError("Number of values doen't match number "
                                'of columns in primary key')
            params = {}
            for column, value in zip(columns, ident):
                params[column.key] = value
            return self.filter_by(**params).first()


def QueryPublic(entities, session=None):
    # It's not derectly related to the problem, but is useful too.
    query = HackedQuery(entities, session).with_polymorphic('*')
    # I haven't ever seen examples with several entities, so I can test
    # this case.
    assert len(entities)==1, entities
    cls = _class_to_mapper(entities[0]).class_
    public_condition = getattr(cls, 'public_condition', None)
    if public_condition is not None:
        query = query.filter(public_condition)
    return query

Блог django на хабрахабре

Django Framework / [Перевод] Анализ участников

Некоторым из вас, возможно, будет интересно узнать, что более 900 уникальных участников было задествовано в разработке Django, начиная с 1 января 2007 года. Данные основаны на логах SVN.
Должен заметить, что сообщество очень сильное, особенно в сравнеии с другими, широко известными проектами:
  • Django: 906
  • Pylons: 80
  • PyPy: 240
  • Linux Kernel: 4043
  • PostgreSQL: 150
  • Apache HTTP server: 118
  • SQLAlchemy: 36
  • Python: 428

Необходимо учесть, что все анонимные и гостевые коммиты считались за одного участника, если конечно проект позволял таковые.

Дневник одного змеевода

Борьба с обNULLением в SQLAlchemy

SQLAlchemy, пожалуй, самый продвинутый ORM для питона. Но, к сожалению, он постоянно подбрасывает неприятные сюрпризы. В очередной раз натолкнувшись на одну из проблем и потратив время на повторный поиск её решения, я решил его задокументировать. Речь об установки в NULL поля с идентификатором при удалении объекта, на который он ссылается, если в маппере для связи используется relation. Для тех, кто привык работать с SQL, такое поведение по умолчанию в лучшем случае вызывает недоумение. Фактически оно означает использование на уровне кода по умолчанию правила ON DELETE SET NULL, вместо привычного (и логичного!) ON DELETE RESTRICT. Если бы не моя чрезмерная педантичность в проставлении nullable=False для полей с FOREIGN KEY, этот сюрприз мог бы привести к весьма печальным последствиям - потери данных. Упоминание об описанном поведении в документации к SQLAlchemy встречается только один раз - при описании ключа passive_deletes функции relation(). Собственно его установка в 'all' и решает проблему. Так как имя ключа ничего не говорит о его истинном назначении, то соответствующий комментарий явно не помешает.

Метки

.net .NET C# .sort 1.2 2009 2010 404 error admin ajax amazon analytics and apache api archlinux asp.net async asynchronous autocomplete bash blender blog blogengine blogs book bootstrap bot bpython buildout byteflow bzr C c plus plus C++ cache cbv Chaco checkio chrome ci ckeditor class based views clojure closure cms cms с удобной админкой code coding style collectd COM comet competition conference ConfigParser contest Context continuous integration CouchDB coverage CppCMS cpyext cpython crud csrf CSS ctypes curl custom model fields cx_freeze cython database db dbm dbqueries debian debug debugging decorator decorators deploy deployment descriptor design dev devconf developers development diveintopython Django django 1.2 django 1.3 django advent django framework django template django trunk django weblog django-admin-tools django-cms django-compressor django-hosts django-piston django-registration django-sphinx django.admin djangoadvent djangocms djangodash doc documentation drupal e-legion eclipse EGit emacs encoding Enthought epoll erlang event exception ExtJS fabric facebook fastcgi finaloption fixtures fonts forms formset fp framework freebsd freeswitch fs2web ftp fun funcparserlib functional gae gamin gandi generic views gettext gevent gil git github gitosis Google Google App Engine google picasa Google Translate google wave Google Web Toolkit grab grablab greenlet gtd gui haskell hg hgshelve highlighter host hosting how-to howto html html5lib Hudson humor i18n icfpc ide idiomatic image-scripting improvements Internet interpreter ipython ironpython izmenimsya.ru jabber java javascript jenkins jetbrains JIT job jquery json jstree jython kde kiev kiyv kyivpy l10n ldap library libs Life Links linux Linux & Unix LLVM logging logs lxml Mac OS X magic mail markdown Matplotlib Mayavi maybe mediavirus meetup memcache Memcached memory messages metaclass middleware migration mikrotik mkd model models mod_python mod_wsgi mongodb monitoring mptt musicmans.ru musicx mvc my-projects mysql netCDF networkx newforms newforms-admin news nginx Nhibernate nix nose NoSQL numpy oop open source OpenID openoffice opster optimization oracle orm os pagination parsing path patterns pdf PDF-принтер PEP PEP8 performance performance optimization perl personality photo php picture-driven computing PIL pinax pingback pip plasma plone plugin plugins postgresql programming progress bar psycopg2 py2exe pybb pybbm pycamp pycharm pycon pycow pycurl pydev pygtk pylons PyNGL pypy pyqt PyQt4 pyrad pyramid PySide Python Python 2.5 python 2.7 python 3 python c api python speed python-mssql python3 pywinauto Qt Qt4 queue rabbitmq radius raw sql re redis redsolution redsolution cms regexp regular expressions release repoze.bfg RequestContext reusable apps robokassa rss ru ruby ruby-on-rails sample satchmo scalability SciPy scraping screencast search selenium self.error seo server setattr settings setuptools shell sikuli sms snippet socket.io software sorting south sphinx spider sql sqlalchemy sqlite ssh startup step-by-step subdomain subversion svn SyntaxHighlighter system tags tdd tddspry teh drama template templates templatetags test testing thinkpad threading threads tips tips and tricks tools tornadio tornado tornado server tricks tutorial tweepy twisted twitter typography uapycon Ubuntu ucsvlog uml Uncategorized unicode unit test unit testing UnitTest Unladen Swallow upload urllib urls utf-8 uwsgi validation vcs versioning video vim virtualenv Visual Studio vkontakte voip wave web web-devel web-services web-разработка webdev webfaction webkit webpy websockets webtest widget widgets Win API windows Wirbel work wrapper wsgi wxPython wxWidgets wysiwyg xapian xml xmonad xmpp xpath yandex youtube zip zomg zope [cdata[cbv]] [cdata[ci]] [cdata[class based views]] [cdata[continuous integration]] [cdata[django framework]] [cdata[django-sphinx]] [cdata[django]] [cdata[nginx]] [cdata[python]] [cdata[virtualenv]] [cdata[программирование]] автоматизация администрирование администрирование django админка алгоритмы архитектура атрибуты базы данных Без рубрики безопасность библиотеки блоге бот веб-разработка видео Визуализация данных вконтакте Все записи гвидо ван россум граббер графика графы декоратор декораторы дескриптор дескрипторы документация заметки игра жизнь идея интересное киев Клиентам книги конференция личное математика метаклассы модели модули монады морфология мысли невозможное новости о облачные вычисления обо мне Обработка данных оптимизация оптимизация кода Основная лента основы парсинг парсинг сайтов перевод песочница Питон поебень поиск правила кодирования программирование Проектирование производительность работа рабочее размышлизмы Разное разработка разработка приложений разработки регулярные выражения сайт событие события ссылки статьи тестирование тесты Тюмень убунтариум фигня философия формы форум Хабрахабр хакинг хостинг шаблоны шаблоны проектирования эксперимент Эксперименты юмор я пиарюсь Яндекс