2011-05-19 12 views
5

Estoy trabajando en una aplicación de Django pero parece que es solo una pregunta de Python, sin nada necesariamente específico de Django. Soy bastante nuevo en Python, y es difícil de describir lo que estoy tratando de hacer, pero más fácil mostrar así que aquí va:¿Cómo aplicar genéricamente una sustitución de una función para mutiple clases en python?

tengo una clase:

class SlideForm(ModelForm): 

    class Meta: 
     model = Slide 

cual subclase:

class HiddenSlideForm(SlideForm): 
    def __init__(self, *args, **kwargs): 
     super(HiddenSlideForm, self).__init__(*args, **kwargs) 
     for name, field in self.fields.iteritems(): 
      field.widget = field.hidden_widget() 
      field.required = False 

y luego tengo otra clase:

class DeckForm(ModelForm):  

    def __init__(self, *args, **kwargs): 
     # do some stuff here 
     return super(DeckForm, self).__init__(*args, **kwargs) 

    class Meta: 
     model = Deck 
     # other stuff here 

que también sub-clase:

class HiddenDeckForm(DeckForm): 

    def __init__(self, *args, **kwargs): 
     super(HiddenDeckForm, self).__init__(*args, **kwargs) 
     for name, field in self.fields.iteritems(): 
      field.widget = field.hidden_widget() 
      field.required = False 

Tenga en cuenta que las subclases tienen exactamente el mismo código que los nombres de clase y hacen exactamente lo mismo. He estado tratando de averiguar cuál es la mejor manera de genéricos para poder mantenerlo SECO y usarlo fácilmente para otras clases, y he considerado decoradores y/o herencias múltiples, los cuales son conceptos nuevos para mí, pero sigue mezclándose.

¡Ayuda se aprecia!

(Como nota adicional, no dude en señalar cualquier problema que usted vea en mi código Django :))

+0

Pregunta quizás debería pedir una forma de resolver el problema para que no implique la incrustación de muchos campos ocultos en el formulario. –

+0

Gracias por la sugerencia, John. Publicó una pregunta por separado que describe el escenario en el que estoy usando esto. http://stackoverflow.com/questions/6054124. Siéntase libre de publicar una respuesta si tiene algunas ideas. Me encantaría descubrir cómo implementar esto de una manera más limpia. –

+0

+1 para encontrar un error en mi respuesta; Ahora sé más acerca de Python también! – SingleNegationElimination

Respuesta

4

Una opción es usar una clase Mixin; ejemplo:

En primer lugar, el comportamiento común va en el mixin:

class SomeMixin(object): 
    def __init__(self, *args, **kwargs): 
     super(SomeMixin, self).__init__(*args, **kwargs) 
     for name, field in self.fields.iteritems(): 
      field.widget = field.hidden_widget() 
      field.required = False 

En la medida en que usted está en control razonable de todas las clases en el gráfico de la herencia, y así siempre y cuando llame super en cada método que debe ser anulado, entonces no importa demasiado cómo se vean las clases derivadas.

Sin embargo, se encuentra con un problema cuando una de las superclases no llama por su cuenta al super en el momento correcto. Es muy importante que el método reemplazado, en ese caso, se llame último, ya que una vez que se invoca, no se realizarán más llamadas.

La solución más simple es asegurarse de que cada clase en realidad se deriva de la superclase infractora, pero en algunos casos, eso simplemente no es posible; ¡derivar una nueva clase crea un nuevo objeto que realmente no quieres que exista! Otra razón podría ser porque la clase base lógica está demasiado arriba en el árbol de herencia para funcionar.\

En ese caso, debe prestar especial atención al pedido en el que se enumeran las clases base. Python considerará primero la superclase más a la izquierda, a menos que haya una clase derivada más presente en el diagrama de herencia. Este es un tema complicado, y para entender qué es lo que realmente está haciendo python, debería leer sobre el presente C3 MRO algorithm en python 2.3 y posteriores.

Las clases base que antes, pero ya que todo el código común proviene de la mixin, las clases derivadas se vuelven triviales

class HiddenSlideForm(SomeMixin, SlideForm): 
    pass 

class HiddenDeckForm(SomeMixin, DeckForm): 
    pass 

Tenga en cuenta que la clase mixin aparece primero , ya que no podemos controlar lo las clases *Form lo hacen en sus métodos init.

Si los métodos __init__ de cualquiera de ellos no son triviales, aún así obtiene una victoria.

class HiddenSlideForm(SomeMixin, SlideForm): 
    def __init__(self, *args, **kwargs): 
     super(HiddenSlideForm, self).__init__(*args, **kwargs) 
     do_something_special() 

Asegúrese de que object es en el diagrama de herencia, en alguna parte. Cosas extrañas pueden suceder de otra manera.

+2

¡Gracias por la respuesta! Sí heredo de las clases base suministradas por Django (ModelForm), pero tu código no funciona para mí. La función init en el Mixin nunca se llama. Aquí hay un caso de prueba simple que demuestra lo que sucede: http://pastebin.com/a1tnXsjA. ¿Alguna idea de lo que es la solución? (por cierto estoy en Python 2.6) –

+0

En lugar de usar super, simplemente llame '__init __()' de 'SomeMixin' y' SlideForm' explícitamente (uno después del otro) en 'HiddenSlideForm .__ init __()'. – Imran

+0

Imram, aunque esto no es ideal, ya que tendré que escribir un init en cada clase de niño, parece que puede ser la manera de hacerlo, ya que las otras ideas sugeridas para automatizar la anulación con un Mixin no parece que funciona Si crea una versión de formulario de respuesta de su comentario, lo marcaré como aceptado, ya que realmente funciona. –

0

La herencia múltiple (en concreto, Mixins) sería probablemente la mejor solución a este problema.

Ejemplo:

class HiddenFormMixin(object): 
    def __init__(self, *args, **kwargs): 
     for name, field in self.fields.iteritems(): 
      field.widget = field.hidden_widget() 
      field.required = False 


class SlideForm(ModelForm): 
    class Meta: 
     model = Slide 


class HiddenSlideForm(SlideForm, HiddenFormMixin): 
    pass 


class DeckForm(ModelForm):  
    def __init__(self, *args, **kwargs): 
     # do some stuff here 
     return super(DeckForm, self).__init__(*args, **kwargs) 

    class Meta: 
     model = Deck 
     # other stuff here 


class HiddenDeckForm(DeckForm, HiddenFormMixin): 
    pass 

tenga en cuenta que esto podría no trabajar directamente si sobrescribe __init__ en ambas clases. En ese caso, podría hacer algo como esto para especificar el orden:

class HiddenDeckForm(DeckForm, HiddenFormMixin): 
    def __init__(self, *args, **kwargs): 
     # do some stuff here 
     DeckForm.__init__(self, *args, **kwargs) 
     HiddenFormMixin.__init__(self, *args, **kwargs) 
+2

Hmm ... no del todo. En este caso, debe llamar a 'super', especialmente con las clases mixin que anulan los métodos especiales. – SingleNegationElimination

Cuestiones relacionadas