Writing the patches ended up being pretty straightforward. Included are
the following:
* A patch to elixir/entity.py (assignment_syntax.patch) that adds a
process_attrs() method to EntityMeta, and calls it in __init__().
* A patch to docs/tutorial.rst (tutorial_assignment_syntax.patch) which
updates the code samples to use class assignment rather than
with_fields.
* The test_attr_syntax.py file, which is mostly a copy of
test_movies.py, just with the class attribute syntax.
All the patches are against SVN revision 193.
All tests still run successfully with SQLAlchemy 0.4. I did have to
make an unrelated fix on elixir/ext/associable.py to get it to work
with 0.4, but that was the case before or after these patches. I don't
have access to a machine with 0.3 right now, but I will tomorrow.
Let me know how this is.
Adam
On Thu, 30 Aug 2007 08:12:47 -0700
AdamG <[EMAIL PROTECTED]> wrote:
>
>
>
> ---------- Forwarded message ----------
> From: AdamG <[EMAIL PROTECTED]>
> Date: Aug 30, 8:54 am
> Subject: DSL vs. OOP Syntax
> To: SQLElixir
>
>
> On Aug 29, 10:36 am, Jonathan LaCour <[EMAIL PROTECTED]>
> wrote:
>
>
>
> > Okay, I have had lots of discussions on this over the past few
> > months, and its becoming more and more clear that the majority of
> > people prefer the more traditional approach when it comes to
> > defining fields. So, with that said, I think we need to listen to
> > our users here, and start supporting the traditional syntax, but
> > only for defining fields. I think the relation style needs to
> > remain as a DSL-like approach.
>
> > That being said, I think its time that we went ahead and deprecated
> > `with_fields` in favor of the "OO" approach, since the syntaxes
> > will end up being quite similar, with the added bonus of preserving
> > field order:
>
> > class Person(Entity):
> > id = Field(Integer, primary_key=True)
> > name = Field(String)
>
> > has_many('pets', of_kind='Pet', inverse='owner')
>
> > In the long-term, we might want to deprecate `has_field` as well, so
> > that we will have only one way to do things. I am hesitant to do
> > this right now, but I haven't been happy having two ways to do the
> > same thing for some time now, and I think we need to resolve this
> > at some point.
>
> > So, the long and short of it is that I will finally break down, so I
> > don't have to hear about it anymore ;) So, whoever wants this,
> > please go ahead and work on a patch, and we'll try to get it into
> > the 0.4 release so that people can use it sooner rather than
> > later. I want this to be the last major API breakage before we
> > release 0.4, and hopefully for a long time to come. The patch
> > should contain tests and documentation updates before we'll accept
> > it.
>
> Sounds good! I've got a test set up with that syntax, and am trying to
> add in the support by adding a function to handle the dict_ parameter
> on the init function of EntityMeta - would that be the right place to
> do this? I'm mostly using the source of Statement.process and one of
> the earlier TurboEntity releases as references, so if there's a better
> place for it to be, let me know. It's not working as of yet, but the
> long weekend coming up should be a good time to get it finished &
> submitted. Should the patches be submitted to Trac now, or directly to
> the devs, or ...?
>
> Thanks,
> Adam Gomaa
>
--
Adam Gomaa
[EMAIL PROTECTED]
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"SQLElixir" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/sqlelixir?hl=en
-~----------~----~----~----~------~----~------~--~---
from elixir import *
from elixir.events import *
def setup():
global Director, Movie, Actor, Media
class Director(Entity):
name = Field(Unicode(60))
has_many('movies', of_kind='Movie', inverse='director')
class Movie(Entity):
"""A simple Movie class
"""
title = Field(Unicode(50))
year = Field(Integer)
belongs_to('director', of_kind="Director", inverse='movies')
has_and_belongs_to_many('actors', of_kind="Actor", inverse='movies')
has_one('media', of_kind="Media", inverse='movie')
class Actor(Entity):
name = Field(Unicode(60))
has_and_belongs_to_many('movies', of_kind="Movie", inverse='actors')
class Media(Entity):
number = Field(Integer, primary_key=True)
belongs_to('movie', of_kind='Movie', inverse='media')
metadata.bind = "sqlite:///"
def teardown():
cleanup_all()
class TestAttrSyntax(object):
def setup(self):
create_all()
def teardown(self):
drop_all()
objectstore.clear()
def test_backref(self):
swars = Movie(title="Star Wars", year=1977)
glucas = Director(name="George Lucas")
swars.director = glucas
# does it work before a flush?
assert swars in glucas.movies
def test_bidirectional(self):
brunner = Movie(title="Blade Runner", year=1982)
alien = Movie(title="Alien", year=1979)
swars = Movie(title="Star Wars", year=1977)
brunner.media = Media(number=1)
m = Media(number=7)
m.movie = alien
rscott = Director(name="Ridley Scott")
glucas = Director(name="George Lucas")
hford = Actor(name="Harrison Ford")
mhamill = Actor(name="Mark Hamill")
sweaver = Actor(name="Sigourney Weaver")
rscott.movies.append(brunner)
rscott.movies.append(alien)
swars.director = glucas
swars.actors.append(hford)
swars.actors.append(mhamill)
alien.actors.append(sweaver)
brunner.actors.append(hford)
objectstore.flush()
objectstore.clear()
# directors
assert Movie.get_by(title="Alien").director is Director.get_by(name="Ridley Scott")
assert Director.get_by(name="Ridley Scott").name == "Ridley Scott"
assert Movie.get_by(title="Alien").director.name == "Ridley Scott"
assert Movie.get_by(title="Star Wars").director is Director.get_by(name="George Lucas")
# movie
assert Movie.get_by(title="Blade Runner").year == 1982
assert Movie.get_by(title="Alien").year == 1979
# actors
assert Actor.get_by(name="Harrison Ford") in Movie.get_by(title="Blade Runner").actors
assert Actor.get_by(name="Harrison Ford") in Movie.get_by(title="Star Wars").actors
assert Movie.get_by(title="Star Wars") in Actor.get_by(name="Mark Hamill").movies
assert Movie.get_by(title="Blade Runner") in Actor.get_by(name="Harrison Ford").movies
# media
assert Movie.get_by(title="Blade Runner").media is Media.get_by(number=1)
assert Movie.get_by(title="Blade Runner").media.number is Media.get_by(number=1).number
assert Actor.get_by(name="Sigourney Weaver") in Media.get_by(number=7).movie.actors
if __name__ == '__main__':
setup()
test = TestAttrSyntax()
test.setup()
test.test_bidirectional()
test.teardown()
teardown()
diff -r cde87ce5bb7f -r 90f24f3ab661 elixir/entity.py
--- a/elixir/entity.py Thu Aug 30 10:59:06 2007 -0400
+++ b/elixir/entity.py Thu Aug 30 11:49:47 2007 -0400
@@ -406,6 +406,15 @@ class EntityDescriptor(object):
class TriggerProxy(object):
+ """A class that serves as a "trigger" ; accessing it's attribues runs
+ the function that is set at initialization.
+
+ Primarily used for setup_all().
+
+ Note that the `setupfunc` parameter is called on each access of
+ the attribute.
+
+ """
def __init__(self, class_, attrname, setupfunc):
self.class_ = class_
self.attrname = attrname
@@ -419,6 +428,7 @@ class TriggerProxy(object):
def __repr__(self):
proxied_attr = getattr(self.class_, self.attrname)
return "<TriggerProxy (%s)>" % (self.class_.__name__)
+
class EntityMeta(type):
"""
@@ -443,6 +453,9 @@ class EntityMeta(type):
# process statements. Needed before the proxy for metadata
Statement.process(cls)
+
+ # Process attributes, for the assignment syntax.
+ cls.process_attrs(dict_)
# setup misc options here (like tablename etc.)
desc.setup_options()
@@ -507,6 +520,37 @@ class EntityMeta(type):
return Query(cls, session=cls._descriptor.objectstore.session)
q = property(q)
+ def process_attrs(cls, attr_dict):
+ """Process attributes in the class, replacing with the
+ instantiated/bound Field objects.
+
+ The method used here is similar to the 0.1.0 TurboEntity
+ release; see ModelMeta.__init__.
+
+ """
+
+ # For example, in:
+ # name = Field(String)
+ # We get {'name': Field(String)} here.
+ for attr, field in attr_dict.iteritems():
+ # Check if it's an Elixir field.
+ if isinstance(field, Field):
+ # No colname is defined, so set it to the name of the
+ # attr. See Field.__init__; it would be a 'colname'
+ # kwarg if it were overriden explicitely.
+ if field.colname is None:
+ # So, for our example, the Field.colname will be 'name'.
+ field.colname = attr
+ # Call the EntityDescriptor.add_field method
+ cls._descriptor.add_field(field)
+ else:
+ # Not an Elixir field, let it be.
+ pass
+ return
+
+
+
+
class Entity(object):
'''
@@ -530,7 +574,6 @@ class Entity(object):
For further information, please refer to the provided examples or
tutorial.
'''
-
__metaclass__ = EntityMeta
def __init__(self, **kwargs):
diff -r cde87ce5bb7f -r c8259088de05 docs/tutorial.rst
--- a/docs/tutorial.rst Thu Aug 30 10:59:06 2007 -0400
+++ b/docs/tutorial.rst Thu Aug 30 12:00:11 2007 -0400
@@ -37,11 +37,9 @@ containing the following lines:
metadata.connect("sqlite:///movies.sqlite")
class Movie(Entity):
- with_fields(
- title = Field(Unicode(30)),
- year = Field(Integer),
- description = Field(Unicode)
- )
+ title = Field(Unicode(30))
+ year = Field(Integer)
+ description = Field(Unicode)
def __repr__(self):
return '<Movie "%s" (%d)>' % (self.title, self.year)
@@ -61,12 +59,12 @@ have, but fully optional. We have put t
have, but fully optional. We have put this into our model so that we can
easily trace what is happening in an interactive python interpreter.
-Also, please note that elixir currently provide two different ways to declare
-the fields on your entities. We have not decided yet on which one we like best,
-or if we will always keep both. The other way to declare your fields is using
-the ``has_field`` statement, rather than the ``with_fields`` statement. The
-``Movie`` example above can be declared using the ``has_field`` statement like
-so:
+Also, please note that elixir currently provide two different ways to
+declare the fields on your entities. We have not decided yet on which
+one we like best, or if we will always keep both. The other way to
+declare your fields is using the ``has_field`` statement, rather than
+assigning directly to the class attributes. The ``Movie`` example
+above could be declared using the ``has_field`` statement like so:
::
@@ -150,9 +148,8 @@ entity "``Genre``" to our ``model.py``:
::
class Genre(Entity):
- with_fields(
- name = Field(Unicode(15), unique=True)
- )
+ name = Field(Unicode(15), unique=True)
+
def __repr__(self):
return '<Genre "%s">' % self.name
@@ -168,11 +165,9 @@ excitement. Add two lines to your ``mode
::
class Movie(Entity):
- with_fields(
- title = Field(Unicode(30)),
- year = Field(Integer),
- description = Field(Unicode)
- )
+ title = Field(Unicode(30)),
+ year = Field(Integer),
+ description = Field(Unicode)
belongs_to('genre', of_kind='Genre') # add this line
@@ -181,9 +176,7 @@ excitement. Add two lines to your ``mode
class Genre(Entity):
- with_fields(
- name = Field(Unicode(15))
- )
+ name = Field(Unicode(15))
has_many('movies', of_kind='Movie') # and this one