kopia lustrzana https://github.com/wagtail/wagtail
Add a mechanism for preserving the raw text value of StreamFields when they fail to parse as JSON
rodzic
6479eb80ef
commit
31f45a8a63
|
@ -260,7 +260,7 @@ class StreamValue(collections.Sequence):
|
|||
"""
|
||||
return self.block.name
|
||||
|
||||
def __init__(self, stream_block, stream_data, is_lazy=False):
|
||||
def __init__(self, stream_block, stream_data, is_lazy=False, raw_text=None):
|
||||
"""
|
||||
Construct a StreamValue linked to the given StreamBlock,
|
||||
with child values given in stream_data.
|
||||
|
@ -273,11 +273,18 @@ class StreamValue(collections.Sequence):
|
|||
Passing is_lazy=False means that stream_data consists of immediately usable
|
||||
native values. In this mode, stream_data is a list of (type_name, value)
|
||||
tuples.
|
||||
|
||||
raw_text exists solely as a way of representing StreamField content that is
|
||||
not valid JSON; this may legitimately occur if an existing text field is
|
||||
migrated to a StreamField. In this situation we return a blank StreamValue
|
||||
with the raw text accessible under the `raw_text` attribute, so that migration
|
||||
code can be rewritten to convert it as desired.
|
||||
"""
|
||||
self.is_lazy = is_lazy
|
||||
self.stream_block = stream_block # the StreamBlock object that handles this value
|
||||
self.stream_data = stream_data # a list of (type_name, value) tuples
|
||||
self._bound_blocks = {} # populated lazily from stream_data as we access items through __getitem__
|
||||
self.raw_text = raw_text
|
||||
|
||||
def __getitem__(self, i):
|
||||
if i not in self._bound_blocks:
|
||||
|
|
|
@ -5,7 +5,7 @@ import json
|
|||
from django.db import models
|
||||
from django import forms
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.utils.six import with_metaclass
|
||||
from django.utils.six import with_metaclass, string_types
|
||||
|
||||
from wagtail.wagtailcore.rich_text import DbWhitelister, expand_db_html
|
||||
from wagtail.utils.widgets import WidgetWithScript
|
||||
|
@ -69,17 +69,16 @@ class StreamField(with_metaclass(models.SubfieldBase, models.Field)):
|
|||
return StreamValue(self.stream_block, [])
|
||||
elif isinstance(value, StreamValue):
|
||||
return value
|
||||
else: # assume string
|
||||
elif isinstance(value, string_types):
|
||||
try:
|
||||
unpacked_value = json.loads(value)
|
||||
except ValueError:
|
||||
# value is not valid JSON; most likely, this field was previously a
|
||||
# rich text field before being migrated to StreamField, and the data
|
||||
# was left intact in the migration. Return an empty stream instead.
|
||||
|
||||
# TODO: keep this raw text data around as a property of the StreamValue
|
||||
# so that it can be retrieved in data migrations
|
||||
return StreamValue(self.stream_block, [])
|
||||
# was left intact in the migration. Return an empty stream instead
|
||||
# (but keep the raw text available as an attribute, so that it can be
|
||||
# used to migrate that data to StreamField)
|
||||
return StreamValue(self.stream_block, [], raw_text=value)
|
||||
|
||||
if unpacked_value is None:
|
||||
# we get here if value is the literal string 'null'. This should probably
|
||||
|
@ -88,9 +87,27 @@ class StreamField(with_metaclass(models.SubfieldBase, models.Field)):
|
|||
return StreamValue(self.stream_block, [])
|
||||
|
||||
return self.stream_block.to_python(unpacked_value)
|
||||
else:
|
||||
# See if it looks like the standard non-smart representation of a
|
||||
# StreamField value: a list of (block_name, value) tuples
|
||||
try:
|
||||
[None for (x, y) in value]
|
||||
except (TypeError, ValueError):
|
||||
# Give up trying to make sense of the value
|
||||
raise TypeError("Cannot handle %r (type %r) as a value of StreamField" % (value, type(value)))
|
||||
|
||||
# Test succeeded, so return as a StreamValue-ified version of that value
|
||||
return StreamValue(self.stream_block, value)
|
||||
|
||||
def get_prep_value(self, value):
|
||||
return json.dumps(self.stream_block.get_prep_value(value), cls=DjangoJSONEncoder)
|
||||
if isinstance(value, StreamValue) and not(value) and value.raw_text is not None:
|
||||
# An empty StreamValue with a nonempty raw_text attribute should have that
|
||||
# raw_text attribute written back to the db. (This is probably only useful
|
||||
# for reverse migrations that convert StreamField data back into plain text
|
||||
# fields.)
|
||||
return value.raw_text
|
||||
else:
|
||||
return json.dumps(self.stream_block.get_prep_value(value), cls=DjangoJSONEncoder)
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
"""
|
||||
|
|
Ładowanie…
Reference in New Issue