2009-06-03 8 views
5

Cercherò di semplificarlo il più possibile. Diciamo che ho il seguente:Ereditarietà modello Django con inclusione_tags personalizzati

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() 

L'esempio non ha molta importanza per quello che voglio, basta andare con me qui. Lo scopo di questo è che posso avere una tabella principale di riferimento per tutte le persone.

Idealmente, desidero essere in grado di richiamare una vista di Person e di elencare i dettagli specifici in modo personalizzato per ciascun tipo di classe. Mi piacerebbe utilizzare un tag di inclusione personalizzato per farlo.

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. 

non ho idea di come farlo chiamare il modello corretto in base al tipo persona.

non riuscivo a capire come ottenere questo risultato con il modello utilizzando {% IfEqual%} come questo:

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

sono andato il percorso che ho scritto sopra con i templatetags. Tuttavia, non so dove mettere la logica per determinare il tipo di persona!

Penso che alla fine mi piacerebbe essere in grado di utilizzare una vista generica anche per questo sull'oggetto Person.

Se c'è un modo migliore per farlo, sono aperto ai suggerimenti, voglio solo farlo funzionare.

Sono stato bloccato qui per più di un giorno ... potrebbe davvero usare una spinta.

risposta

5

Vedere this answer per un modo efficace per ottenere il tipo di oggetto utente di tipo corretto dopo l'esecuzione di query sulla tabella Persona.

Una volta che funziona, dovresti essere in grado di eliminare la maggior parte della complessità nei tag del modello usando il polimorfismo. Se si desidera visualizzare ciascun tipo di persona utilizzando un modello diverso, rendere il nome del modello un attributo di classe del modello o anche solo creare il nome del modello in base al nome del modello (utilizzando person._meta.module_name). Un semplice tag modello dovrebbe quindi essere in grado di coprire tutti i casi senza nemmeno conoscere i dettagli di quali sottoclassi esistono. EDIT Questo singolo tag non può essere registrato utilizzando il decoratore inclusion_tag, poiché è necessario determinare il nome del modello in modo dinamico. Ma è facile scrivere utilizzando il decoratore 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}) 

Questo renderà un operaio utilizzando worker.html, un pensionato utilizzando retired.html, ecc Se il modello specifico per il sottotipo non viene trovato, si ricade alla persona predefinita.html.

+0

I Sono un po 'confuso sull'implementazione del tag template personalizzato di questo. testperson.cast() restituisce l'oggetto secondario corretto, ma come posso utilizzarlo chiamando il parametro inclusion_tag corretto. Il modo in cui lo ho impostato sopra, show_person non ha modo di essere chiamato (non è registrato). – lostincode

+0

Ecco cosa ho affrontato nel secondo paragrafo. Modificato per, si spera, renderlo più chiaro. –

2

Approfitta di contenttypes framework di Django per identificare i tipi di modello.

Vedi questo frammento: Child aware model inheritance e utilizzare Carl Meyer's suggerimento nei commenti (avvolgere l'assegnazione in "if not self.id:")

+0

Oppure vedere questa risposta: http://stackoverflow.com/questions/929029/how-do-i-access-the-child-classes-of-an-object-in-django-without-knowing-the- nome/929982 # 929982 –

+1

Si noti inoltre che rix ha risolto il suo snippet in base al mio suggerimento, quindi non è più necessario indicare il commento. –

+0

Oh duh, ero alla fine di una notte e per qualche ragione il mio cervello non si stava registrando da solo .pk è self.id :) –

0

io uso un metodo che ho messo insieme in fretta, al momento di fare un lavoro molto simile. Ho molti modelli che ereditano da un modello "base" principale: posso accedere a tutto nel mio database con un codice univoco (lo chiamiamo "codice go"). Per scoprire il figlio dell'oggetto genitore, faccio qualcosa di simile:

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 

Lo so che è disordinato, ma funziona bene. Conservo il "tipo" dell'oggetto in un campo denormalizzato nel modello oggetto genitore, quindi devo solo "trovarlo" una sola volta - il che riduce i colpi sul database e il lavoro della CPU richiesto. Quindi ho appena scavalcato save() per trovare il tipo di oggetto sulla creazione e memorizzarlo in quel campo.

Spero che aiuti!

+0

Questo ha l'idea giusta, ma è più complicato (e più lento) di quanto deve essere. Al momento della creazione si conosce già il tipo corretto, quindi non è mai necessario trovarlo scorrendo un elenco di tipi e controllandoli ciascuno. Vedi http://stackoverflow.com/questions/929029/how-do-i-access-the-child-classes-of-an-object-in-django-without-knowing-the-name/929982#929982 –

0

utilizzare questo frammento per il metodo modello di 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 

lo restituirà il primo oggetto (o un lavoratore o di un pensionato) correlata.

Problemi correlati