Add fields dynamicly to form and get_or_create in on step

This commit is contained in:
Kevin Muller 2025-03-04 12:16:56 +01:00
parent 4273c7a608
commit 99f1d78e29
6 changed files with 177 additions and 86 deletions

View file

@ -1 +1,5 @@
from django.contrib import admin
from panel.models import Configuration
admin.site.register(Configuration)

View file

@ -1,44 +1,56 @@
from django import forms
from panel.models import (
Configuration,
PeertubeConfig,
PixelfedConfig,
MastodonConfig,
)
from django.db import models
from panel.models import Configuration
class Deployment(forms.ModelForm):
"""Dynamically includes all related OneToOne fields from Configuration."""
class Meta:
model = Configuration
fields = [
'domain',
]
fields = ['domain'] # Only the base model fields (related fields added dynamically)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
config = self.instance # The Configuration instance
operator = self.instance.operator.username
self.fields['domain'].choices = [
(code, f"{operator}.{label}")
for code, label in self.instance._meta.get_field('domain').choices
]
model = Configuration
fields = ['domain']
# Dynamically add related model fields
for field in config._meta.get_fields():
if isinstance(field, models.OneToOneField): # Check for OneToOne fields
related_instance = getattr(config, field.name, None)
# Auto-create related instance if missing
if related_instance is None:
related_instance = field.related_model.objects.create()
setattr(config, field.name, related_instance)
config.save()
class MastodonConfigForm(forms.ModelForm):
class Meta:
model = MastodonConfig
fields = ['enable']
# Add all BooleanFields from the related model
for related_field in field.related_model._meta.get_fields():
if isinstance(related_field, models.BooleanField):
field_name = f"{field.name}_{related_field.name}" # e.g., "mastodon_enable"
self.fields[field_name] = forms.BooleanField(
required=False,
initial=getattr(related_instance, related_field.name),
label=f"{field.name.capitalize()} {related_field.name.capitalize()}"
)
def save(self, commit=True):
"""Saves the Configuration model and all related models dynamically."""
config = super().save(commit=False) # Save Configuration model but don't commit yet
class PixelfedConfigForm(forms.ModelForm):
class Meta:
model = PixelfedConfig
fields = ['enable']
# Save related model fields dynamically
for field in config._meta.get_fields():
if isinstance(field, models.OneToOneField): # Only process related models
related_instance = getattr(config, field.name)
for related_field in field.related_model._meta.get_fields():
if isinstance(related_field, models.BooleanField): # Only BooleanFields
field_name = f"{field.name}_{related_field.name}"
setattr(related_instance, related_field.name, self.cleaned_data[field_name])
class PeertubeConfigForm(forms.ModelForm):
class Meta:
model = PeertubeConfig
fields = ['enable']
related_instance.save() # Save related model
if commit:
config.save()
return config

View file

@ -1,4 +1,4 @@
# Generated by Django 4.2.16 on 2025-03-04 07:55
# Generated by Django 4.2.16 on 2025-03-04 10:59
from django.conf import settings
from django.db import migrations, models
@ -15,36 +15,115 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name='MastodonConfig',
name="MastodonConfig",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enable', models.BooleanField(default=False)),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("enable", models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='PeertubeConfig',
name="PeertubeConfig",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enable', models.BooleanField(default=False)),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("enable", models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='PixelfedConfig',
name="PixelfedConfig",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enable', models.BooleanField(default=False)),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("enable", models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='Configuration',
name="Configuration",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enable', models.BooleanField(default=False, help_text='Enable the configuration')),
('domain', models.CharField(choices=[('fediversity_eu', 'fediversity.eu'), ('fediversity_net', 'fediversity.net')], max_length=255)),
('mastodon', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='config', to='panel.mastodonconfig')),
('operator', models.ForeignKey(help_text='Operator who owns the configuration', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='configurations', to=settings.AUTH_USER_MODEL)),
('peertube', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='config', to='panel.peertubeconfig')),
('pixelfed', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='config', to='panel.pixelfedconfig')),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"enable",
models.BooleanField(
default=False, help_text="Enable the configuration"
),
),
(
"domain",
models.CharField(
choices=[
("fediversity_eu", "fediversity.eu"),
("fediversity_net", "fediversity.net"),
],
max_length=255,
),
),
(
"mastodon",
models.OneToOneField(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="config",
to="panel.mastodonconfig",
),
),
(
"operator",
models.ForeignKey(
help_text="Operator who owns the configuration",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="configurations",
to=settings.AUTH_USER_MODEL,
),
),
(
"peertube",
models.OneToOneField(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="config",
to="panel.peertubeconfig",
),
),
(
"pixelfed",
models.OneToOneField(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="config",
to="panel.pixelfedconfig",
),
),
],
),
]

View file

@ -24,6 +24,38 @@ class PeertubeConfig(models.Model):
class Configuration(models.Model):
class ConfigurationManager(models.Manager):
# Define which related fields should be auto-created
auto_create_related = [
'mastodon',
'pixelfed',
'peertube',
]
def create(self, **kwargs):
# Create the model instance but don't save yet
instance = self.model(**kwargs)
# Auto-create any missing related objects
for field_name in self.auto_create_related:
# Skip if the field was provided
if field_name in kwargs:
continue
# Get the related model class
field = self.model._meta.get_field(field_name)
related_model = field.related_model
# Create the related object
related_obj = related_model.objects.create()
# Set the relation
setattr(instance, field_name, related_obj)
# Save and return the instance
instance.save()
return instance
operator = models.ForeignKey(
User,
on_delete=models.SET_NULL,
@ -48,3 +80,4 @@ class Configuration(models.Model):
mastodon = models.OneToOneField(MastodonConfig, on_delete=models.CASCADE, related_name='config', null=True)
pixelfed = models.OneToOneField(PixelfedConfig, on_delete=models.CASCADE, related_name='config', null=True)
peertube = models.OneToOneField(PeertubeConfig, on_delete=models.CASCADE, related_name='config', null=True)

View file

@ -4,9 +4,6 @@
{% csrf_token %}
{{ form.as_p }}
<h2>Services</h2>
{{ children.as_p }}
<button class="button" disabled>Deploy</button>
<button class="button" type="submit" >Save</button>
</form>

View file

@ -9,15 +9,9 @@ from panel.models import Configuration
from panel import forms
from .models import (
Configuration,
MastodonConfig,
PixelfedConfig,
PeertubeConfig,
)
from .forms import (
Deployment,
MastodonConfigForm,
PixelfedConfigForm,
PeertubeConfigForm,
)
@ -44,33 +38,5 @@ class ConfigurationForm(LoginRequiredMixin, UpdateView):
success_url = reverse_lazy('configuration_form')
def get_object(self, queryset=None):
obj, created = Configuration.objects.get_or_create(
operator=self.request.user)
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
deploy_form = self.get_object()
context.update({
'mastodon_form': MastodonConfigForm(self.request.POST or None, instance=deploy_form.mastodon),
'pixelfed_form': PixelfedConfigForm(self.request.POST or None, instance=deploy_form.pixelfed),
'peertube_form': PeertubeConfigForm(self.request.POST or None, instance=deploy_form.peertube),
})
return context
def form_valid(self, form):
response = super().form_valid(form) # Save main Configuration form
obj = self.get_object() # Get instance
# Save related forms
MastodonConfigForm(self.request.POST,
instance=obj.mastodon).save()
PixelfedConfigForm(self.request.POST,
instance=obj.pixelfed).save()
PeertubeConfigForm(self.request.POST,
instance=obj.peertube).save()
return response
obj, created = Configuration.objects.get_or_create(operator=self.request.user)
return obj