views:

1115

answers:

2

In Django, given excerpts from an application animals likeso:

A animals/models.py with:

from django.db import models
from django.contrib.contenttypes.models import ContentType

class Animal(models.Model):
  content_type = models.ForeignKey(ContentType,editable=False,null=True)
  name = models.CharField()

class Dog(Animal):
  is_lucky = models.BooleanField()

class Cat(Animal):
  lives_left = models.IntegerField()

And an animals/urls.py:

from django.conf.urls.default import *

from animals.models import Animal, Dog, Cat

dict = { 'model' : Animal }

urlpatterns = (
  url(r'^edit/(?P<object_id>\d+)$', 'create_update.update_object', dict),
)

How can one use generic views to edit Dog and/or Cat using the same form?

I.e. The form object that is passed to *animals/animal_form.html* will be Animal, and thus won't contain any of the specifics for the derived classes Dog and Cat. How could I have Django automatically pass a form for the child class to *animal/animals_form.html*?

Incidentally, I'm using Djangosnippets #1031 for ContentType management, so Animal would have a method named *as_leaf_class* that returns the derived class.

Clearly, one could create forms for each derived class, but that's quite a lot of unnecessary duplication (as the templates will all be generic -- essentially {{ form.as_p }}).

Incidentally, it's best to assume that Animal will probably be one of several unrelated base classes with the same problem, so an ideal solution would be generic.

Thank you in advance for the help.

A: 

AFAICT, cats and dogs are on different DB tables, and maybe there's no Animal table. but you're using one URL pattern for all. somewhere you need to choose between each.

I'd use a different URL patter for cats and dogs, both would call 'create_update.update_object'; but using a different dict for each. one with 'model':Dog and the other with 'model':Cat

or maybe you want a single table where each record can be a cat or a dog? i don't think you can use inherited models for that.

Javier
Hi Javier, thanks for replying. Django creates a one-to-one mapping between parent and inherited ORM tables. The design I'm working with has thousands of derived classes, so a static url mapping is impractical. The template is always the same, so an elegant solution would be great! :) Cheers
Brian M. Hunt
so, in your template you follow that 1:1 relation to the inherited? or the other way around?
Javier
Javier: Have a look at model inheritance, here: http://docs.djangoproject.com/en/dev/topics/db/models/#id6 - it's a good description of the ORM inheritance that the example is using. Cheers
Brian M. Hunt
ok, i see it; but my original point stands: how would a single template managed by a single view on a single URL can discern if you're want a dog or a cat? somewhere on the chain you have to have a discriminator
Javier
Ah-ha - great point. I follow something like this brilliant snippet: http://www.djangosnippets.org/snippets/1031/ :)
Brian M. Hunt
+1  A: 

Alright, here's what I've done, and it seems to work and be a sensible design (though I stand to be corrected!).

In a core library (e.g. mysite.core.views.create_update), I've written a decorator:

from django.contrib.contenttypes.models import ContentType
from django.views.generic import create_update

def update_object_as_child(parent_model_class):
   """
   Given a base models.Model class, decorate a function to return  
   create_update.update_object, on the child class.

   e.g.
   @update_object(Animal)
   def update_object(request, object_id):
      pass

  kwargs should have an object_id defined.
  """

  def decorator(function):
      def wrapper(request, **kwargs):
          # may raise KeyError
          id = kwargs['object_id']

          parent_obj = parent_model_class.objects.get( pk=id )

          # following http://www.djangosnippets.org/snippets/1031/
          child_class = parent_obj.content_type.model_class()

          kwargs['model'] = child_class

          # rely on the generic code for testing/validation/404
          return create_update.update_object(request, **kwargs)
      return wrapper

  return decorator

And in animals/views.py, I have:

from mysite.core.views.create_update import update_object_as_child

@update_object_as_child(Animal)
def edit_animal(request, object_id):
  pass

And in animals/urls.py, I have:

urlpatterns += patterns('animals.views',
  url(r'^edit/(?P<object_id>\d+)$', 'edit_animal', name="edit_animal"),
)

Now I only need a unique edit function for each base class, which is trivial to create with a decorator.

Hope someone finds that helpful, and I'd be delighted to have feedback.

Brian M. Hunt