resources.py•4.23 kB
from __future__ import annotations
import inspect
import os
import sys
from pathlib import Path
from typing import Any
from typing import Literal
import django
from django.apps import AppConfig
from django.apps import apps
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
from pydantic import BaseModel
from pydantic import field_serializer
def get_source_file_path(obj: Any) -> Path:
target = obj if inspect.isclass(obj) else obj.__class__
try:
return Path(inspect.getfile(target))
except (TypeError, OSError):
return Path("unknown")
class ProjectResource(BaseModel):
python: PythonResource
django: DjangoResource
@classmethod
def from_env(cls) -> ProjectResource:
py = PythonResource.from_sys()
dj = DjangoResource.from_django()
return ProjectResource(python=py, django=dj)
class PythonResource(BaseModel):
base_prefix: Path
executable: Path
path: list[Path]
platform: str
prefix: Path
version_info: tuple[
int, int, int, Literal["alpha", "beta", "candidate", "final"], int
]
@classmethod
def from_sys(cls) -> PythonResource:
return cls(
base_prefix=Path(sys.base_prefix),
executable=Path(sys.executable),
path=[Path(p) for p in sys.path],
platform=sys.platform,
prefix=Path(sys.prefix),
version_info=sys.version_info,
)
class DjangoResource(BaseModel):
apps: list[str]
auth_user_model: str | None
base_dir: Path
databases: dict[str, dict[str, str]]
debug: bool
settings_module: str
version: tuple[int, int, int, Literal["alpha", "beta", "rc", "final"], int]
@classmethod
def from_django(cls) -> DjangoResource:
app_names = [app_config.name for app_config in apps.get_app_configs()]
databases = {
db_alias: {
"engine": db_config.get("ENGINE", ""),
"name": str(db_config.get("NAME", "")),
}
for db_alias, db_config in settings.DATABASES.items()
}
if "django.contrib.auth" in app_names:
user_model = get_user_model()
auth_user_model = f"{user_model.__module__}.{user_model.__name__}"
else:
auth_user_model = None
return cls(
apps=app_names,
auth_user_model=auth_user_model,
base_dir=Path(getattr(settings, "BASE_DIR", Path.cwd())),
databases=databases,
debug=settings.DEBUG,
settings_module=os.environ.get("DJANGO_SETTINGS_MODULE", ""),
version=django.VERSION,
)
class AppResource(BaseModel):
name: str
label: str
path: Path
models: list[ModelResource]
@classmethod
def from_app(cls, app: AppConfig) -> AppResource:
appconfig = get_source_file_path(app)
app_path = appconfig.parent if appconfig != Path("unknown") else Path("unknown")
app_models = (
[
ModelResource.from_model(model)
for model in app.models.values()
if not model._meta.auto_created
]
if app.models
else []
)
return cls(name=app.name, label=app.label, path=app_path, models=app_models)
@field_serializer("models")
def serialize_models(self, models: list[ModelResource]) -> list[str]:
return [model.model_dump()["model_class"] for model in models]
class ModelResource(BaseModel):
model_class: type[models.Model]
import_path: str
source_path: Path
fields: dict[str, str]
@classmethod
def from_model(cls, model: type[models.Model]):
field_types = {
field.name: field.__class__.__name__ for field in model._meta.fields
}
return cls(
model_class=model,
import_path=f"{model.__module__}.{model.__name__}",
source_path=get_source_file_path(model),
fields=field_types,
)
@field_serializer("model_class")
def serialize_model_class(self, klass: type[models.Model]) -> str:
return klass.__name__