Generating Elegance
There was a moment on Friday that sums up why I like to teach. As part of preparing for PyCon’08, David Wolever is tidying up DrProject’s database schema. One task is to rationalize the handling of enumerations such as ticket states: at present, these are variously represented as:
- string columns in the database;
- integer columns that index into tables that store the corresponding human-readable string; and
- integer columns that are translated into strings in the code.
The cleaned-up version is to have one table that stores all enumerated values:
- whatfor (string): name of enumeration
- name (string): name of enumeration elements
- value (string): integer value of that element (for sorting and cross-referencing)
which gives:
whatfor | name | value |
---|---|---|
'ticket_type' | 'bug' | 0 |
'ticket_type' | 'enhancement' | 1 |
'ticket_type' | 'task' | 2 |
'user_state' | 'pending_confirmation' | 0 |
'user_state' | 'registered' | 1 |
... | ... | ... |
Using SQLAlchemy and Elixir, this schema is:
class Enum(Entity): has_field('whatfor', Unicode(), primary_key = True) has_field('name', Unicode(), primary_key = True) has_field('value', Integer()) using_options(tablename='enums', order_by=['value'])class Ticket(Entity): belongs_to('status', of_kind='Enum')
The problem is, we also want to define default values for the fields whatfor
and name
when declaring those columns. Normally, this would be done like this:
belongs_to( 'status', of_kind='Enum', column_kwargs = {'default': lambda 'some_default'} )
But that doesn’t work in this case, because there are two fields in the primary key of Enum
(and they both get assigned 'some_value'
). In the long run, there doesn’t seem to be any reason why we shouldn’t be able to specify per-column settings when the primary key spans multiple fields, like this:
belongs_to( 'status', of_kind='Enum', per_column_kwargs = { 'whatfor' : { 'default' : 'foo'}, 'value' : { 'default' : 'bar'} } )
But that doesn’t work yet, so David came up with this:
def enum_default(whatfor, value): def _enum_generator(whatfor, value): yield whatfor yield value
return _enum_generator(whatfor, value).next belongs_to( 'status', of_kind='Enum', column_kwargs = { 'default': enum_default('foo', 'bar') } )
It took me a moment to figure it out, but when I did, I thought it was actually rather elegant. I wonder what I’ll learn from my students next week?