rendering.py 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import random
  2. import string
  3. from functools import cached_property
  4. __all__ = (
  5. 'FieldSet',
  6. 'InlineFields',
  7. 'ObjectAttribute',
  8. 'TabbedGroups',
  9. )
  10. class FieldSet:
  11. """
  12. A generic grouping of fields, with an optional name. Each field will be rendered
  13. on its own row under the heading (name).
  14. """
  15. def __init__(self, *fields, name=None):
  16. self.fields = fields
  17. self.name = name
  18. class InlineFields:
  19. """
  20. A set of fields rendered inline (side-by-side) with a shared label; typically nested within a FieldSet.
  21. """
  22. def __init__(self, *fields, label=None):
  23. self.fields = fields
  24. self.label = label
  25. class TabbedGroups:
  26. """
  27. Two or more groups of fields (FieldSets) arranged under tabs among which the user can navigate.
  28. """
  29. def __init__(self, *fieldsets):
  30. for fs in fieldsets:
  31. if not fs.name:
  32. raise ValueError(f"Grouped fieldset {fs} must have a name.")
  33. self.groups = fieldsets
  34. # Initialize a random ID for the group (for tab selection)
  35. self.id = ''.join(
  36. random.choice(string.ascii_lowercase + string.digits) for _ in range(8)
  37. )
  38. @cached_property
  39. def tabs(self):
  40. return [
  41. {
  42. 'id': f'{self.id}_{i}',
  43. 'title': group.name,
  44. 'fields': group.fields,
  45. } for i, group in enumerate(self.groups, start=1)
  46. ]
  47. class ObjectAttribute:
  48. """
  49. Renders the value for a specific attribute on the form's instance.
  50. """
  51. def __init__(self, name):
  52. self.name = name