Estoy tratando de crear una clase de modelo con el módulo attrs . En mi script, la clase CollectionModel
se hereda en la clase User
. Entonces, lógicamente, todos los atributos en CollectionModel
deberían estar disponibles en User
. Pero al intentar crear una nueva instancia de Usuario desde el diccionario (es posible con attrs), muestra que los atributos de CollectionModel
no están presentes en el User
Mi guión:
from bson import ObjectId from attrs import asdict, define, field, validators, Factory import time @define class CollectionModel: """ Base Class For All Collection Schema""" _id : str = field(converter=str, default=Factory(ObjectId)) _timestamp : float = field(default=Factory(time.time)) def get_dict(self): return asdict(self) def update(self): self._timestamp = time.time() @define class User(CollectionModel): username : str = field(factory = str, validator=validators.instance_of(str)) userType : str = field(factory = str, validator=validators.instance_of(str)) password : str = field(factory = str, validator=validators.instance_of(str)) user_object = User() new_user_object = User(**asdict(user_object))
Aquí, estoy tratando de crear un nuevo objeto Usuario desde el user_object
. Muestra el siguiente error.
TypeError: User.__init__() got an unexpected keyword argument '_id'
Supuse que la clase principal no se inicia al principio. Así que traté de iniciarlo con la función super()
y, de acuerdo con la documentación de attrs, debería hacerse con __attrs_pre_init__
. De la documentación:
La única razón de la existencia de
__attrs_pre_init__
es dar a los usuarios la oportunidad de llamar asuper().__init__()
, porque algunas API basadas en subclases lo requieren.
Entonces la clase secundaria modificada se vuelve así.
@define class User(CollectionModel): username : str = field(factory = str, validator=validators.instance_of(str)) userType : str = field(factory = str, validator=validators.instance_of(str)) password : str = field(factory = str, validator=validators.instance_of(str)) def __attrs_pre_init__(self): super().__init__()
Pero el problema aún permanece. ¿Estoy haciendo el OOP de forma incorrecta? ¿O es solo un error del módulo attrs ?
He resuelto ese problema y había dos problemas. El primero, en python, subrayado antes de un nombre de atributo lo convierte en un atributo privado. Entonces, al crear una nueva instancia desde el diccionario _id
es una clave inesperada.
El segundo problema es que, de hecho, necesito un campo _id
ya que estoy trabajando con MongoDB. En MongoDB, el documento _id
está reservado para usar como clave principal.
Ahora, el primer problema se puede resolver reemplazando _id
por id_
(sí, guión bajo en el lado opuesto ya que id
es literalmente una mala elección para un nombre de variable en python).
Para el segundo problema, he buscado mucho para crear un alias para el campo id_
. Pero no es simplemente fácil trabajar con el módulo attrs
. Así que modifiqué el método get_dict()
para obtener un diccionario perfecto antes de incluir mis datos en la colección de MongoDB.
Aquí está la clase CollectionModel
modificada y la clase User
:
@define class CollectionModel: """ Base Class For All Collection Schema""" id_: ObjectId = field(default=Factory(ObjectId)) timestamp : float = field(default=Factory(time.time)) def get_dict(self): d = asdict(self) d["_id"] = d["id_"] del d["id_"] return d def update(self): self.timestamp = time.time() @define class User(CollectionModel): username : str = field(factory = str, validator=validators.instance_of(str)) userType : str = field(factory = str, validator=validators.instance_of(str)) password : str = field(factory = str, validator=validators.instance_of(str))
Ahora imprimiendo una instancia:
user_object = User() print(user_object.get_dict())
Producción:
{'timestamp': 1645131182.421929, 'username': '', 'userType': '', 'password': '', '_id': ObjectId('620eb5ae10f27b87de5be3a9')}