Syntax: attribute-based vs has_field and with_fields

We moved the default syntax from the DSL-syntax like below:

class Person(Entity):
    has_field('name', Unicode(128))
    has_field('birthdate', Date)
    belongs_to('father', of_kind='Person')

to a more usual-looking, attribute-based syntax:

class Person(Entity):
    using_options(tablename='person')

    name = Field(Unicode(128))
    birthdate = Field(Date)
    father = ManyToOne('Person')

The old with_fields-based syntax is deprecated and will disappear in a few releases, once everybody has had a chance to upgrade. The has_field syntax as above is half-deprecated, meaning it's not the default syntax anymore but will stay in for longer, if not forever. As you can see in the above example, the DSL syntax remains the default for options. If you want to do the switch and have already quite a few models using the has_field syntax, you might be interested in this automated migration script. It's a quite crude bash script and is provided as is without any warranty (you should backup your code before running it). It just did the job for me. If you make it better/nicer, feel free to send any improvements to the mailing list and/or as a trac ticket.

Automatic VS manual setup

One of the most important (even if not the most visible) change in version 0.4 is that your entities (classes) are not setup as soon as the entity is declared anymore. But what does it mean when an entity is setup, you'll ask? By setup, I mean that the (SQLAlchemy's) table and mapper objects are created. So now when you declare your class, nothing of that happens. From that point, you have two options:

  • you decide yourself when you want Elixir to do that setup phase. It means that, at one point, you tell Elixir to setup the entities you have already declared by using setup_all() or setup_entities([...]) explicitly. You usually do this after you have declared all your entities, so that they can find each other without problem. This is the recommended approach even though it is not the default in version 0.4 (it is in version 0.5). To use this method of setup in version 0.4 you have to configure your entities with the autosetup=False option. This can be done globally, for all your entities, by adding the following line: elixir.options_defaults['autosetup'] = False once and before you declare any of your entities.
  • you let Elixir guess when you want that setup phase to happen. For that your entities need to have the autosetup=True option. For backward-compatibility reasons, this is the default in version 0.4, even though it is not the recommended way. In that case, the setup phase will happen as late as possible: it will only happen if and when any of your autosetup entities are used in any of the following ways for the first time:
    • it is instantiated,
    • some of its attributes are accessed (c, query, table, or mapper),
    • or if the create_all method of the metadata containing its table is called.

Module-level metadata (and session) attributes

A little known (and undocumented) feature of Elixir 0.3 was to set an alternate metadata for all the entities of a module at once by defining a metadata attribute for that module. This had the nasty side-effect that people using:

from elixir import *

in a module also set unconsciously all the entities of that module to use Elixir's default metadata (unless explicitly specified for each entity). And if that person was using a framework like TurboGears which require you to use its own metadata, he was very likely to be screwed.

The session attribute had the same problem (but it was introduced post 0.3). So, to make a long story short, the metadata module-level attribute becomes __metadata__ and the session module-level attribute becomes __session__.

objectstore becomes session

You should use session instead. And by the way, now it's possible to provide your own session, configured to your needs, so we encourage you to do so.

For example:

objectstore.flush()

becomes

session.flush()

Query methods on the class are deprecated

All those query methods (which were inherited from assign_mapper) will now throw a deprecated warning. You should use the "query" attribute on your class instead.

MyClass.filter(...)

becomes

MyClass.query.filter(...)

The concerned methods are: filter, filter_by, select, select_by, selectfirst, selectfirst_by, selectone, selectone_by, join_to, join_via, count, count_by, options and instances.

The only survivors to that deprecation frenzy are the get and get_by methods which are left as shortcuts.

MyClass.get_by(...)

is equivalent to:

MyClass.query.filter_by(...).first()

If you don't want to have any of those methods at all, or if, on the contrary, you want those methods back without the deprecation warning, you can provide your own base class for your entities by simply using the EntityMeta metaclass, as for example in this base class which only provides the convenient __init__ method which allows you to set values to your instance by using kwargs.

class MyBase(object):
    __metaclass__ = EntityMeta

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

Attachments