MongoEngine/mongoengine

DDD and mongoengine

JaktensTid opened this issue · 0 comments

Hi,

I am trying to create a python project with DDD, but do not distinguish ORM classes and DDD classes
The problem is that using custom types for the documents is really cumbersome

For example I am trying to call .delete, there was an error that I cannot
cannot encode object: ExperimentId(value=UUID('dd9ee71c-d47b-4126-a3de-e66f2e0d87db'))

Ok, I added def delete where I override such values which is a dirty hack

class Experiment(Document, Entity):
    id: ExperimentId = ExperimentId.Field(db_field='_id', primary_key=True, required=True)
    name: ExperimentName = ExperimentName.Field(required=True)
    created_at: datetime.datetime = DateTimeField(default=datetime.datetime.utcnow)

    def __init__(self, id: ExperimentId, name: ExperimentName, created_at: datetime.datetime | None = None, *args, **kwargs):
        if not id:
            raise NoLabsException('Experiment id is empty', ErrorCodes.invalid_experiment_id)

        if not name:
            raise NoLabsException('Experiment name is empty', ErrorCodes.invalid_experiment_name)

        created_at = created_at if created_at else datetime.datetime.now(tz=datetime.timezone.utc)

        super().__init__(id=id, name=name, created_at=created_at, *args, **kwargs)

    def set_name(self, name: ExperimentName):
        if not name:
            raise NoLabsException('Name cannot be empty', ErrorCodes.invalid_experiment_name)

        self.name = name

    def delete(self, signal_kwargs=None, **write_concern):
        self.__class__.objects.filter(id=self.id.value).delete()

Another problem arises when I try to dereference ListField(ReferenceField)

class LocalisationJob(Job):
    amino_acids: List[AminoAcid] = ListField(ReferenceField(AminoAcid, required=False, reverse_delete_rule=PULL))
    probabilities: List[LocalisationJobResult] = EmbeddedDocumentListField(LocalisationJobResult)

    def __init__(self, id: JobId, name: JobName, experiment: Experiment, *args, **kwargs):
        if not id:
            raise NoLabsException('Job id is invalid', ErrorCodes.invalid_job_id)
        if not name:
            raise NoLabsException('Job name is invalid', ErrorCodes.invalid_job_name)
        if not experiment:
            raise NoLabsException('Job must be in experiment', ErrorCodes.invalid_experiment_id)

        super().__init__(id=id, name=name, experiment=experiment, *args, **kwargs)

when I do

aa = job.amino_acids

it throws me

cannot encode object: AminoAcidId(value=UUID('dd9ee71c-d47b-4126-a3de-e66f2e0d87db')), of type: <class 'nolabs.refined.domain.models.common.AminoAcidId'>"

So the question - is there a way to defined custom types to be treated as primitivies once and ever in mongodb?

def to_python and to_mongo help only while saving the entities, but all the queries and delete operations do not work

For example AminoAcidId will ever must be convertible to UUID, ExperimentName to str

Example of AminoAcidId

@dataclass
class AminoAcidId(ValueObjectUUID):
    def __post_init__(self):
        if not self.value:
            NoLabsException.throw(ErrorCodes.invalid_aa_id)

    def __hash__(self):
        return hash(self.value)

    def __eq__(self, other):
        if not isinstance(other, AminoAcidId):
            return False

        return self.value == other.value

    class Field(fields.BaseField):
        def to_mongo(self, value):
            if isinstance(value, AminoAcidId):
                return value.value
            else:
                return value

        def to_python(self, value):
            if isinstance(value, UUID):
                return AminoAcidId(value)
            return value