views:

85

answers:

2

Hi,

I am about to start a large Django project at work. And the key features are form handling. There is going to be a lot of forms that the users of the app is going to use. And one requirement is that it should be possible to edit the forms in the admin interface of the application.

That is, it is not a requirement to add/delete form fields. But it should be possible to edit form field attributes. E.g change which fields wich is required and such.

Anybody have any good solutions on how to do this. Im thinking that I might have to subclass the fields I want to be dynamic and do something there. I guess that all the models fields have to have "blank=True/null=True" and some how add some meta information in a separate model(?) about the model wich declares wich fields are required. And then use that information when forms is displayed and validated.

Before i start off doing this I would really like some input in how to design such a solution, anybody got ideas on how it should be done?


After more research, I've learned that you could do a'lot with factory functions that would return the form with the given attributes set. So it looks like the problem is not that hard do solve.

I would make a function that returns a form with the right attributes/fields set. But the part that I haven't found a good solution for is how to manage this in the admin interface. I am thinking that I would make a db.Model that stores information about another models fields. Where I can set wich is required and such.

And then in the function that returns the form, go trough that model and return a form with the right attributes. But how to make that model (wich should mirror another models fields) in a good way?

+3  A: 

You should use certain models for this. For every form that might be customised this way, a new database entry must be created. I guess, it should look like this:

class FormSettings(Model):
  form = CharField(..)

class FormAttrib(Model):
  form_settings = ForeignKey(FormSettings)
  field = CharField(..)
  attrib_name=CharField(..)
  attrib_value=CharField(..)

In FormSettings.form you should store some address of form, like say . and when form is built (in init), it should look for an entry in db and use attribs, that were described for it.

You can make it create db entries easily, if you use own metaclass for your forms and make it register in the database (create proper FormSettings entry), when a class is created. It will be done once when the process is started, which shouldn't be that bad.

I hope it helps a little. If you have further questions, I'll be happy to help :-) I like those non standard appliances of django :-) This is where all the fun begins :-)

EDIT:

OK, let's say, that you want to you have app food and form FavouriteFoodForm with food_name field. You store in the database in formsettings table as food.FavouriteFoodForm and add one attribute setting: field='food_name', attrib_name='required', attribute_value='True' (it's not perfect, but still it's not that bad').

No in FavouriteFoddForm you look for the settings in database, so you do:

settings = FormSettings.objects.get(form=app_name + self.__class__.name)

and you iterate over settings

for setting in settings.formattrib_set():

and here you simple call exec and set proper attribute:

exec("getattr(self, settings.field)[attrib_name] = %s" % attrib_value)

This should set attributes to required=True.

gruszczy
Thank's for the response, but I had some problems understanding what you meen. Could you possible write an even more detailed description of your solution?Like I mention in the last part of the question I found lots of usefull information in this article at b-list.orghttp://www.b-list.org/weblog/2008/nov/09/dynamic-forms/Seems like that is going to help alot. But still have to tackle how to store the form settings in db.
Espen Christensen
Ok, that gives a lot more sense to me. Seems like a solution that would work. But could be done more elegantly? Is it possible to do model introspection or something make populate the FormAttrib and FormSettings?
Espen Christensen
You mean Form introspection, right? Yes, it can be done, but I believe, that this is a little too much to describe here ;)Try reading about metaclasses - I believe this is the point, where you need them, although some python guru says, that in 99% of cases you don't need them. It's best to read code of ModelForm, whihch analyzes models to build proper fields. You will need something similar, but for Form. Basically, you will do it other way round and have FormModel ;-)
gruszczy
+2  A: 

This isn't an exact answer to your problem, but I did something similar which I thought might be of use to you. I created an entirely customizable form, so that the form can be customized by end users.

I ended up with 2 models, a BuiltForm and a BuiltFormField:

class BuiltForm(models.Model): 
    name = models.CharField(max_length=32) 
    def form(self, data=None, initial=None):
        form = BuiltFormGenericForm(data, initial=initial)
        form.addBuiltFormFields(BuiltFormField.objects.filter(builtform=self, disabled=0))
        return form            

class BuiltFormField(models.Model):
    builtform = models.ForeignKey(BuiltForm)
    type = models.CharField(max_length=32, choices=ALL_FIELD_TYPES)
    label = models.CharField(max_length=32)
    fieldname = models.CharField(max_length=32)
    helptext = models.CharField(max_length=256, blank=True)
    required = models.BooleanField(default=False)
    sort_order = models.IntegerField(default=0)
    disabled = models.BooleanField(default=False)
    options = models.TextField(blank=True)
    def field(self):
        field = ALL_FIELD_MAPS.get(self.type)
        ## Build out the field, supplying choices, `required`, etc.

There are a couple of things that are abnormal. ALL_FIELD_TYPES is a map of the types of fields allowed in the form. This is used in conjuction with a dict() to discern what class (CharField, EmailField, ChoiceField, etc) should be used for this field. options is also a pickled list of options for later use in a ChoiceField. This allowed me to create an arbitrary list of options without a separate call to the database.

The other major piece to this is a custom Form class which allows itself to be populated with the fields from a BuiltForm. Mine looks like this:

class BuiltFormGenericForm(forms.Form):
    built_form_fields = {}
    builtform = None
    def addBuiltFormFields(self, fields):
        for field in fields:
            self.fields[field.label] = field.field()
            self.built_form_fields[field.pk] = field
    def is_valid(self):
        # Do validation here.  My code for this is pretty big because of custom fields
        # and calculations that I have to squeeze in.

The BuiltFormField objects aren't designed to be created through the admin interface, but rather through a custom one with a ton of JavaScript to make it user-friendly, but you can certainly expose parts of the BuiltFormField model in the admin interface for them to be updated.

Hope this helps you work out a model for your forms.

Jack M.
Genius, a lot of great code and consepts here. Nice to see things like this is possible. Kind a simular to @gruszczy proposed solution.
Espen Christensen