registros pages page modelo listar lista example datos crear administrador python django django-models proxy-classes

python - pages - ¿Forma correcta de devolver la instancia del modelo de proxy desde una instancia de modelo base en Django?



listar registros django (4)

El enfoque de Metaclass propuesto por thedk es de hecho un camino muy poderoso, sin embargo, tuve que combinarlo con una respuesta a la pregunta here para que la consulta devuelva una instancia de modelo de proxy . La versión simplificada del código adaptada al ejemplo anterior sería:

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

La ventaja de este enfoque de proxy es que no se requiere migración de db al crear nuevas subclases. El inconveniente es que no se pueden agregar campos específicos a las subclases.

Me gustaría recibir comentarios sobre este enfoque.

Digamos que tengo modelos:

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

Digamos que quiero hacer:

animals = Animal.objects.all() for animal in animals: animal.make_sound()

Quiero recuperar una serie de Woofs y Meows. Claramente, podría definir un make_sound en el modelo original que se basa en animal_type, pero cada vez que agrego un nuevo tipo de animal (imagino que están en diferentes aplicaciones), tendría que entrar y editar esa función make_sound . Preferiría simplemente definir modelos de proxy y hacer que ellos mismos definan el comportamiento. Por lo que puedo decir, no hay forma de devolver instancias mixtas de Cat o Dog, pero pensé que tal vez podría definir un método "get_proxy_model" en la clase principal que devuelve un modelo de gato o de perro.

Seguramente podrías hacer esto, y pasar algo como la clave principal y luego simplemente hacer Cat.objects.get (pk = passed_in_primary_key). Pero eso significaría hacer una consulta adicional para los datos que ya tiene que parecen redundantes. ¿Hay alguna manera de convertir a un animal en una instancia de gato o perro de manera eficiente? ¿Cuál es la forma correcta de hacer lo que quiero lograr?


Esta respuesta puede dejar de lado la pregunta porque no usa modelos de proxy. Sin embargo, como pregunta, permite escribir lo siguiente (y sin tener que actualizar la clase Animal si se agregan nuevos tipos) -

animals = Animal.objects.all() for animal in animals: animal.make_sound()

Para evitar la programación de metaclases, se podría usar la composición sobre la herencia . Por ejemplo--

class Animal(models.Model): type = models.CharField(max_length=255) @property def type_instance(self): """Return a Dog or Cat object, etc.""" return globals()[self.type]() def make_sound(self): return self.type_instance.make_sound() class Dog(object): def make_sound(self): print "Woof!" class Cat(object): def make_sound(self): print "Meow!"

Si las clases Dog y Cat necesitan acceder a la instancia de Animal , también puedes ajustar el método type_instance() anterior para pasarle lo que necesita al constructor de la clase (por ejemplo, self ).


Quizás puedas hacer que los modelos de Django sean polimórficos usando el enfoque descrito here . Ese código está en las primeras etapas de desarrollo, creo, pero vale la pena investigarlo.


la única forma conocida por los humanos es usar la programación de Metaclass.

Aquí hay una respuesta breve:

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!"

y el resultado deseado:

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

¿Como funciona?

  1. Almacenar información sobre el nombre de clase del animal - utilizamos object_class para ese
  2. Elimina el meta atributo "proxy" - necesitamos revertir la relación en Django (el lado malo de esto, creamos una tabla DB adicional para cada modelo hijo y desperdiciamos un golpe de DB adicional para eso, lo bueno es que podemos agregar algunos campos secundarios dependientes del modelo)
  3. Personalice save () para Animal para guardar el nombre de clase en object_class del objeto que invoca save.
  4. El método get_object es necesario para hacer referencia mediante relación inversa en Django al modelo con el nombre almacenado en caché en object_class.
  5. Haga esto .get_object () "fundición" automáticamente cada vez que se instancia Animal, redefiniendo la Metaclass of Animal model. Metaclass es algo así como una plantilla para una clase (al igual que una clase es una plantilla para un objeto).

Más información sobre Metaclass en Python: http://www.ibm.com/developerworks/linux/library/l-pymeta.html