es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Utilizando la misma clase en la definición del atributo de la clase.

¿Cómo puedo hacer algo como esto?

class Formulario(object, metaclass=...):
    pass

class Campo(object):
    def __init__(self, clase_formulario_anidado=None):
        self.clase_formulario_anidado = clase_formulario_anidado

class FormularioTarea(Formulario):
    hijos = Campo(clase_formulario_anidado=FormularioTarea)

Rendereiza:

NameError: el nombre 'FormularioTarea' no está definido

El error ocurre en la línea que define el atributo ‘hijos’.

Utilizo atributos de clase en la función __new__ de la metaclase y no puedo moverlo a la función __init__ de la clase actual.

Mi metaclase (como en el framework de Django):

class MetaForm(type):
    """
    Metaclase para extraer campos del modelo
    """

    def __new__(mcs, nombre, bases, attrs):
        campos_encontrados = dict()

        for k, v in attrs.items():
            if isinstance(v, Campo):
                campos_encontrados[k] = v

        attrs['campos_encontrados'] = campos_encontrados

        nueva_clase = super().__new__(mcs, nombre, bases, attrs)

        campos_padres = {}

        for base in reversed(nueva_clase.__mro__):
            # Recolecta los campos de la clase base.
            if hasattr(base, 'campos_encontrados'):
                campos_padres.update(base.campos_encontrados)

            # Deshabilita los campos reordenados.
            for attr, value in base.__dict__.items():
                if value is None and attr in campos_padres:
                    campos_padres.pop(attr)

        nueva_clase.campos_base = campos_padres
        nueva_clase.campos_encontrados = campos_padres

        return nueva_clase
Tags:  ,

Answer

  1. Avatar for davy.ai

    Para crear una metaclase que permita la funcionalidad deseada, puedes hacer lo siguiente:

    class FormMeta(type):
        def __new__(cls, name, bases, attrs):
            new_attrs = {}
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    fields[key] = value.nested_form_class
                new_attrs[key] = value
            new_attrs['fields'] = fields
            return super().__new__(cls, name, bases, new_attrs)
    
    
    class Form(metaclass=FormMeta):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            for field_name, field_class in self.fields.items():
                setattr(self, field_name, field_class())
    
    
    class Field:
        def __init__(self, nested_form_class=None):
            self.nested_form_class = nested_form_class
    
    
    class TaskForm(Form):
        children = Field(nested_form_class='TaskForm')
    

    La metaclase FormMeta te permite crear dinámicamente un atributo fields en la clase, el cual es un diccionario de nombres de campo a los atributos nested_form_class definidos en cada Field. Luego, se define la clase base Form para instanciar cada objeto Field como un atributo en la nueva instancia de la clase. Finalmente, se define la clase TaskForm para utilizar una cadena en lugar de una referencia real a sí misma al definir el campo children, lo cual permite que sea definido antes de que TaskForm sea efectivamente definida.

Comments are closed.