sot*_*ips 26 python django proxy-classes django-models
说我有模特:
class Animal(models.Model):
type = models.CharField(max_length=255)
class Dog(Animal):
def make_sound(self):
print "Woof!"
class Meta:
proxy = True
class Cat(Animal):
def make_sound(self):
print "Meow!"
class Meta:
proxy = True
Run Code Online (Sandbox Code Playgroud)
让我们说我想做:
animals = Animal.objects.all()
for animal in animals:
animal.make_sound()
Run Code Online (Sandbox Code Playgroud)
我想找回一系列的Woofs和Meows.很明显,我可以在原始模型中定义一个基于animal_type分叉的make_sound,但每次我添加一个新的动物类型(想象它们在不同的应用程序中)时,我必须进入并编辑make_sound函数.我宁愿只定义代理模型并让它们自己定义行为.据我所知,没有办法返回混合的Cat或Dog实例,但我想也许我可以在主类上定义一个返回cat或dog模型的"get_proxy_model"方法.
当然你可以这样做,并传递类似主键的东西,然后只做Cat.objects.get(pk = passed_in_primary_key).但这意味着对您已经拥有的数据进行额外查询,这似乎是多余的.有没有办法以有效的方式将动物变成猫或狗的实例?做我想做的事的正确方法是什么?
Sam*_*uel 11
通过thedk提出的元类的做法的确是去一个非常强大的方式,但是,我不得不把它与答案相结合的问题这里有查询返回的代理模型实例.适用于前一个示例的代码的简化版本将是:
from django.db.models.base import ModelBase
class InheritanceMetaclass(ModelBase):
def __call__(cls, *args, **kwargs):
obj = super(InheritanceMetaclass, cls).__call__(*args, **kwargs)
return obj.get_object()
class Animal(models.Model):
__metaclass__ = InheritanceMetaclass
type = models.CharField(max_length=255)
object_class = models.CharField(max_length=20)
def save(self, *args, **kwargs):
if not self.object_class:
self.object_class = self._meta.module_name
super(Animal, self).save( *args, **kwargs)
def get_object(self):
if self.object_class in SUBCLASSES_OF_ANIMAL:
self.__class__ = SUBCLASSES_OF_ANIMAL[self.object_class]
return self
class Dog(Animal):
class Meta:
proxy = True
def make_sound(self):
print "Woof!"
class Cat(Animal):
class Meta:
proxy = True
def make_sound(self):
print "Meow!"
SUBCLASSES_OF_ANIMAL = dict([(cls.__name__, cls) for cls in ANIMAL.__subclasses__()])
Run Code Online (Sandbox Code Playgroud)
此代理方法的优点是在创建新子类时不需要db迁移.缺点是没有特定的字段可以添加到子类.
我很乐意得到关于这种方法的反馈.
人类已知的唯一方法是使用元类编程。
这是简短的答案:
from django.db.models.base import ModelBase
class InheritanceMetaclass(ModelBase):
def __call__(cls, *args, **kwargs):
obj = super(InheritanceMetaclass, cls).__call__(*args, **kwargs)
return obj.get_object()
class Animal(models.Model):
__metaclass__ = InheritanceMetaclass
type = models.CharField(max_length=255)
object_class = models.CharField(max_length=20)
def save(self, *args, **kwargs):
if not self.object_class:
self.object_class = self._meta.module_name
super(Animal, self).save( *args, **kwargs)
def get_object(self):
if not self.object_class or self._meta.module_name == self.object_class:
return self
else:
return getattr(self, self.object_class)
class Dog(Animal):
def make_sound(self):
print "Woof!"
class Cat(Animal):
def make_sound(self):
print "Meow!"
Run Code Online (Sandbox Code Playgroud)
和预期的结果:
shell$ ./manage.py shell_plus
From 'models' autoload: Animal, Dog, Cat
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> dog1=Dog(type="Ozzie").save()
>>> cat1=Cat(type="Kitty").save()
>>> dog2=Dog(type="Dozzie").save()
>>> cat2=Cat(type="Kinnie").save()
>>> Animal.objects.all()
[<Dog: Dog object>, <Cat: Cat object>, <Dog: Dog object>, <Cat: Cat object>]
>>> for a in Animal.objects.all():
... print a.type, a.make_sound()
...
Ozzie Woof!
None
Kitty Meow!
None
Dozzie Woof!
None
Kinnie Meow!
None
>>>
Run Code Online (Sandbox Code Playgroud)
它是如何工作的?
有关Python中的元类的更多信息:http : //www.ibm.com/developerworks/linux/library/l-pymeta.html