Dynamic django forms - Variable fields

2019-04-15 02:11发布

问题:

I have some models; Vocabularies (tags lists), Labels (tags) and different articles types.

These article types have some vocabs enabled, and some not, for instance: Image can tagged with terms from A, B and a news article with terms from A or C.

This however only exists at the database level.

What i'm trying to do is to output fields depending on if a vocab is enabled for a content type or not, like this:

  • Determine if this content type is connected to a vocabulary.

  • Load the vocabularies

  • output a CharField or Select for each vocabulary.

All my efforts for the last step have failed however, so I'm hoping someone can point me in the right direction.


My apologies for not being very clear about the code I am using, so here it is:

Vocabulary - Tags

NewsArticle can use tags from vocab 1, 3 Blog can use tags from vocab 1,2

This code get's the vocabularies which are enabled for the content type:

    def get_vocabularies(self, type = None):
    vocabularies = {}
    object = namedtuple("Vocabulary", "vid name desc help relations hierarchy multiple req tags weight")
    if (type != None):
        vocabs = VocabularyTypes.objects.filter(type=type)
        for vocab in vocabs:
            v = Vocabulary.objects.get(pk=vocab.vid)
            vocabularies[v.vid] = object(vid = v.vid, 
                                         name = v.name, 
                                         desc = v.desc,
                                         help = v.help,
                                         relations = v.relations,
                                         hierarchy = v.hierarchy,
                                         multiple = v.multiple,
                                         req = v.req,
                                         tags = v.tags,
                                         weight = v.weight
                                       )
    else:
        vocabs = Vocabulary.objects.all()
        for v in vocabs:
            vocab = Vocabulary.objects.get(pk=v.vid)
            vocabularies[v.vid] = object(vid = vocab.vid, 
                                         name = vocab.name, 
                                         desc = vocab.desc,
                                         help = vocab.help,
                                         relations = vocab.relations,
                                         hierarchy = vocab.hierarchy,
                                         multiple = vocab.multiple,
                                         req = vocab.req,
                                         tags = vocab.tags,
                                         weight = vocab.weight
                                       )

    return vocabularies

So code I have tried so far is this:

    def free_tags(self, type):
    vocabs = Label.objects.get_vocabularies(type)

    for vid in vocabs.keys():
        output = forms.CharField(label=vocabs[vid].name,
                                 required = vocabs[vid].req,
                                 widget = forms.TextInput(attrs={'size' : 64})
                                 )
        return output

However, when using it in a view it prints out the internal python identifier (object intance at ... etc).

Another problem however, it that these field have no name i.e. they are all called 'output'.

So what I need to find out is how output a number of fields, when the number of fields is unknown.

回答1:

This article helped me a lot to get started with dynamic forms.

The best way (I think) is this: You have function that creates your form class, e.g.

def make_form(instance):
    fields = { 'a': forms.CharField(max_length=50),
               'b': forms.CharField(widget=forms.Textarea) }
    if some_condition:
        fields['additional'] = forms.CharField()
    return type('SomeForm', (forms.BaseForm,), { 'base_fields': fields })

You just define some fields in a dictionary, set certain fields only if conditions are fulfilled or not (instance is some model instance that you want to test for conditions) and create a new class with Python's type() function.