4

I need class runtime class declaration from database.

Some class need override field and redeclaration.

models.py

class Staff(object):
    name = StringField( verbose_name = "Full name")
    age = IntegerField( verbose_name = "Age")

utils.py

def class_changer(class_path, field_key, new_Field):
    pass
    ?????? 
>>> class_changer("models.Staff", "gender", BooleanField()) # Add new field to Staff
>>> class_changer("models.Country", "name", StringField()) # Add new class with name field
>>> class_changer("models.Staff", "country", ForeignKey("Country")) # Add new field to Staff

result is

class Staff(object):
    name = StringField( verbose_name = "Full name")
    age = IntegerField( verbose_name = "Age")
    gender = BooleanField()
    country = ForeignKey("Country")

class Country(object):
    name = StringField()

How to implement class_changer?

shahjapan
  • 13,637
  • 22
  • 74
  • 104
Ankhaa
  • 1,128
  • 2
  • 12
  • 17
  • combination of `setattr` & `eval` may help you - not robust but easy to implement – shahjapan Nov 10 '10 at 09:05
  • i found too many method. http://en.wikipedia.org/wiki/Aspect-oriented_programming eval , setattr, decorator .... But i am not sure how witch method better???. i not have few experience. – Ankhaa Nov 10 '10 at 09:21
  • 1
    It's clear that you're doing this with django so you should be aware that thinking about doing this hurts my head because of all the metaclasses that they use: everything is connected to everything else. I'm normally a pretty happy go lucky programmer too but I'm going to just walk away from this one. I recommend that you do as well and think of a better architecture. What are you trying to actually accomplish? – aaronasterling Nov 10 '10 at 10:04
  • well, I am trying to build Dynamic Model class. But not Django model. I am using use http://mongoengine.org/ and Django url, view, template, etc... MongoEngine ModelClass is like django ModelClass. Mongomodel class is much easier than modelclass, u can try it. – Ankhaa Nov 11 '10 at 06:28

2 Answers2

4

you need better architecture for this, but as a starting solution you may try this,

In [12]: class a1(object):
   ...:     pass

In [13]: def class_changer(cls_path, attr, val):
   ....:     try:
   ....:         obj = eval(cls_path)
   ....:         setattr(obj, attr, val)
   ....:     except:
   ....:         raise 
   ....:     
In [14]: def getGender(self):
   ...:     return True


In [15]: class_changer('a1','getGender', getGender)

In [16]: a1().getGender()
Out[16]: True
shahjapan
  • 13,637
  • 22
  • 74
  • 104
  • Read [this question](http://stackoverflow.com/questions/1832940/is-using-eval-in-python-a-bad-practice), especially the accepted answer and discussion around it, to understand some of the risks and caveats of `eval`. This is not to say that the solution is bad, but you should know what you are getting into. – Björn Pollex Nov 10 '10 at 09:23
  • yeah eval is harmfull thats truth all should know, but exactly when users's input is gonna evaluated, if programmer writes eval for his own purpose - then regardless of statement it shouldn't be considered as risk - it should be considered as flexibility or programmer's choice or feature whatever.... – shahjapan Nov 10 '10 at 16:59
1

First adding new attributes to a class:

>>> class_changer("models.Staff", "gender", BooleanField()) # Add new field to Staff
>>> class_changer("models.Staff", "country", ForeignKey("Country")) # Add new field to Staff

For these two, just go ahead and set Staff directly:

models.Staff.gender = BooleanField()
models.Staff.country = ForeignKey("Country")

Or to make it generic:

def add_to_class(cls, name, attr):
    setattr(cls, name, attr)

add_to_class(models.Staff, "gender", BooleanField())
add_to_class(models.Staff, "country", ForeignKey("Country"))

Second, creating a new class:

>>> class_changer("models.Country", "name", StringField()) # Add new class with name field

You can create a class in a function and then assign it to a module:

def new_class(mod, name):
    class New(object): pass
    setattr(mod, name, New)

new_class(test, "Country")
add_to_class(test, "Country", StringField())

I'm not sure you'd want to combine new_class and add_to_class, but I suppose you could do:

def create_if_needed_and_add_to_class(mod, clsname, attrname, value):
    if clsname not in dir(mod):
        new_class(mod, clsname)
    add_to_class(mod, attrname, value)

and then finally for your class_changer:

def class_changer(mod_clsname_string, attrname, value):
    modname, clsname = '.'.split(mod_clsname_string)
    create_if_needed_and_add_to_class(globals()[modname], clsname, attrname, value)

Edit: fixed class_changer to use locals() for module name lookup, since it is a string and not a module.

Edit: oops, it should be globals().

Robie Basak
  • 6,492
  • 2
  • 30
  • 34