Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top