2013-08-27 19 views
5

ho una serie di modelli che assomigliano a questo:Creazione di istanze di modelli del proxy Django dalla loro classe di base

class Analysis(models.Model): 
    analysis_type = models.CharField(max_length=255) 

    def important_method(self): 
     ...do stuff... 


class SpecialAnalysis(Analysis): 
    class Meta: 
     proxy = True 

    def important_method(self): 
     ...something different... 

Tutto questo è abbastanza standard. Tuttavia, ciò che mi piacerebbe fare è convertire automaticamente un modello in un modello proxy basato sul valore del campo analysis_type. Ad esempio, mi piacerebbe essere in grado di scrivere codice che assomiglia a questo:

>>> analysis = Analysis.objects.create(analysis_type="nothing_special") 
>>> analysis.__class__ 
<class 'my_app.models.Analysis'> 

>>> analysis = Analysis.objects.create(analysis_type="special") 
>>> analysis.__class__ 
<class 'my_app.models.SpecialAnalysis'> 

>>> analysis = Analysis.objects.get(pk=2) 
>>> analysis.__class__ 
<class 'my_app.models.SpecialAnalysis'> 

>>> # calls the ``important_method`` of the correct model 
>>> for analysis in Analysis.objects.all(): 
...  analysis.important_method() 

È questo anche lontanamente possibile? Una domanda simile è stata posta a here, che in realtà fornisce un codice per l'esempio di iterazione, ma mi lascia ancora la domanda su come ottenere o creare un'istanza di una classe proxy dal suo genitore. Suppongo che potrei semplicemente scavalcare un gruppo di metodi manager, ma sento che ci deve essere un modo più elegante per farlo.

risposta

8

Non ho trovato un modo "pulito" o "elegante" per farlo. Quando mi sono imbattuto in questo problema l'ho risolto ingannando un po 'Python.

class Check(models.Model): 
    check_type = models.CharField(max_length=10, editable=False) 
    type = models.CharField(max_length=10, null=True, choices=TYPES) 
    method = models.CharField(max_length=25, choices=METHODS) 
    'More fields.' 

    def __init__(self, *args, **kwargs): 
     super(Check, self).__init__(*args, **kwargs) 
     if self.check_type: 
      map = {'TypeA': Check_A, 
       'TypeB': Check_B, 
       'TypeC': Check_C} 
      self.__class__ = map.get(self.check_type, Check) 

    def run(self): 
     'Do the normal stuff' 
     pass 


class Check_A(Check): 
    class Meta: 
     proxy = True 

    def run(self): 
     'Do something different' 
     pass 

class Check_B(Check): 
    class Meta: 
     proxy = True 

    def run(self): 
     'Do something different' 
     pass 


class Check_C(Check): 
    class Meta: 
     proxy = True 

    def run(self): 
     'Do something different' 
     pass 

Non è molto pulito ma è stato il trucco più semplice da trovare che ha risolto il mio problema.

Forse questo ti aiuta, forse no.

Inoltre sto sperando che qualcuno altro ha una soluzione più divinatorio a questo problema dal momento che sto contando i giorni fino a questo metodo non riesce e torna a tormentarmi ..

+1

Beh, questo è decisamente più pulito delle cose pazze che stavo provando. Grazie per l'aiuto! –

+0

È improbabile che il tuo metodo fallisca di per sé, ma la mappatura manuale non ha una buona scalabilità; i visitatori futuri sarebbero meglio serviti usando il metodo Model .__ subclasses __() per ottenere l'elenco dei modelli proxy e interrogandone alcuni/tutti in modo dinamico secondo necessità. – Ivan

4

Questo è un ottimo approccio e io don lo considero particolarmente un imbroglione. Ecco alcuni miglioramenti apportati alla funzione __init__ in modo che non debba cambiare quando si aggiungono più classi.

def __init__(self, *args, **kwargs): 
    super(Analysis, self).__init__(*args, **kwargs) 
    if not self.__type and type(self) == Analysis: 
     raise Exception("We should never create a base Analysis object. Please create a child proxy class instead.") 

    for _class in Analysis.__subclasses__(): 
     if self.check_type == _class.__name__: 
      self.__class__ = _class 
      break 

def save(self, **kwargs): 
    self.check_type = self.__class__.__name__ 
    super(Analysis, self).save(**kwargs) 

Spero che questo aiuti!

+0

Ooh bello. Non conoscevo '__subclasses__', migliorerebbe sicuramente la manutenibilità. – EWit

Problemi correlati