kopia lustrzana https://github.com/wagtail/wagtail
Allow StreamBlock to pick up blank, min_num, max_num and block_counts kwargs from StreamField
rodzic
f03c11f1d1
commit
643bbfc600
|
@ -47,6 +47,12 @@ class Block(metaclass=BaseBlock):
|
|||
classname = None
|
||||
group = ''
|
||||
|
||||
# Attributes of Meta which can legally be modified after the block has been instantiated.
|
||||
# Used to implement __eq__. label is not included here, despite it technically being mutable via
|
||||
# set_name, since its value must originate from either the constructor arguments or set_name,
|
||||
# both of which are captured by the equality test, so checking label as well would be redundant.
|
||||
MUTABLE_META_ATTRIBUTES = []
|
||||
|
||||
"""
|
||||
Setting a 'dependencies' list serves as a shortcut for the common case where a complex block type
|
||||
(such as struct, list or stream) relies on one or more inner block objects, and needs to ensure that
|
||||
|
@ -121,6 +127,16 @@ class Block(metaclass=BaseBlock):
|
|||
if not self.meta.label:
|
||||
self.label = capfirst(force_str(name).replace('_', ' '))
|
||||
|
||||
def set_meta_options(self, options):
|
||||
"""
|
||||
Called when this block is used as the top-level block of a StreamField, to pass on any options
|
||||
from the StreamField constructor that ought to be handled by the block, e.g.
|
||||
body = StreamField(SomeStreamBlock(), max_num=5)
|
||||
"""
|
||||
# Ignore all options here; block types that are allowed at the top level (i.e. currently just
|
||||
# StreamBlock) and recognise these options will override this method
|
||||
pass
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
return forms.Media()
|
||||
|
@ -395,9 +411,9 @@ class Block(metaclass=BaseBlock):
|
|||
def __eq__(self, other):
|
||||
"""
|
||||
Implement equality on block objects so that two blocks with matching definitions are considered
|
||||
equal. (Block objects are intended to be immutable with the exception of set_name(), so here
|
||||
'matching definitions' means that both the 'name' property and the constructor args/kwargs - as
|
||||
captured in _constructor_args - are equal on both blocks.)
|
||||
equal. Block objects are intended to be immutable with the exception of set_name() and any meta
|
||||
attributes identified in MUTABLE_META_ATTRIBUTES, so checking these along with the result of
|
||||
deconstruct (which captures the constructor arguments) is sufficient to identify (valid) differences.
|
||||
|
||||
This was originally necessary as a workaround for https://code.djangoproject.com/ticket/24340
|
||||
in Django <1.9; the deep_deconstruct function used to detect changes for migrations did not
|
||||
|
@ -439,7 +455,14 @@ class Block(metaclass=BaseBlock):
|
|||
# the migration, rather than leaving the migration vulnerable to future changes to FooBlock / BarBlock
|
||||
# in models.py.
|
||||
|
||||
return (self.name == other.name) and (self.deconstruct() == other.deconstruct())
|
||||
return (
|
||||
self.name == other.name
|
||||
and self.deconstruct() == other.deconstruct()
|
||||
and all(
|
||||
getattr(self.meta, attr, None) == getattr(other.meta, attr, None)
|
||||
for attr in self.MUTABLE_META_ATTRIBUTES
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class BoundBlock:
|
||||
|
|
|
@ -51,6 +51,11 @@ class BaseStreamBlock(Block):
|
|||
|
||||
self.dependencies = self.child_blocks.values()
|
||||
|
||||
def set_meta_options(self, opts):
|
||||
for attr in ['required', 'min_num', 'max_num', 'block_counts']:
|
||||
if attr in opts:
|
||||
setattr(self.meta, attr, opts[attr])
|
||||
|
||||
def get_default(self):
|
||||
"""
|
||||
Default values set on a StreamBlock should be a list of (type_name, value) tuples -
|
||||
|
@ -381,6 +386,8 @@ class BaseStreamBlock(Block):
|
|||
max_num = None
|
||||
block_counts = {}
|
||||
|
||||
MUTABLE_META_ATTRIBUTES = ['required', 'min_num', 'max_num', 'block_counts']
|
||||
|
||||
|
||||
class StreamBlock(BaseStreamBlock, metaclass=DeclarativeSubBlocksMetaclass):
|
||||
pass
|
||||
|
|
|
@ -51,13 +51,31 @@ class Creator:
|
|||
|
||||
class StreamField(models.Field):
|
||||
def __init__(self, block_types, **kwargs):
|
||||
|
||||
# extract kwargs that are to be passed on to the block, not handled by super
|
||||
block_opts = {}
|
||||
for arg in ['min_num', 'max_num', 'block_counts']:
|
||||
if arg in kwargs:
|
||||
block_opts[arg] = kwargs.pop(arg)
|
||||
|
||||
# for a top-level block, the 'blank' kwarg (defaulting to False) always overrides the
|
||||
# block's own 'required' meta attribute, even if not passed explicitly; this ensures
|
||||
# that the field and block have consistent definitions
|
||||
block_opts['required'] = not kwargs.get('blank', False)
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
if isinstance(block_types, Block):
|
||||
# use the passed block as the top-level block
|
||||
self.stream_block = block_types
|
||||
elif isinstance(block_types, type):
|
||||
self.stream_block = block_types(required=not self.blank)
|
||||
# block passed as a class - instantiate it
|
||||
self.stream_block = block_types()
|
||||
else:
|
||||
self.stream_block = StreamBlock(block_types, required=not self.blank)
|
||||
# construct a top-level StreamBlock from the list of block types
|
||||
self.stream_block = StreamBlock(block_types)
|
||||
|
||||
self.stream_block.set_meta_options(block_opts)
|
||||
|
||||
def get_internal_type(self):
|
||||
return 'TextField'
|
||||
|
|
Ładowanie…
Reference in New Issue