2009-06-03 8 views
5

Voy a tratar de simplificar esto tanto como sea posible. Digamos que tengo el siguiente:Herencia de modelo de Django con inclusión de etiquetas personalizadas

models.py

class Person(models.Model): 
    name = models.CharField(max_length=255) 

    def getRealPerson(self): 
     # is there a better way to do this? 
     ret = None 
     try: 
      ret = self.worker 
     except: 
      try: 
       ret = self.retired 
      except: 
       ret = self 
     return ret 
class Worker(Person): 
    salary = models.IntegerField(default=0) 

class Retired(Person): 
    age = models.IntegerField() 

El ejemplo no importa realmente para lo que quiero, sólo tiene que ir conmigo aquí. El propósito de esto es que pueda tener una tabla maestra de Persona para hacer referencia a todas las personas.

Lo ideal sería poder llamar a una vista de Persona y tener cada uno de los detalles específicos enumerados de forma personalizada para cada tipo de clase. Me gustaría utilizar una etiqueta de inclusión personalizada para hacer esto.

people.html

{% load people_extras %} 
{% for person in people %} 
    {% show_person person %} 
{% endfor %} 

people_extras.py - templatetags

from django import template 

@register.inclusion_tag('worker.html') 
def show_worker(person): 
    return {'person':person} 

@register.inclusion_tag('worker.html') 
def show_retired(person): 
    return {'person':person} 

#How do I write this function and use it as the show_person person tag? 
from project.app.models import Worker, Retired 
def show_person(person): 
    person = person.getRealPerson(): 
    if isinstance(person, Worker): 
     return show_worker # yes, this doesn't work. 

no tengo ni idea de cómo llegar a llamar la plantilla correcta en función del tipo de persona.

no podía encontrar la manera de lograr esto con el uso de la plantilla {%}% ifequal así:

{% ifequal person.getRealPerson.__class__.__name__ "Worker" %} 
    {% show_worker %} 
... 

Fui la ruta que escribí más arriba con las templatetags. Sin embargo, ¡no sé dónde poner la lógica para determinar el tipo de persona!

Creo que eventualmente me gustaría poder utilizar una vista genérica para esto también en el objeto Person.

Si hay una forma mucho mejor de hacerlo, estoy abierto a sugerencias, solo quiero que funcione.

He estado un poco atrapado aquí por más de un día ... realmente podría usar un empujón.

Respuesta

5

Consulte this answer para obtener una forma eficiente de obtener el subobjeto del tipo de persona correcto después de realizar una consulta en la tabla Person.

Una vez que tenga ese funcionamiento, debería poder eliminar la mayor parte de la complejidad en sus etiquetas de plantilla mediante el uso de polimorfismo. Si desea mostrar cada tipo de persona con una plantilla diferente, cree un nombre de clase en el modelo o simplemente haga el nombre de la plantilla en función del nombre del modelo (utilizando person._meta.module_name). Una etiqueta de plantilla simple debería poder cubrir todos los casos sin siquiera saber los detalles de qué subclases existen. EDITAR Esta única etiqueta no se puede registrar utilizando el decorador inclusion_tag, porque deberá determinar dinámicamente el nombre de la plantilla. Pero es fácil de escribir usando el decorador simple_tag:

@register.simple_tag 
def show_person(person): 
    t = template.loader.select_template(["%s.html" % person._meta.module_name, 
             "person.html") 
    return t.render({'person': person}) 

Esto hará que un trabajador mediante worker.html, un retirado usando retired.html, etc. Si no se encuentra el modelo específico para el subtipo, se cae de nuevo a la persona predeterminada.html.

+0

I Estoy un poco confundido con la implementación de la etiqueta de plantilla personalizada de esto. testperson.cast() devuelve el subobjeto correcto, pero ¿cómo puedo usarlo para llamar al include_tag correcto? La forma en que lo configuro arriba, show_person no tiene forma de ser llamado (no está registrado). – lostincode

+0

Eso es lo que mencioné en el segundo párrafo. Editado para que sea más claro. –

2

Aproveche el contenttypes framework de Django para identificar sus modelos.

ver este fragmento: Child aware model inheritance y utilizar Carl Meyer's sugerencia en los comentarios (envuelva la asignación en "if not self.id:")

+0

O vea esta respuesta: http://stackoverflow.com/questions/929029/how-do-i-access-the-child-classes-of-an-object-in-django-without-knowing-the- name/929982 # 929982 –

+1

También tenga en cuenta que rix arregló su fragmento en función de mi sugerencia, por lo que ya no es necesario señalar el comentario. –

+0

Oh duh, estaba en la cola de una noche cerrada y por alguna razón mi cerebro no se registraba self.pk es self.id :) –

0

utilizo un método que me han pirateado forma rápida en el momento de hacer un trabajo muy similar. Tengo muchos modelos que heredan de un modelo principal "base": puedo acceder a todo en mi base de datos mediante un código único (lo llamamos "código de acceso"). Para averiguar el hijo del objeto padre, hago algo como esto:

def get_child_types(self): 
    return [ c.__name__.lower() for c in self.__subclasses__() ] 

def get_child(self): 
    for type in self.get_child_types(): 
     try: 
      o = self.__getattribute__(type) 
     except: 
      pass 
     else: 
      return o 

Sé que es complicado, pero funciona bien. Guardo el "tipo" del objeto en un campo desnormalizado en el modelo de objetos padre, por lo que solo tengo que "encontrarlo" una vez, lo que reduce los aciertos en la base de datos y el trabajo requerido de la CPU. Luego, simplemente reemplazo save() para encontrar el tipo de objeto en la creación, y lo almaceno en ese campo.

Espero que ayude!

+0

Esto tiene la idea general correcta, pero es más complicado (y más lento) de lo que necesita ser En el momento de la creación, ya conoce el tipo correcto, por lo que no necesita encontrarlo al recorrer una lista de tipos y verificar cada uno. Ver http://stackoverflow.com/questions/929029/how-do-i-access-the-child-classes-of-an-object-in-django-without-knowing-the-name/929982#929982 –

0

Usar este fragmento para el método de modelo de persona getRealPerson():

def getRealPerson(self): 
    from django.db.models.fields.related import OneToOneRel 
    from django.core.exceptions import ObjectDoesNotExist 
    for related in self._meta.get_all_related_objects(): 
     rel_opts_name = related.get_accessor_name() 
     if isinstance(related.field.rel, OneToOneRel): 
      try: 
       sub_obj = getattr(self, rel_opts_name) 
      except ObjectDoesNotExist: 
       pass 
      else: 
       return sub_obj 

Se le devolverá el primer objeto relacionado (ya sea un trabajador o una Retirado).

Cuestiones relacionadas