feat: auto-populate slug fields on save
Add clubmanager.base.unique_slugify(instance, value, scope=...): slugify a source value, truncate to the field's max_length, and append -2/-3/... to stay unique within a scope. ClubScopedModel gains a slug_source hook that fills a blank slug (unique per club) on save. Wire it up so every SlugField auto-populates from its natural source when left blank (explicit values are always kept): - shop.Product.slug <- name (per club) - formbuilder.Form.slug <- title (per club) - formbuilder.Field.key <- label (per form) Club.slug already auto-populated; refactor it onto the shared helper. Also fix shop.Product.slug's multi-tenancy bug: it was globally unique (unique=True); make it unique per club like the others. Migrations added. Full suite at 100% coverage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,15 +2,17 @@ from django.db import models
|
||||
from django.db.models import UniqueConstraint
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from clubmanager.base import ClubScopedModel, UUIDModel
|
||||
from clubmanager.base import ClubScopedModel, UUIDModel, unique_slugify
|
||||
from members.models import Member
|
||||
|
||||
|
||||
class Form(ClubScopedModel):
|
||||
title = models.CharField(_("title"), max_length=255)
|
||||
slug = models.SlugField(_("slug"))
|
||||
slug = models.SlugField(_("slug"), blank=True)
|
||||
description = models.TextField(_("description"), blank=True)
|
||||
|
||||
slug_source = "title"
|
||||
|
||||
is_active = models.BooleanField(_("is active?"), default=True)
|
||||
login_required = models.BooleanField(_("login required?"), default=False)
|
||||
|
||||
@@ -43,7 +45,7 @@ class Field(UUIDModel):
|
||||
FILE = "file", _("file")
|
||||
|
||||
form = models.ForeignKey(Form, on_delete=models.CASCADE, related_name="fields", verbose_name=_("form"))
|
||||
key = models.SlugField(_("key"))
|
||||
key = models.SlugField(_("key"), blank=True)
|
||||
label = models.CharField(_("label"), max_length=255)
|
||||
field_type = models.CharField(_("field type"), max_length=255, choices=FieldType.choices, default=FieldType.TEXT)
|
||||
required = models.BooleanField(_("required?"), default=True)
|
||||
@@ -60,6 +62,11 @@ class Field(UUIDModel):
|
||||
UniqueConstraint(fields=["form", "key"], name="unique_field_key_per_form"),
|
||||
]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.key:
|
||||
self.key = unique_slugify(self, self.label, slug_field="key", scope={"form": self.form})
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
|
||||
Reference in New Issue
Block a user