views:

332

answers:

2

I'd like to create a form that includes fields from two separate models, along with some other regular (non-model) fields. The form will create an instance of each model. I don't think I can use inline formsets for this, since I don't want to include all the fields from both models.

I'd like to create the form field without hard-coding the type of the model fields.

I know I can get a form field from a model field using model_field.formfield(). But how can I get the specific model field?

My first solution:

def get_fields(model_class):
    fields = {}
    for f in model_class._meta.fields:
        fields[f.name] = f

class MyForm(forms.Form):
    foo_name = get_fields(Foo)['name'].formfield()
    bar_name = get_fields(Bar)['name'].formfield()
    other_field = ...

Is there an equivalent of get_fields already? Is this a bad idea? I'm uncomfortable relying on the model _meta attribute. Or am I going about this the completely wrong way?

+3  A: 

You also can take a look to django.forms.models.fields_for_model That should give you a dictionary of fields, and then you can add the fields of the form

naw
Hadn't noticed that method, thanks. That's the answer I was looking for, but perhaps Casey Stark's suggestion is more appropriate.
harto
+2  A: 

You should never have to build the fields yourself unless you want some special behavior.

This should be as simple as using two ModelForms and an extra Form inside one <form> tag in your template with one submit button.

in forms.py:

class Model1Form(forms.ModelForm):
    class Meta:
        model = Model1
        fields = ('fields', 'you', 'want')

class Model2Form(forms.ModelForm):
    class Meta:
        model = Model2
        fields = ('fields', 'you', 'want')

class ExtraFieldsForm(forms.Form):
    extra_field = form.TextField() # or whatever field you're looking for

in views.py:

form1 = Model1Form(request.POST or None)
form2 = Model2Form(request.POST or None)
form3 = ExtraFieldsForm(request.POST or None)

if form1.is_valid() and form2.is_valid() and form3.is_valid():
    form1.save()
    form2.save()
    form3.save()

    ...do other stuff like a redirect...

and in the template:

<form method="POST" action="">{% csrf_token %}
    <fieldset>
        {{ form1|as_uni_form }}
        {{ form2|as_uni_form }}
        {{ form3|as_uni_form }}
        <div class="form_block">
            <input type="submit" value="Save both models"/>
        </div>
    </fieldset>
</form>

I'm used to using django-uni-form, but you can render the form fields however you like. Good luck with your site.

Casey Stark
It hadn't occurred to me do use multiple `Form` objects within a single `<form>` element. Thanks for the suggestion - I'll try that out tonight.
harto
Cool. I hope it works for you. I've seen it done before but this isn't tested. You might need to play with the view logic to get what you want.
Casey Stark