1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- from django.db import models
- from django.forms.widgets import Textarea
- from wagtail.fields import StreamField
- from wagtailcrx.widgets import ColorPickerWidget
- class CoderedStreamField(StreamField):
- """
- An exact copy of the Wagtail StreamField, modified to NOT PRESERVE HISTORY
- in Django migrations.
- Since our StreamFields are generally huge, and we also let sites override
- the blocks in our concrete models dynamically, this creates a slew of
- migration problems (most commonly: a client overrides CODERED_FRONTEND_*,
- which changes a string used in a concrete model, which triggers a migration
- back in wagtailcrx). Eliminiating the blocks from the deconstructed
- StreamField allows us to have dynamic streamfields without breaking
- migrations or having to refactor the core concepts of this package.
- Internally, we should ALWAYS use CoderedStreamField on CONCRETE models,
- meaning models which are part of our package and saved to the database. For
- ABSTRACT models - meaning they are made concrete in the client site - we
- should continue to use Wagtail StreamField to keep things Wagtail-ish by
- default. The client may then decide to use CoderedStreamField if they are
- annoyed by the big migrations.
- CAVEAT EMPTOR:
- Client sites built with CRX may use this in place of the Wagtail
- StreamField, in order to avoid huge migration files. However, note that it
- will not be possible to mine data out of a CoderedStreamField during a
- migration (e.g. RunPython).
- Inspired by:
- https://cynthiakiser.com/blog/2022/01/06/trimming-wagtail-migration-cruft.html
- """
- def __init__(self, *args, **kwargs):
- """
- Patch init to work around django reconstruct not sending empty args.
- """
- # If we did not get an arg, pass an empty list through to the parent.
- if not args:
- args = [[]]
- return super().__init__(*args, **kwargs)
- def deconstruct(self):
- """
- Override to ignore any blocks within the StreamField when
- decustructing into a migration.
- The output should look something like this, regardless of how many
- blocks are nested within the StreamField::
- ("body", wagtailcrx.fields.CoderedStreamField([]))
- """
- name, path, block_types, kwargs = super().deconstruct()
- block_types = []
- return name, path, block_types, kwargs
- class ColorField(models.CharField):
- """
- A CharField which uses the HTML5 color picker widget.
- """
- def __init__(self, *args, **kwargs):
- kwargs['max_length'] = 255
- super().__init__(*args, **kwargs)
- def formfield(self, **kwargs):
- kwargs['widget'] = ColorPickerWidget
- return super().formfield(**kwargs)
- class MonospaceField(models.TextField):
- """
- A TextField which renders as a large HTML textarea with monospace font.
- """
- def formfield(self, **kwargs):
- kwargs["widget"] = Textarea(attrs={
- "rows": 12,
- "class": "monospace",
- "spellcheck": "false",
- })
- return super().formfield(**kwargs)
|