在Django中,如何从父类对象实例构造代理对象实例?

8 投票
2 回答
2123 浏览
提问于 2025-04-16 05:24

我对Django中的代理模型和它们的父类之间的关系还有点困惑。现在我想知道,如何从已经获取的父类实例中得到一个代理模型的实例呢?

假设我有:

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

好的。那么,"# 让我变成一只猫" 里面应该放什么内容,才能不需要再去数据库查询,比如:

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

有没有简单的方法可以从我知道是猫的动物实例中创建一个猫的实例呢?

2 个回答

3

我会这样做:

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

我还没有用“动物园”来测试过这个;)不过用我自己的模型测试是有效的。

7

你正在尝试为一个继承结构实现持久化存储。使用一个具体的表和一个 type 开关是个不错的办法。不过,我觉得你的实现方式,特别是:

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

有点不符合 Django 的设计理念。根据类型进行切换不应该是代理(或模型)类之外的事情。

如果我是你,我会这样做:

首先,给代理模型添加一个“类型感知”的管理器。这样可以确保 Dog.objects 总是获取到 type="dog"Animal 实例,而 Cat.objects 则获取到 type="cat"Animal 实例。

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

撰写回答