Динамическое создание форм на основе данных из базы в Django

    Началось всё с того что надо было часто создавать однотипные формы и сохранять введённые данные в базу. По суте форма всегда одна и та же — «Заявка на регистрацию» — но в зависимости от мероприятия поля в ней разные.

    Обязанности подготовки формы надо было переложить на администратора сайта, поэтому было принято решение создать механизм управления формами через админский интерфейс Django. Так появилось приложение CDBForms.

    Идея очень простая — создать модели для описания форм и хранения введённых данных. Соответственно появились модели Template (шалон формы), TemplateField (поле в шаблоне формы) и FieldParameter (параметры поля) для хранения информации о формах (шаблоны форм) и модели Record и RecordData для хранения данных, введённых через формы.

    Собственно:
    class Template(models.Model):
        tag = models.SlugField(max_length=16, unique=True)
        title = models.CharField(max_length=50)
        
        def fields(self):
            return TemplateField.objects.filter(template=self).order_by('tab')
    
        def __unicode__(self):
            return self.title
    
    class TemplateField(models.Model):
        FieldTypes = (('T', 'Text'), ('B', 'Bool'), ('E', 'E-mail'), ('U', 'URL'), ('C', 'Choices'),)
        template = models.ForeignKey(Template)
        tag = models.SlugField(max_length=32)
        title = models.CharField(max_length=50)
        type = models.CharField(max_length=1, choices=FieldTypes)
        tab = models.IntegerField(default=0)
        required = models.BooleanField(default=True)
    
        def __unicode__(self):
            return u'%s: %s' % (self.template, self.tag)
    
        def parameters(self):
            return FieldParameter.objects.filter(field=self).order_by('tab')
            
        class Meta:
            unique_together = ("template", "tag")
            
    class FieldParameter(models.Model):
        field = models.ForeignKey(TemplateField)
        tag = models.SlugField(max_length=32)
        value = models.CharField(max_length=255)
        tab = models.IntegerField(default=0)
    
        def __unicode__(self):
            return u'%s: %s = %s' % (self.field, self.tag, self.value)
            
        class Meta:
            unique_together = ("field", "tag", "value")
    
    class Record(models.Model):
        template = models.ForeignKey(Template)
        dt = models.DateTimeField()
        
        def __unicode__(self):
            return u'%s @ %s' % (self.template, self.dt)
    
    class RecordData(models.Model):
        record = models.ForeignKey(Record)
        field = models.ForeignKey(TemplateField)
        value = models.CharField(max_length=255)
        
        def __unicode__(self):
            return u'%s %s' % (self.record, self.field)
            
        class Meta:
            unique_together = ("record", "field")
    


    Дальше осталось только реализовать класс нашей формы:
    class CDBForm(forms.Form):
        def __init__(self, template=None, tag=None, *args, **kwargs):
            if template:
                self.template = template
            elif tag:
                self.template = Template.objects.get(tag=tag)
            fields = self.template.fields()
            for field in fields:
                if field.type == "B":
                    self.base_fields[field.tag] = forms.BooleanField(label=field.title, required=field.required)
                elif field.type == "T":
                    self.base_fields[field.tag] = forms.CharField(label=field.title, required=field.required)
                elif field.type == "C":
                    choices = []
                    for parameter in field.parameters():
                        if parameter.tag == "choice":
                            choices.append((parameter.value, parameter.value))
                    self.base_fields[field.tag] = forms.ChoiceField(choices=choices, label=field.title, required=field.required)
                elif field.type == "E":
                    self.base_fields[field.tag] = forms.EmailField(label=field.title, required=field.required)
                elif field.type == "U":
                    self.base_fields[field.tag] = forms.URLField(label=field.title, required=field.required)
            forms.Form.__init__(self, *args, **kwargs)
    
        def save(self, commit=True):
            data = self.cleaned_data
            if commit:
                rec = Record(template=self.template, dt=datetime.now())
                rec.save()
                for k,v in data.iteritems():
                    f = TemplateField.objects.get(template=self.template, tag=k)
                    d = RecordData(record=rec, field=f, value=v)
                    d.save()
            return data
    


    Форма готова к использованию, осталось только реализовать удобный интерфейс администратора. Пример как это работает можно посмотреть тут и тут.

    Скачать код можно с code.google.com/p/django-cdbforms
    Кстати, если кому-то интересно, то буду рад сотрудничеству! Спасибо!

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 9

      +1
      В профильный блог "Джанго" бы перенести неплохо было. А так — спасибо Вам — полезно бывает, иметь в арсенале гибкую форму для заявок. Только помимо текста, мейла и пр. не хватает загрузки файла. Если б его со стандартным стораджем подружить — было бы совсем хорошо.
        0
        в джанго-блог не могу писать т.к. кармы мало :)
        вообще можно делать не только заявки, а всякие разные формы для которых не нужен хитрый хэндлер или как минимум хэндлер может быть написан один для всей группы форм
        спасибо за отзыв!
        0
        интересно, поковыряю дома, спасибо
          0
          welcome!
          0
          ура, дождался (: а то надо было что-то подобное, но как-то лень (8
            0
            спасибо, в ближайшее время планируются обновления и усовершенствования
            следите
            если есть пожелания — говорите
            0
            Пример как это работает можно посмотреть тут и тут.

            404 и там и там
              0
              А что вы хотели, вы через 6 лет пришли с даты публикации, а я вообще через 8 ))
                0
                Да уж. А раньше вот на века строили :)

            Only users with full accounts can post comments. Log in, please.