используя django, как мне создать экземпляр объекта прокси из экземпляра объекта суперкласса? - PullRequest
5 голосов
/ 13 октября 2010

Я все еще немного озадачен отношением моделей Proxy к их суперклассам в django. Теперь у меня вопрос: как мне получить экземпляр модели Proxy из уже извлеченного экземпляра суперкласса?

Итак, допустим, у меня есть:

class Animal(models.Model):
   type = models.CharField(max_length=20)
   name = models.CharField(max_length=40)

class Dog(Animal):  
   class Meta:
       proxy = True

   def make_noise(self):  
       print "Woof Woof"  

Class Cat(Animal):  
   class Meta:
       proxy = True

   def make_noise(self):  
       print "Meow Meow"

animals = Animal.objects.all()
for animal in animals:
   if (animal.type == "cat"):
      animal_proxy = # make me a cat
   elif (animal.type == "dog"):
      animal_proxy = # make me a dog
   animal_proxy.make_noise()

OK. Итак ... Что входит в "# make me cat", которое не требует запроса обратно в базу данных, например:

animal_proxy = Cat.objects.get(id=animal.id)

Есть ли простой способ создать экземпляр Cat из экземпляра Animal, который, как я знаю, является котом?

Ответы [ 2 ]

5 голосов
/ 13 октября 2010

Вы пытаетесь реализовать постоянство для иерархии наследования. Использование одного конкретного стола и переключателя type - хороший способ сделать это. Однако я думаю, что ваша реализация, а именно:

for animal in animals:
   if (animal.type == "cat"): 
      animal_proxy = # make me a cat

идет против зерна Джанго. Тип включения не должен быть посторонним для прокси (или модели) класса.

На вашем месте я бы сделал следующее:

Во-первых, добавьте менеджер с учетом типов к моделям прокси. Это гарантирует, что Dog.objects всегда будет извлекать Animal экземпляров с type="dog", а Cat.objects будет извлекать Animal экземпляров с type="cat".

class TypeAwareManager(models.Manager):
    def __init__(self, type, *args, **kwargs):
        super(TypeAwareManager, self).__init__(*args, **kwargs)
        self.type = type

    def get_query_set(self):
        return super(TypeAwareManager, self).get_query_set().filter(
              type = self.type)

class Dog(Animal):
    objects = TypeAwareManager('dog')
    ...

class Cat(Animal):
    objects = TypeAwareManager('cat')
    ...

Во-вторых, извлекать экземпляры подкласса отдельно. Затем вы можете объединить их, прежде чем работать с ними. Я использовал itertools.chain, чтобы объединить два Querysets.

from itertools import chain
q1 = Cat.objects.all() # [<Cat: Daisy [cat]>]

q2 = Dog.objects.all() # [<Dog: Bruno [dog]>]

for each in chain(q1, q2): 
    each.make_noise() 

# Meow Meow
# Woof Woof
3 голосов
/ 02 июля 2014

Я бы сделал:

def reklass_model(model_instance, model_subklass):

    fields = model_instance._meta.get_all_field_names()
    kwargs = {}
    for field_name in fields:
        try:
           kwargs[field_name] = getattr(model_instance, field_name)
        except ValueError as e: 
           #needed for ManyToManyField for not already saved instances
           pass

    return model_subklass(**kwargs)

animals = Animal.objects.all()
for animal in animals:
   if (animal.type == "cat"):
      animal_proxy = reklass_model(animal, Cat)
   elif (animal.type == "dog"):
      animal_proxy = reklass_model(animal, Cat)
   animal_proxy.make_noise()

# Meow Meow
# Woof Woof

Я не проверял это с "зоопарком";) но с моими собственными моделями, кажется, работает.

...