views:

79

answers:

2

I'm using Django and want to be able to store classes in a database for things like forms and models so that I can easily make them creatable through a user interface since they are just stored in the database as opposed to a regular file. I don't really know a whole lot about this and am not sure if this is a situation where I need to use exec in python or if there is some other way. My searches on this aren't turning up much of anything.

Basically, it would just be where I do a database call and get the contents of a class, then I want to instantiate it. Any advice is appreciated on how to best do this sort of thing.

EDIT: In response to the idea of a malicious __init__ in the class, these are only for things like forms or models where it is tightly controlled through validation what goes in the class, there would never be an __init__ in the class and it would be basically impossible, since I would validate everything server side, to put anything malicious in the class.

+5  A: 

Do not store code in the database!!!

Imagine a class with a malicious __init__ method finding it's way in your "class repository" in the database. This means whoever has write access to those database tables has the ability to read any file from your web server and even nuke it's file system, since they have the ability to execute any python code on it.

Vasil
Ok I get what you mean.. what is the recommended approach for this then? I want to create cms like features where the user can create forms (which are classes in django) and things like this.. I supposed I should just have it write the files directly and then track them in the database?
Rick
oh and also these don't have __init__ since they are just form classes and it would be already controlled as to what can go in them, through heavy validation, its not like they could create a custom __init__
Rick
@rick allowing arbitrary python code found in your database to be executed on demand is insecure, whatever validation you have there you'll miss something. You can create something like a form model which will have a field model holing a foreign key to it, then the field model would have a name/label/type etc, from these models you would create form classes on the fly, which requires some knowledge of how forms work internally but is doable. I suggest you open another question for this.
Vasil
ok yeah, I am kind of just trying to get the right train of thought for the approach to this, what you said about creating it from the foreign key, etc sounds like a good approach, I will look into it more and create a new question around this, if I can't find enough info, thanks for the help
Rick
@Rick I think if you phrased the question differently you might get more useful answers. Something like "How can I let users create forms using Django" or something like that, as that seems to be what you want to do.
extraneon
thanks, I will gather my thoughts to make a more focused question around that topic, I agree that storing code in the DB doesn't sound like a good idea other than maybe on just a development server but then its probably not worth the trouble
Rick
+1  A: 

Don't store the class itself, store the import path as a string in the database (e.g. 'django.forms.CharField')

I started doing this same thing for another project, and saved off the code in my local repository. To address the security concerns I was going to add an argument to the field constructor of allowed base classes. If you do implement this, let me know, I'd love to have it.

helpers.py

def get_class_from_concrete_classpath(class_path):
    # Unicode will throw errors in the __import__ (at least in 2.6)
    class_path = str(class_path)

    mod_list = class_path.split('.')
    module_path = '.'.join(mod_list[:-1])
    class_name = mod_list[-1]

    base_mod = __import__(module_path, fromlist=[class_name,])
    return getattr(base_mod, class_name)


def get_concrete_name_of_class(klass):
    """Given a class return the concrete name of the class.

    klass - The reference to the class we're interested in.

    Raises a `TypeError` if klass is not a class.
    """

    if not isinstance(klass, (type, ClassType)):
        raise TypeError('The klass argument must be a class. Got type %s; %s' %
                        (type(klass), klass))

    return '%s.%s' % (klass.__module__, klass.__name__)

fields.py

class ClassFormField(forms.Field):
    def to_python(self, value):
        return get_concrete_name_of_class(value)

class ClassField(models.CharField):
    __metaclass__ = models.SubfieldBase

    """Field used for storing a class as a string for later retrieval"""

    MAX_LENGTH = 255
    default_error_messages = {
        'invalid': _(u'Enter a valid class variable.'),
    }

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = kwargs.get('max_length', ClassField.MAX_LENGTH)
        super(ClassField, self).__init__(*args, **kwargs)

    def get_prep_value(self, value):
        if isinstance(value, (basestring, NoneType)):
            return value

        return get_concrete_name_of_class(value)


    def to_python(self, value):
        if isinstance(value, basestring):
            return get_class_from_concrete_classpath(value)

        return value

    def formfield(self, **kwargs):    
        defaults = {'form_class' : ClassFormField}
        defaults.update(kwargs)
    return super(ClassField, self).formfield(**defaults)
sdolan
Thanks for posting this, I will definitely take a look.. I do think that Vasil made a good point though so I don't know if I would actually use this approach on a production website now that I am considering the alternatives
Rick