Say Hello to Elixir for SQLAlchemy · Monday, February 12, 2007

Today, I am pleased to announce the initial release of Elixir, a new declarative mapper for SQLAlchemy. Elixir is the result of a collaboration between:

When I released ActiveMapper, it was the result of a few hours worth of hacking on a weekend. It has served me, and others, well, but it has some serious problems that were difficult to resolve with the current implementation. In addition, since I was the only one working on the project, I was unable to keep up with the number of emails I was receiving about ActiveMapper.

When I saw the release of TurboEntity, I immediately contacted Daniel and asked if he wanted to work together in the future. Later on, we both got in touch with Gaëtan, and Elixir was born. Having three people working together made a big difference, and as a result Elixir is a lot more polished than ActiveMapper could ever hope to be.

Elixir takes a very different approach than both TurboEntity and ActiveMapper, taking some inspiration from Ruby on Rails’ ActiveRecord database layer, and building something fairly similar on top of SQLAlchemy. Figuring out to to support a DSL-like syntax in Python ended up being much easier than any of us expected, and the result makes declaring your database model very pretty. Here is a simple model, from the Elixir videostore example:

class Director(Entity): has_field('name', Unicode(60)) has_many('movies', of_kind='Movie', inverse='director') using_options(tablename='directors') class Movie(Entity): has_field('title', Unicode(60)) has_field('description', Unicode(512)) has_field('releasedate', DateTime) belongs_to('director', of_kind='Director', inverse='movies') has_and_belongs_to_many('actors', of_kind='Actor', inverse='movies') using_options(tablename='movies') class Actor(Entity): has_field('name', Unicode(60)) has_and_belongs_to_many('movies', of_kind='Movie', inverse='actors') using_options(tablename='actors')

I will leave the explanations short, because one of our main goals with Elixir was to wait to release until we had some excellent documentation. I am proud to say that we all three worked very hard to produce a solid set of documentation, including a module reference, a solid tutorial, and a fully working TurboGears example application with identity support.

Over the next few weeks, I am planning on doing a series of posts walking through the creation of the sample application step-by-step, and posting on how you can create plugins for Elixir for generically adding behavior (like tagging, history tracking, and more) to your model objects. I am very excited by the future of this project, and I would encourage anyone who is interested to join the mailing list and help us to move from beta to our goal of becoming an official SQLAlchemy extension.

Comment

  1. It looks pretty interesting, thanks for this work !
    Jonathan Ballet    1921 days ago    #
  2. If I could show you how to change:

    from lib import *

    class Movie(Entity):
        has_field(‘title’, Unicode(60))
        has_field(‘description’, Unicode(512))
        has_field(‘releasedate’, DateTime)

    into:

    from lib import Entity, Field

    class Movie(Entity):
        title = Field(unicode, 60)
        description = Field(unicode, 512)
        releasedate = Field(datetime.datetime)

    ...would you be interested? I’ve found that syntax much more readable in Dejavu. Drop me an email or catch me at PyCon if so.
    Robert Brewer    1921 days ago    #
  3. +1 to Robert’s suggestion. It’d probably mean some metaprogramming, but declarative fields are ever so nice.
    James Bennett    1921 days ago    #
  4. You have come up with a great declarative syntax.

    Is it threadsafe ? What happens if statements for two classes are being executed interleaved with each other ?

    I’m only curious…
    Fuzzyman    1921 days ago    #
  5. Personally I think the current syntax:

    has_field(‘title’, Unicode(60))
    has_field(‘description’, Unicode(512))
    has_field(‘releasedate’, DateTime)

    is much more readable than:

    class Movie(Entity):
    title = Field(unicode, 60)
    description = Field(unicode, 512)
    releasedate = Field(datetime.datetime)

    My 2 cents worth…
    Fuzzyman    1921 days ago    #
  6. Robert: the two pre-existing mappers that this one replaces both used metaclass trickery to implement that style interface. I’m pretty sure the current style was a deliberate design decision.
    Karl G    1921 days ago    #
  7. @Robert Brewer

    TurboEntity uses that syntax, so I’m sure they know how to do that. In any case, I admit I find it a little easer to read myself. I’d be interested in knowing why that sytax was rejected.
    Waylan Limberg    1921 days ago    #
  8. IMHO spike from OSDL/Chandler can be used for schema

    http://builds.osafoundation.org/chandler/releases/0.7alpha2/internal/Spike/src/spike/
    Niki Spahiev    1920 days ago    #
  9. Hello Robert, I’d be even more concise:

    class Movie(Entity):
    title = Unicode(60)
    description = Unicode(512)
    releasedate = DateTime()
    Tarek Ziadé    1920 days ago    #
  10. Yes, to everyone suggesting an alternate syntax: the approach we took is a deliberate design decision. It already requires some metaprogramming, which doesn’t bother me at all.

    The decision was made to go with this DSL-like syntax because of how readable we found it, and because of some of the things we can do by allowing people to create their own DSL “statements” to extend their model objects. I will be posting a tutorial on how to do this very soon.

    Thanks for all the feedback!
    Jonathan LaCour    1920 days ago    #
  11. +1 on Roberts suggestion. Especially the bit about avoiding to import *

    But, if you listen to everybodys comments you’ll soon end up with what looks like Zope3 code which was invented years ago.
    Peter Bengtsson    1919 days ago    #
  12. Thankfully, I won’t be listening to everybody’s comments, because I definitely don’t want to clone Zope in any way shape or form!

    Also, import * should only be used in examples, and probably not even then. Its certainly not a requirement to import everything when using Elixir.
    Jonathan LaCour    1919 days ago    #
  13. I don’t like this style of non-declarative binding.

    At the least my IDE will struggle recognising these as class attributes?? No thanks!!!

    How are these attributes referred to statically? Is this not breaking some things in Python?
    Nick Bower    1913 days ago    #
  14. Nick,

    If you don’t like it, you don’t have to use it.

    Also, if you use traditional SQLAlchemy mapping by defining your table and your class separately, your IDE still won’t see any fields, so in this way Elixir is no different from SQLAlchemy, except with Elixir you will at least see the definition of your fields on the class itself. Oh, and its not breaking anything in Python.

    Besides, the IDE/static argument smells terribly of Java. Python is optimized for your brain, not your IDE.
    Jonathan LaCour    1913 days ago    #
  15. Just to chime in one more time about your syntax. Did you look at the enthought.traits syntax—it already does a tremendous amount of work in the area of manifest typing, validation, syntactic sugar, MVC, etc., etc.

    an example:

    class Person(HasTraits):
    name = Str
    age = Float
    hair_color = Enum(“brown”, “blonde”, “red”)

    p = Person()

    We use this for fat-client apps and have automatic GUI generation, an observer pattern, delegation, validation, and much more.

    By the way, it’s quite fast (faster ‘get’ than raw python properties, slightly slower ‘set’).

    Just a “heads-up.”

    http://code.enthought.com/traits
    travis    1911 days ago    #

commenting closed for this article