Files
RosterChief/formbuilder/admin.py
Bernard Siebens 5dd3715c1f feat(formbuilder): fix tenancy bugs, add admin + services
Fix multi-tenancy/integrity bugs in the models: Form.slug and Field.key
were globally unique (unique=True), so two clubs couldn't reuse a form
slug and two forms couldn't reuse a field key — make slug unique per club
(constraint already present) and key unique per form. Add a
(submission, field) uniqueness constraint on Answer.

Register all four models in the admin (Field inline on Form, Answer inline
on Submission) and add formbuilder to the admin registration smoke test.

Add a service layer:
- submit_form(form, member, data): enforces is_active / login_required /
  open window / max_submissions, validates required + choice fields, and
  writes a Submission with Answers atomically (FormSubmissionError carries
  per-field errors).
- build_form(form): a live django.forms.Form built from a Form's active
  fields, mapping each FieldType to the matching form field.
- form_report(form): a tabular overview of every submission's answers plus
  per-value tallies for choice-type fields.

Full suite at 100% coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 21:13:16 +02:00

51 lines
1.5 KiB
Python

from django.contrib import admin
from .models import Answer, Field, Form, Submission
class FieldInline(admin.TabularInline):
model = Field
extra = 0
fields = ["order", "key", "label", "field_type", "required", "is_active"]
ordering = ["order"]
@admin.register(Form)
class FormAdmin(admin.ModelAdmin):
list_display = ["title", "slug", "club", "is_active", "login_required", "opens_at", "closes_at"]
list_filter = ["club", "is_active", "login_required"]
search_fields = ["title", "slug"]
prepopulated_fields = {"slug": ["title"]}
inlines = [FieldInline]
@admin.register(Field)
class FieldAdmin(admin.ModelAdmin):
list_display = ["label", "key", "form", "field_type", "order", "required", "is_active"]
list_filter = ["field_type", "required", "is_active"]
search_fields = ["label", "key", "form__title"]
raw_id_fields = ["form"]
class AnswerInline(admin.TabularInline):
model = Answer
extra = 0
raw_id_fields = ["field"]
@admin.register(Submission)
class SubmissionAdmin(admin.ModelAdmin):
list_display = ["form", "member", "submitted_at"]
list_filter = ["form"]
search_fields = ["form__title", "member__first_name", "member__last_name"]
raw_id_fields = ["form", "member"]
readonly_fields = ["submitted_at"]
inlines = [AnswerInline]
@admin.register(Answer)
class AnswerAdmin(admin.ModelAdmin):
list_display = ["submission", "field", "value"]
search_fields = ["field__label", "field__key"]
raw_id_fields = ["submission", "field"]