A (possible) cleaner ORM fields description
|Reported by:||mmarshall||Owned by:||Adrian Holovaty|
|Has patch:||no||Needs documentation:||no|
|Needs tests:||no||Patch needs improvement:||no|
Here is a cleaner approach to the ORM fields. Using this patch, the example in the tutorial can be changed to this:
from django.core.meta import Model # Create your models here. class Poll(Model): def fields(Field): Field.Char('question', maxlength=200) Field.DateTime('pub_date', 'date published') class Choice(Model): def fields(Field): Field.ForeignKey(Poll) Field.Char('choice', 'choice', maxlength=200, core=True) Field.Integer('votes', 'votes', core=True)
I have done an implementation, which is fully backwards compatible, and quite simple.
Index: django/core/meta.py =================================================================== --- django/core/meta.py (revision 227) +++ django/core/meta.py (working copy) @@ -377,12 +377,53 @@ new_v.func_globals[k] = func new_functions[k] = func + +class FieldGenerator(object): + """FieldGenerator is used for... uh... Generating fields. It has attributes, + such as "Char" and "DateTime", that can be used to create class instances such as + "CharField" or "DateTimeField". These instances are then added to the list "fields", + which can be retrieved to be used as the "fields" attribute in a Model class.""" + def __init__(self): + self.fields =  + self.last_class = None #This is the class obj that corrisponds to the last __getattr__ call. + + def __getattr__(self,name): + self.last_class = None + try: + obj = eval(name+"Field") + if issubclass(obj,Field): self.last_class = obj + except NameError: pass + if not self.last_class: # If name+"Field" didn't work, just try name. + try: + obj = eval(name) + if issubclass(obj,Field): self.last_class = obj + except NameError: pass + if not self.last_class: + raise AttributeError("Could not find either %s or %sField (or they are not decendants of Field.)" % (name,name)) + return self.GenerateField + + def GenerateField(self, *args, **kargs): + """ This creates an instance of the class corrisponding to the + last __getattr__ call, and adds it to self.fields.""" + if not self.last_class: + raise ValueError("GenerateField should only be called with self.last_class set.") + self.fields.append(self.last_class(*args, **kargs)) + self.last_class = None + + class ModelBase(type): "Metaclass for all models" def __new__(cls, name, bases, attrs): # If this isn't a subclass of Model, don't do anything special. if not bases: return type.__new__(cls, name, bases, attrs) + + # If 'fields' is callable, we want to call it, passing a FieldGenerator. + if callable(attrs['fields']): + fg = FieldGenerator() + attrs['fields'](fg) + attrs['fields'] = fg.fields # If this model is a subclass of another Model, create an Options # object by first copying the base class's _meta and then updating it
I don't know if this is the best way of doing it... but it's out there!