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