开发者

Making a Django ModelForm model-generic?

I am facing an interesting situation with Django, and I was hoping someone would see a solution to thi开发者_运维知识库s, or at least could give me a hint.

I am trying to make a ModelForm model-generic. I do not know if this is something that should be done, but here it goes.

This works fine :

A tuple referencing models

# settings.py
SPECIES = (
    ('TIG', 'Tiger'),
    ('SHR', 'Shark'),
)

A URL to create an Animal object

# urls.py
from django.conf.urls.defaults import patterns, include, url
urlpatterns = patterns('species.views',
    url(r'^add/$', 'add_animal', name='add_animal'),
)

An Animal model and two children of it

# models.py
from django.db import models
from django.conf import settings

class Animal(models.Model):
    name = models.CharField(max_length=100)
    nickname = models.CharField(max_length=100)
    species = models.CharField(max_length=3, choices=settings.SPECIES)

class Tiger(Animal):
    fangs_size = models.IntegerField()

class Shark(Animal):
    color = models.CharField(max_length=20)

The view that displays the form

The right model is chosen through a GET parameter.

# views.py
def add_animal(request):
    if request.method == "GET":
        if request.GET['model_name']:
            model_name = request.GET['model_name']
    else:
        model_name = 'Animal'

    print "Model name is: %s" % model_name

    model = get_model("species", model_name)
    form = AnimalForm(model=model)

    return create_object(
        request,
        model=model,
        post_save_redirect=reverse('index'),
        template_name='species/my_form.html',
    )

A template

# my_form.html
<!doctype html>
<html>
    <head>
        <title>Adding an animal</title>
    </head>
    <body>
        <h1>Add an animal to the farm</h1>
        <form>
            {% csrf_token %}
            {{ form.as_p }}
            <input type="submit" value="Submit" />
        </form>
    </body>
</html>

When I visit /add?model_name=tiger, I get the right form displayed.

Now, let's say I want to hide the nickname field. I would then need to use a custom ModelForm. How to instanciate it with the right model? That's my problem.

Here is the form?

# forms.py
from species.models import Animal
from django import forms

class AnimalForm(forms.ModelForm):

    class Meta:
        model = Animal

    def __init__(self, *args, **kwargs):
        model = kwargs.pop('model')
        super(AnimalForm, self).__init__(*args, **kwargs)
        self.Meta.model = model

The views becomes:

# views.py
...
model = get_model("species", model_name)
form = AnimalForm(model=model)

return create_object(
    request,
    # model=model,            # Need for customization
    # form_class=AnimalForm,  # With the class name, how to pass the argument?
    form_class=form,          # Not sure this can be done, I get this error: 'AnimalForm' object is not callable
    post_save_redirect=reverse('index'),
    template_name='species/my_form.html',
)
...

My goal is to be able to create new models inheriting from Animal later on, add them to the SPECIES tuple and be done. Can this be accomplished?

Thanks for your help!


You probably want to use django.forms.models.modelform_factory - it takes the model class and an exclude tuple among its parameters. You can use it in the view to create your form class dynamically.

form_class = modelform_factory(model, exclude=('nickname',))
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜