-
Notifications
You must be signed in to change notification settings - Fork 2
Description
We should consider de-duplicating repeated view-model structures by introducing shared Pydantic mixins/components for common fields (e.g., externalLinks, with URN). This would reduce duplication and drift between view models, centralize validation, aliasing, and serialization rules for shared fields and simplify future additions of linkouts/collections across resources.
Scope:
Within the new components/ directory introduced in #619, we should add a mixins.py with WithExternalLinks, WithOfficialCollections, WithUrn, etc. to compose shared fields. This would allow us to add new properties to view models easily while keeping validation logic with-in mix-ins, reducing duplicated code.
For example, we could construct a WithUrn mix-in and handle all URN validation there:
# src/mavedb/view_models/components/mixins.py
from typing import ClassVar, Iterable, Optional
import re
from pydantic import BaseModel, Field, field_validator
class WithUrn(BaseModel):
urn: str = Field(..., alias="urn", description="Stable URN identifier")
# Provide any number of acceptable patterns; subclasses override this.
_urn_patterns: ClassVar[Iterable[re.Pattern[str]]] = ()
@field_validator("urn")
@classmethod
def validate_urn(cls, v: str) -> str:
v = v.strip()
if not v:
raise ValueError("urn cannot be empty")
patterns = list(cls._urn_patterns)
if not patterns:
return v # no constraints configured
if any(p.match(v) for p in patterns):
return v
# Helpful error with pattern names (if available)
expected = " or ".join(getattr(p, "name", p.pattern) for p in patterns)
raise ValueError(f"invalid URN format; expected: {expected}")You could then override the generic class with specific patterns we want to mix-in:
# src/mavedb/view_models/components/mixins.py (continued from above)
from mavedb.lib.validation.urn_re import SCORE_SET_URN_RE, EXPERIMENT_SET_URN_RE, TMP_URN_RE
class WithScoreSetOrTmpUrn(WithUrn):
urn_patterns = (SCORE_SET_URN_RE, TMP_URN_RE)
class WithExperimentSetOrTmpUrn(WithUrn):
urn_patterns = (EXPERIMENT_SET_URN_RE, TMP_URN_RE)And finally inherit from these specific patterns in each class that has a URN
from mavedb.view_models.base.base import Base
from src.mavedb.view_models.components.mixins import WithScoreSetOrTmpUrn
class ScoreSetSaved(WithScoreSetOrTmpUrn, Base):
title: str
... other properties ...