Skip to content

models#

SystemCreatorBaseModel#

Bases: Model

Abstact model with system creator field.

Source code in src/apps/common/models.py
class SystemCreatorBaseModel(models.Model):
    """Abstact model with system creator field."""

    system_creator = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        related_name="%(app_label)s_%(class)ss",
        null=True,
        editable=False,
        blank=True,
    )

    class Meta:
        abstract = True

CustomSoftDeletableQuerySet#

Bases: QuerySet

Customized version of model_utils SoftDeletableQuerySet QuerySet for SoftDeletableModel. Instead of removing instance sets its removed field to timestamp.

Source code in src/apps/common/models.py
class CustomSoftDeletableQuerySet(QuerySet):
    """Customized version of model_utils SoftDeletableQuerySet
    QuerySet for SoftDeletableModel. Instead of removing instance sets
    its ``removed`` field to timestamp.
    """

    def delete(self):
        """
        Soft delete objects from queryset (set their ``removed``
        field to current timestamp)
        """
        self.update(removed=timezone.now())

delete() #

Soft delete objects from queryset (set their removed field to current timestamp)

Source code in src/apps/common/models.py
def delete(self):
    """
    Soft delete objects from queryset (set their ``removed``
    field to current timestamp)
    """
    self.update(removed=timezone.now())

CustomSoftDeletableManager#

Bases: Manager

Customized version of model_utils SoftDeletableManager Manager that limits the queryset by default to show only not removed instances of model.

Source code in src/apps/common/models.py
class CustomSoftDeletableManager(models.Manager):
    """Customized version of model_utils SoftDeletableManager
    Manager that limits the queryset by default to show only not removed
    instances of model.
    """

    _queryset_class = CustomSoftDeletableQuerySet

    def __init__(self, *args, _emit_deprecation_warnings=False, **kwargs):
        self.emit_deprecation_warnings = _emit_deprecation_warnings
        super().__init__(*args, **kwargs)

    def get_queryset(self):
        """
        Return queryset limited to not removed entries.
        """

        if self.emit_deprecation_warnings:
            warning_message = (
                "{0}.objects model manager will include soft-deleted objects in an "
                "upcoming release; please use {0}.available_objects to continue "
                "excluding soft-deleted objects. See "
                "https://django-model-utils.readthedocs.io/en/stable/models.html"
                "#softdeletablemodel for more information."
            ).format(self.model.__class__.__name__)
            warnings.warn(warning_message, DeprecationWarning)

        kwargs = {"model": self.model, "using": self._db}
        if hasattr(self, "_hints"):
            kwargs["hints"] = self._hints

        return self._queryset_class(**kwargs).filter(removed__isnull=True)

get_queryset() #

Return queryset limited to not removed entries.

Source code in src/apps/common/models.py
def get_queryset(self):
    """
    Return queryset limited to not removed entries.
    """

    if self.emit_deprecation_warnings:
        warning_message = (
            "{0}.objects model manager will include soft-deleted objects in an "
            "upcoming release; please use {0}.available_objects to continue "
            "excluding soft-deleted objects. See "
            "https://django-model-utils.readthedocs.io/en/stable/models.html"
            "#softdeletablemodel for more information."
        ).format(self.model.__class__.__name__)
        warnings.warn(warning_message, DeprecationWarning)

    kwargs = {"model": self.model, "using": self._db}
    if hasattr(self, "_hints"):
        kwargs["hints"] = self._hints

    return self._queryset_class(**kwargs).filter(removed__isnull=True)

CustomSoftDeletableModel#

Bases: Model

Customized version of model_utils SoftDeletableModel

Source code in src/apps/common/models.py
class CustomSoftDeletableModel(models.Model):
    """Customized version of model_utils SoftDeletableModel"""

    objects = CustomSoftDeletableManager(_emit_deprecation_warnings=True)
    available_objects = CustomSoftDeletableManager()
    all_objects = models.Manager()
    removed = models.DateTimeField(null=True, blank=True, editable=False)

    def delete(self, using=None, soft=True, *args, **kwargs):
        """
        Soft delete object (set its ``removed`` field to current time).
        Actually delete object if setting ``soft`` to False.
        """
        if soft:
            self.removed = timezone.now()
            self.save(using=using)
        else:
            return super().delete(using=using, *args, **kwargs)

    class Meta:
        abstract = True

delete(using=None, soft=True, *args, **kwargs) #

Soft delete object (set its removed field to current time). Actually delete object if setting soft to False.

Source code in src/apps/common/models.py
def delete(self, using=None, soft=True, *args, **kwargs):
    """
    Soft delete object (set its ``removed`` field to current time).
    Actually delete object if setting ``soft`` to False.
    """
    if soft:
        self.removed = timezone.now()
        self.save(using=using)
    else:
        return super().delete(using=using, *args, **kwargs)

AbstractBaseModel#

Bases: SystemCreatorBaseModel, TimeStampedModel, CustomSoftDeletableModel

Adds soft-delete and created / modified timestamp functionalities

Added fields are
  • created
  • modified
  • removed
Source code in src/apps/common/models.py
class AbstractBaseModel(SystemCreatorBaseModel, TimeStampedModel, CustomSoftDeletableModel):
    """Adds soft-delete and created / modified timestamp functionalities

    Added fields are:
     - created
     - modified
     - removed
    """

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    class Meta:
        abstract = True
        get_latest_by = "modified"
        ordering = ["created", "id"]

AbstractDatasetProperty#

Bases: AbstractBaseModel

Base class for simple refdata fields with only id and title properties

Attributes:

Name Type Description
title HstoreField

property title

url URLField

property url

Source code in src/apps/common/models.py
class AbstractDatasetProperty(AbstractBaseModel):
    """Base class for simple refdata fields with only id and title properties

    Attributes:
        title (HstoreField): property title
        url (models.URLField): property url
    """

    url = models.URLField(
        max_length=512,
        help_text="valid url to the property definition",
    )
    title = HStoreField(help_text='example: {"en":"title", "fi":"otsikko"}')

    def __str__(self):
        return self.url

    class Meta(AbstractBaseModel.Meta):
        abstract = True

AbstractFreeformConcept#

Bases: AbstractBaseModel

Permissive version of concept object with added custom fields

Necessary for objects that do not conform to the requirements of reference data. Should only be used with core-app.

Attributes:

Name Type Description
title HstoreField

property title, usually this would be pref_label in reference data

pref_label HStoreField

title of the concept

description HStoreField

detailed freeform description of the concept

in_scheme URLField

scheme of the concept

Source code in src/apps/common/models.py
class AbstractFreeformConcept(AbstractBaseModel):
    """Permissive version of concept object with added custom fields

    Necessary for objects that do not conform to the requirements of reference data. Should only be used with core-app.

    Attributes:
        title (HstoreField): property title, usually this would be pref_label in reference data
        pref_label (HStoreField): title of the concept
        description (HStoreField): detailed freeform description of the concept
        in_scheme (models.URLField): scheme of the concept
    """

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    pref_label = HStoreField(
        blank=True, null=True, help_text="The preferred label of the concept."
    )
    definition = HStoreField(
        blank=True, null=True, help_text="Definition or formal explanation of the concept."
    )
    concept_identifier = models.URLField(
        max_length=512, help_text="An URL identifying the concept."
    )
    in_scheme = models.URLField(
        max_length=255, null=True, blank=True, help_text="The scheme the concept belongs to."
    )

    class Meta(AbstractDatasetProperty.Meta):
        abstract = True

    def __str__(self):
        return str({k: v for k, v in self.__dict__.items() if v is not None})

ProxyBasePolymorphicModel#

Bases: PolymorphicModel

Base class for models instantiated as one of their proxy classes.

Provider helper functions for choosing proxy class based on value defined by proxy_lookup_field.

Mapping of proxy_lookup_field values to proxy models is determined by the proxy_mapping dict.

Actual class is determined by polymorphic_ctype_id ContentType field.

Source code in src/apps/common/models.py
class ProxyBasePolymorphicModel(PolymorphicModel):
    """Base class for models instantiated as one of their proxy classes.

    Provider helper functions for choosing proxy class based on value
    defined by proxy_lookup_field.

    Mapping of proxy_lookup_field values to proxy models is determined by
    the proxy_mapping dict.

    Actual class is determined by polymorphic_ctype_id ContentType field.
    """

    objects = ProxyBasePolymorphicManager()

    # Subclasses need to define these values
    proxy_mapping: Dict[str, str]  # map field value to proxy class name
    proxy_lookup_field: str  # field used for determining proxy class

    class Meta:
        abstract = True

    @classmethod
    def get_proxy_instance(cls, *args, **kwargs) -> models.Model:
        """Return new proxy model instance with supplied arguments.

        Proxy model is determined from proxy lookup field value in keyword arguments."""
        if not cls.proxy_lookup_field in kwargs:
            raise ValueError(f"Expected {cls.proxy_lookup_field} keyword argument.")
        model = cls.get_proxy_model(kwargs[cls.proxy_lookup_field])
        return model(*args, **kwargs)

    @classmethod
    @functools.lru_cache
    def get_proxy_model(cls, lookup_value) -> models.Model:
        """Return proxy model corresponding to lookup_value."""
        proxy_name = cls.proxy_mapping.get(lookup_value)
        if not proxy_name:
            raise ValueError(f"Unknown {cls.proxy_lookup_field} value {lookup_value}.")
        for proxy_cls in cls.get_proxy_classes():
            if proxy_name == proxy_cls.__name__:
                return proxy_cls
        raise ValueError(
            f"{cls.__name__} proxy {proxy_name} "
            f"not found for {cls.proxy_lookup_field} value {lookup_value}."
        )

    @classmethod
    def get_proxy_classes(cls) -> Iterator[models.Model]:
        """Get proxy subclasses and also current class if it's a proxy."""
        if cls._meta.proxy:
            yield cls
        yield from cls.get_proxy_subclasses()

    @classmethod
    def get_proxy_subclasses(cls) -> Iterator[models.Model]:
        """Get all proxy subclasses recursively."""
        for subclass in cls.__subclasses__():
            if subclass._meta.proxy:
                yield from subclass.get_proxy_subclasses()
                yield subclass

    def save(self, *args, **kwargs):
        """Raise error if proxy_lookup_field value does not match current model."""
        proxy = self.get_proxy_model(getattr(self, self.proxy_lookup_field))
        is_correct_proxy_model = self.__class__ == proxy
        if not is_correct_proxy_model:
            raise ValueError(
                f"Wrong type {self.__class__.__name__} "
                f"for {self.proxy_lookup_field} value '{self.storage_service}', "
                f"expected {proxy.__name__}."
            )
        return super().save(*args, **kwargs)

get_proxy_classes() classmethod #

Get proxy subclasses and also current class if it's a proxy.

Source code in src/apps/common/models.py
@classmethod
def get_proxy_classes(cls) -> Iterator[models.Model]:
    """Get proxy subclasses and also current class if it's a proxy."""
    if cls._meta.proxy:
        yield cls
    yield from cls.get_proxy_subclasses()

get_proxy_instance(*args, **kwargs) classmethod #

Return new proxy model instance with supplied arguments.

Proxy model is determined from proxy lookup field value in keyword arguments.

Source code in src/apps/common/models.py
@classmethod
def get_proxy_instance(cls, *args, **kwargs) -> models.Model:
    """Return new proxy model instance with supplied arguments.

    Proxy model is determined from proxy lookup field value in keyword arguments."""
    if not cls.proxy_lookup_field in kwargs:
        raise ValueError(f"Expected {cls.proxy_lookup_field} keyword argument.")
    model = cls.get_proxy_model(kwargs[cls.proxy_lookup_field])
    return model(*args, **kwargs)

get_proxy_model(lookup_value) cached classmethod #

Return proxy model corresponding to lookup_value.

Source code in src/apps/common/models.py
@classmethod
@functools.lru_cache
def get_proxy_model(cls, lookup_value) -> models.Model:
    """Return proxy model corresponding to lookup_value."""
    proxy_name = cls.proxy_mapping.get(lookup_value)
    if not proxy_name:
        raise ValueError(f"Unknown {cls.proxy_lookup_field} value {lookup_value}.")
    for proxy_cls in cls.get_proxy_classes():
        if proxy_name == proxy_cls.__name__:
            return proxy_cls
    raise ValueError(
        f"{cls.__name__} proxy {proxy_name} "
        f"not found for {cls.proxy_lookup_field} value {lookup_value}."
    )

get_proxy_subclasses() classmethod #

Get all proxy subclasses recursively.

Source code in src/apps/common/models.py
@classmethod
def get_proxy_subclasses(cls) -> Iterator[models.Model]:
    """Get all proxy subclasses recursively."""
    for subclass in cls.__subclasses__():
        if subclass._meta.proxy:
            yield from subclass.get_proxy_subclasses()
            yield subclass

save(*args, **kwargs) #

Raise error if proxy_lookup_field value does not match current model.

Source code in src/apps/common/models.py
def save(self, *args, **kwargs):
    """Raise error if proxy_lookup_field value does not match current model."""
    proxy = self.get_proxy_model(getattr(self, self.proxy_lookup_field))
    is_correct_proxy_model = self.__class__ == proxy
    if not is_correct_proxy_model:
        raise ValueError(
            f"Wrong type {self.__class__.__name__} "
            f"for {self.proxy_lookup_field} value '{self.storage_service}', "
            f"expected {proxy.__name__}."
        )
    return super().save(*args, **kwargs)

MediaTypeValidator#

Bases: RegexValidator

Validator for media types (formerly MIME types).

Source code in src/apps/common/models.py
class MediaTypeValidator(RegexValidator):
    """Validator for media types (formerly MIME types)."""

    def __init__(self, **kwargs):
        super().__init__(regex=mediatype_regex, **kwargs)