Source code for apis_core.relations.models

import functools

from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models.base import ModelBase
from model_utils.managers import InheritanceManager

from apis_core.generic.abc import GenericModel


# This ModelBase is simply there to check if the needed attributes
# are set in the Relation child classes.
[docs] class RelationModelBase(ModelBase): def __new__(metacls, name, bases, attrs): if name == "Relation": return super().__new__(metacls, name, bases, attrs) else: new_class = super().__new__(metacls, name, bases, attrs) if not hasattr(new_class, "subj_model"): raise ValueError( "%s inherits from Relation and must therefore specify subj_model" % name ) if not hasattr(new_class, "obj_model"): raise ValueError( "%s inherits from Relation and must therefore specify obj_model" % name ) return new_class
[docs] @functools.cache def get_by_natural_key(natural_key: str): app_label, name = natural_key.lower().split(".") return ContentType.objects.get_by_natural_key(app_label, name).model_class()
[docs] class Relation(models.Model, GenericModel, metaclass=RelationModelBase): subj_content_type = models.ForeignKey( ContentType, on_delete=models.CASCADE, related_name="relation_subj_set" ) subj_object_id = models.PositiveIntegerField() subj = GenericForeignKey("subj_content_type", "subj_object_id") obj_content_type = models.ForeignKey( ContentType, on_delete=models.CASCADE, related_name="relation_obj_set" ) obj_object_id = models.PositiveIntegerField() obj = GenericForeignKey("obj_content_type", "obj_object_id") objects = InheritanceManager()
[docs] def save(self, *args, **kwargs): if self.subj_content_type: if self.subj_content_type.model_class() not in self.subj_list(): raise ValidationError( f"{self.subj} is not of any type in {self.subj_list()}" ) if self.obj_content_type: if self.obj_content_type.model_class() not in self.obj_list(): raise ValidationError( f"{self.obj} is not of any type in {self.obj_list()}" ) super().save(*args, **kwargs)
@property def subj_to_obj_text(self) -> str: if hasattr(self, "name"): return f"{self.subj} {self.name()} {self.obj}" return f"{self.subj} relation to {self.obj}" @property def obj_to_subj_text(self) -> str: if hasattr(self, "reverse_name"): return f"{self.obj} {self.reverse_name()} {self.subj}" return f"{self.obj} relation to {self.subj}" def __str__(self): return self.subj_to_obj_text @classmethod def _get_models(cls, model): models = model if isinstance(model, list) else [model] return [ get_by_natural_key(model) if isinstance(model, str) else model for model in models ]
[docs] @classmethod def subj_list(cls) -> list[models.Model]: return cls._get_models(cls.subj_model)
[docs] @classmethod def obj_list(cls) -> list[models.Model]: return cls._get_models(cls.obj_model)
[docs] @classmethod def name(cls) -> str: return cls._meta.verbose_name
[docs] @classmethod def reverse_name(cls) -> str: return cls._meta.verbose_name + " reverse"