I'm gonna ignore comparsions beetween Django and Zope about the schema defintion, since the former uses a relational DB as backned while the latter uses a OODB, but Subway (hence SQLObject) approach has some vitues that would be cool to port to Django.
The model in Django is a lot more feature-rich than the Subway one and it's desiderable to avoid name clashes between fields, so it would be good to have a way to wrap fields into a private namespace. Taking the tutorial example, i was wondering if something like this could work:
from django.core import meta
class Poll(meta.Model):
class fields(object): question = meta.CharField('question', maxlength=200) pub_date = meta.DateTimeField('date published')
# but how do we handle FK?
it reads better, the a = b pattern looks more natural to my pythonic eyes. I can tell immediately where are the fields available, just look on the left. actually i don't known how hard is to change current ORM code to play well with the new approach, but i would like to brought up the point.
> class Poll(meta.Model): > class fields(object): > question = meta.CharField('question', maxlength=200) > pub_date = meta.DateTimeField('date published')
> # but how do we handle FK?
I agree that this is prettier and more readable, and, for the record, I'm 100% open to changing model syntax at this point, with no regard for backwards compatibility. (We at World Online will manually convert our dozens of models to the new syntax if need be -- and we're the only people using Django in production at this point.)
Once we have our first public release, our intention is to be backwards-compatible from that point. So now is the time to make these design decisions.
Although it's prettier, your proposal has a few interesting problems:
* As you pointed out, how does it handle ForeignKey()? * "class fields(object):" is crufty
That is, use leading underscores on non-field attributes. It's kind of ugly/crufty. Is it worth changing? Any other ideas?
Also, I'll just throw out there that it would be possible to have different model syntaxes, using subclasses:
class Poll(meta.LeanModel): question = meta.CharField(maxlength=200) pub_date = meta.DateTimeField()
class Poll(meta.DictFieldModel): dict_fields = { 'question': meta.CharField(maxlength=200), 'pub_date': meta.DateTimeField(), }
This blatantly goes against Python's "One obvious way to do it" philosophy, but we might use this technique to support old models as a stop gap -- i.e., if we change model syntax, we can tell people to change their model subclasses to "meta.OldModel" as an immediate fix, before they have time to convert models to the "real" new model syntax.
> That is, use leading underscores on non-field attributes. It's kind of > ugly/crufty. Is it worth changing? Any other ideas?
What about this:
class Poll(meta.Model) # your models fields go here question = meta.CharField(maxlength=200) pub_date = meta.DateTimeField()
# Django specific config goes here. (No icky underscores.) class metadata(): admin = meta.Admin(...) ordering = [('pub_date', 'DESC')]
As a bonus suggestion:
To add fuel to the fire, this would give you a viewable model in your web browser without any extra code required (when quickstarting newbies the first time):
class Poll(meta.Model): use_scaffolding = True # :-)
I am not sure what to think about the proposed solutions, but I do have another idea to throw out there:
import django.core.meta as meta
import django.core.meta.field as field
class Poll(meta.Model):
def fields():
field.Char('question', maxlength=200)
field.DateTime('pub_date', 'date published')
class Choice(meta.Model):
def fields():
field.ForeignKey(Poll)
field.Char('choice', maxlength=200)
field.Integer('votes')
IMHO, this looks pretty clean. It would take a few tricks to implement, however.
The idea would be, that before fields() is called, sys.stdio (or wherever print writes to... I cannot remember) is replaced so that it is captured.
The field.* are functions, which return a string that can be exec()'ed to create the old meta.*Field classes.
Just some fuel for thought... probably could use some work.
> class Poll(meta.Model) > # your models fields go here > question = meta.CharField(maxlength=200) > pub_date = meta.DateTimeField()
> # Django specific config goes here. (No icky underscores.) > class metadata(): > admin = meta.Admin(...) > ordering = [('pub_date', 'DESC')]
you guys might already known it, but this approach is what recent SQLObject revisions uses (post 0.6.1), developers removed underscored prefixed "special" names and wrapped into a "sqlmeta" class. for example:
class Entry(SQLModel):
class sqlmeta(object): table = 'entry' # explicit table name
class Poll:
CharField('question', maxlength=200)
DateTimeField('date published')
Model(Poll)
Is better...
Anyhow, it's sorta hacky is the problem.
What you do is have the *Fields as generator functions,
and a variable in django.core.fields holds a list of created fields.
And then Model would grab that list, apply the fields to the passed in
class, and then empty the list for the next group.
And now that I think of it, you couldn't do:
class Poll:
etc...
class Choice:
etc...
Model(Poll)
Model(Choice)
Which a lot of people would try doin'.
On 7/19/05, Matthew Marshall <mmarsh...@myrealbox.com> wrote:
deelan wrote: > you guys might already known it, but this approach is what recent > SQLObject revisions uses (post 0.6.1), developers removed underscored > prefixed "special" names and wrapped into a "sqlmeta" class. for > example:
Ahh.. I didn't know that about SQLObject > 0.6.1 (I've only played around with 0.6.1).
Sounds like that myth about the 100th monkey. :-) All converging on the same good idea.
The biggest problem I can see here, is the 'Model(Poll)' line. (aside from requiring global variables.) What do you think about this:
from django.core.fields.meta import Model
class Poll(Model):
def fields(Field):
Field.Char('question', maxlength=200)
Field.DateTime('pub_date', 'date_published')
'Field' would be an object, with functions that generate the meta.*Field instances, and put them into a list. This list would then be assigned (internally) to self.fields.
This would be 100% backwards compatible, and almost trivial to implement. I might just go and make a patch now... just for fun.
MWM
On Tuesday 19 July 2005 06:51 pm, Brant Harris wrote:
> Anyhow, it's sorta hacky is the problem.
> What you do is have the *Fields as generator functions,
> and a variable in django.core.fields holds a list of created fields.
> And then Model would grab that list, apply the fields to the passed in
> class, and then empty the list for the next group.
> And now that I think of it, you couldn't do:
> class Poll:
> etc...
> class Choice:
> etc...
> Model(Poll)
> Model(Choice)
> Which a lot of people would try doin'.
> On 7/19/05, Matthew Marshall <mmarsh...@myrealbox.com> wrote:
> > On Tuesday 19 July 2005 05:41 pm, deadwis...@gmail.com wrote:
> > > Let me just put my idea out. I think it is the cleanest.
> > Well, AFAIK decorators only apply to functions, but other than that...
> > I think this looks best too, but I don't see any way to implement it. Am
> > I missing something? Won't whatever was returned by the classes be lost?
def FillFields(cls):
global fields
for field in fields:
setattr(cls, field, field)
fields = []
return cls
def CharField(name, **kw):
global fields
fields.append(name)
class Field:
pass
class ModelBase(type):
def __new__(cls, cls_name, bases, locals):
print repr(cls), repr(cls_name), repr(bases), repr(locals)
f = Field()
FillFields(f)
for k,v in locals.items():
setattr(f, k, v)
return f
class Model:
pass
Model.__class__ = ModelBase
class Article(Model):
CharField("headline")
something = 0
I'm going to throw my hat -- hopefully not my head along with it --
into the ring: I don't like any of the proposed options so far. These are the problems I see in the proposals thus far:
* Matthew's proposals (the ones involving "def fields()") strike me
as too magic and crufty: the business with capturing sys.stdout just
scares me, and the business with the Field object as the parameter to
the function doesn't seem any clearer than the current field tuple.
* Adrian's (and Ian's) proposals involving leading underscores is OK,
but leading underscores are supposed to be used for private APIs, not
public member variables, and beyond that it just "smells" wrong to me.
* Jason's idea of a nested metadata subclass (echoed by Ian's
description of how sqlobject works) seems OK, too, but it still
doesn't solve the ForeignKey problem, and what if I want to have a
field named "metadata"? This also holds for leading underscrores --
I need to be able to name a field anything I want. Why? Think about
wrapping an already-existing database structures; if there's ANY
limitiation of names whatsoever I could be SOL.
* Brant's idea of "class decorators" is, by his own definition,
hackish, and suffers from the same problem that pre-2.4 function
decorators did of coming "after the fact", as it were.
* Ian's idea of a DjangoSQLObject has a lot of merit; I love and use
SQLObject quite a bit. Still, it's completely possible to use
SQLObject with Django as it is; just don't use DJango's ORM (yeah,
you'll lose the admin interface, form validation, the use of generic
views...)
Like Adrian, I'm open to reworking the data descriptions -- somewhere
on this huge hard drive of mine I've got a half-finished XML-based
data description implementation which I maybe should dredge up -- but
in order for me to be interested in another situation, it has to:
* Preserve field "order" (metaclasses get an attribute dict, so it's
impossible to tell which field in systems like SQLObject came first)
* Deal with metadata on a first-class basis -- fields are important,
sure, but the other options you can pass to a model are equally
important, at least to me.
Jacob Kaplan-Moss wrote:
> * Preserve field "order" (metaclasses get an attribute dict, so it's
> impossible to tell which field in systems like SQLObject came first)
Yes, I find this awkward as well. To fix it in SQLObject the *Col classes would, on instantiation, get a number from a global counter. Then later you can sort based on this number, essentially sorting by order of creation.
> I'm going to throw my hat -- hopefully not my head along with it --
> into the ring: I don't like any of the proposed options so far.
> These are the problems I see in the proposals thus far:
> * Matthew's proposals (the ones involving "def fields()") strike me
> as too magic and crufty: the business with capturing sys.stdout just
> scares me, and the business with the Field object as the parameter to
> the function doesn't seem any clearer than the current field tuple.
> * Adrian's (and Ian's) proposals involving leading underscores is OK,
> but leading underscores are supposed to be used for private APIs, not
> public member variables, and beyond that it just "smells" wrong to me.
> * Jason's idea of a nested metadata subclass (echoed by Ian's
> description of how sqlobject works) seems OK, too, but it still
> doesn't solve the ForeignKey problem, and what if I want to have a
> field named "metadata"? This also holds for leading underscrores --
> I need to be able to name a field anything I want. Why? Think about
> wrapping an already-existing database structures; if there's ANY
> limitiation of names whatsoever I could be SOL.
> * Brant's idea of "class decorators" is, by his own definition,
> hackish, and suffers from the same problem that pre-2.4 function
> decorators did of coming "after the fact", as it were.
> * Ian's idea of a DjangoSQLObject has a lot of merit; I love and use
> SQLObject quite a bit. Still, it's completely possible to use
> SQLObject with Django as it is; just don't use DJango's ORM (yeah,
> you'll lose the admin interface, form validation, the use of generic
> views...)
> Like Adrian, I'm open to reworking the data descriptions -- somewhere
> on this huge hard drive of mine I've got a half-finished XML-based
> data description implementation which I maybe should dredge up -- but
> in order for me to be interested in another situation, it has to:
> * Preserve field "order" (metaclasses get an attribute dict, so it's
> impossible to tell which field in systems like SQLObject came first)
> * Deal with metadata on a first-class basis -- fields are important,
> sure, but the other options you can pass to a model are equally
> important, at least to me.