Build dynamic model and modelform generation
This commit is contained in:
parent
af64d5468f
commit
234ff8e1d6
1 changed files with 92 additions and 0 deletions
92
src/servala/core/crd.py
Normal file
92
src/servala/core/crd.py
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator
|
||||||
|
from django.db import models
|
||||||
|
from django.forms.models import ModelForm, ModelFormMetaclass
|
||||||
|
|
||||||
|
|
||||||
|
def generate_django_model(crd, group, version, kind):
|
||||||
|
"""
|
||||||
|
Generates a virtual Django model from a Kubernetes CRD's OpenAPI v3 schema.
|
||||||
|
"""
|
||||||
|
schema = crd.spec.versions[0].schema.open_apiv3_schema
|
||||||
|
properties = schema.properties
|
||||||
|
required_fields = schema.required
|
||||||
|
model_fields = {"__module__": "crd_models"}
|
||||||
|
|
||||||
|
defaults = {"apiVersion": f"{group}/{version}", "kind": kind}
|
||||||
|
|
||||||
|
for prop_name, prop_schema in properties.items():
|
||||||
|
is_required = prop_name in required_fields
|
||||||
|
field = get_django_field(
|
||||||
|
prop_schema, is_required, default=defaults.get(prop_name)
|
||||||
|
)
|
||||||
|
model_fields[prop_name] = field
|
||||||
|
|
||||||
|
meta_class = type("Meta", (), {"app_label": "crd_models"})
|
||||||
|
model_fields["Meta"] = meta_class
|
||||||
|
|
||||||
|
# create the model class
|
||||||
|
model_name = crd.spec.names.kind
|
||||||
|
model_class = type(model_name, (models.Model,), model_fields)
|
||||||
|
return model_class
|
||||||
|
|
||||||
|
|
||||||
|
def get_django_field(prop_schema, is_required=False, default=None):
|
||||||
|
field_type = prop_schema.type or "string"
|
||||||
|
format = prop_schema.format
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
"blank": not is_required,
|
||||||
|
"null": not is_required,
|
||||||
|
"help_text": prop_schema.description or "",
|
||||||
|
"validators": [],
|
||||||
|
"default": default,
|
||||||
|
# TODO: verbose_name?
|
||||||
|
}
|
||||||
|
|
||||||
|
if prop_schema.minimum:
|
||||||
|
kwargs["validators"].append(MinValueValidator(prop_schema.minimum))
|
||||||
|
if prop_schema.maximum:
|
||||||
|
kwargs["validators"].append(MaxValueValidator(prop_schema.maximum))
|
||||||
|
|
||||||
|
if field_type == "string":
|
||||||
|
if format == "date-time":
|
||||||
|
return models.DateTimeField(**kwargs)
|
||||||
|
elif format == "date":
|
||||||
|
return models.DateField(**kwargs)
|
||||||
|
else:
|
||||||
|
max_length = prop_schema.max_length or 255
|
||||||
|
if prop_schema.pattern:
|
||||||
|
kwargs["validators"].append(RegexValidator(regex=prop_schema.pattern))
|
||||||
|
return models.CharField(max_length=max_length, **kwargs)
|
||||||
|
elif field_type == "integer":
|
||||||
|
return models.IntegerField(**kwargs)
|
||||||
|
elif field_type == "number":
|
||||||
|
return models.FloatField(**kwargs)
|
||||||
|
elif field_type == "boolean":
|
||||||
|
return models.BooleanField(**kwargs)
|
||||||
|
elif field_type == "object":
|
||||||
|
kwargs["help_text"] += " (JSON object)"
|
||||||
|
return models.JSONField(**kwargs)
|
||||||
|
elif field_type == "array":
|
||||||
|
kwargs["help_text"] += " (JSON array)"
|
||||||
|
return models.JSONField(**kwargs)
|
||||||
|
return models.CharField(max_length=255, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CrdModelFormMixin:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields["apiVersion"].disabled = True
|
||||||
|
|
||||||
|
|
||||||
|
def generate_model_form_class(model):
|
||||||
|
meta_attrs = {
|
||||||
|
"model": model,
|
||||||
|
"fields": "__all__",
|
||||||
|
}
|
||||||
|
fields = {
|
||||||
|
"Meta": type("Meta", (object,), meta_attrs),
|
||||||
|
"__module__": "crd_models",
|
||||||
|
}
|
||||||
|
class_name = f"{model.__name__}ModelForm"
|
||||||
|
return ModelFormMetaclass(class_name, (CrdModelFormMixin, ModelForm), fields)
|
Loading…
Add table
Add a link
Reference in a new issue