2013-08-27 9 views
5

Mam szereg modeli, które wyglądać tak:Tworzenie instancji modeli proxy Django ze swojej klasy bazowej

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... 

To wszystko jest dość standardowy. Jednak chciałbym automatycznie przekonwertować model Analysis na model proxy na podstawie wartości pola analysis_type. Na przykład chciałbym móc napisać kod wyglądający następująco:

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

Czy to jest nawet możliwe? Podobne pytanie zostało zadane here, które faktycznie podaje kod dla przykładu iteracji, ale nadal pozostawia mnie z pytaniem, jak uzyskać lub utworzyć instancję klasy proxy od jej rodzica. Przypuszczam, że mógłbym zastąpić kilka metod zarządzania, ale wydaje mi się, że musi to być bardziej elegancki sposób.

Odpowiedz

8

Nie znalazłem "czystej" lub "eleganckiej" metody, aby to zrobić. Kiedy wpadłem na ten problem, rozwiązałem go, trochę oszukując Pythona.

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 

To naprawdę nie jest czyste, ale to był najłatwiejszy hack do znalezienia, który rozwiązał mój problem.

Może to ci pomaga, może nie.

Mam również nadzieję, że ktoś inny ma więcej pythonic rozwiązanie tego problemu, ponieważ liczę dni do tej metody nie powiedzie się i wraca do mnie prześladować ..

+1

Cóż, jest to zdecydowanie czystsze niż szalone rzeczy, które próbowałem. Dzięki za pomoc! –

+0

Twoja metoda raczej nie zawiedzie sama w sobie, ale ręczne mapowanie zdecydowanie nie skaluje się; przyszli goście mogliby być lepiej obsługiwani za pomocą metody .__ podklas __(), aby uzyskać listę modeli proxy i zapytać niektórych/wszystkich dynamicznie, w razie potrzeby. – Ivan

4

To jest wielki podejście i don szczególnie postrzega to jako oszust. Oto IMHO kilka ulepszeń funkcji __init__, więc nie musi się zmieniać po dodaniu kolejnych klas.

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) 

Mam nadzieję, że to pomoże!

+0

Ooch, miło. Nie wiedziałem o '__subclasses__', zdecydowanie poprawiłoby to łatwość konserwacji. – EWit

Powiązane problemy