Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions contentful/content_type_field_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,20 @@ class RichTextField(BasicField):
Coerces Rich Text fields and resolves includes for entries included.
"""

def _coerce_link(self, value, includes=None, errors=None, resources=None, default_locale='en-US', locale=None):
def _coerce_link(self, value, includes=None, errors=None, resources=None,
default_locale='en-US', locale=None, includes_index=None, error_ids=None):
if value['data']['target']['sys']['type'] != 'Link':
return value['data']['target']

if unresolvable(value['data']['target'], errors):
if unresolvable(value['data']['target'], errors, error_ids=error_ids):
return None

resource = resource_for_link(
value['data']['target'],
includes,
resources,
locale=locale if locale else '*'
locale=locale if locale else '*',
includes_index=includes_index
)

if isinstance(resource, FieldsResource): # Resource comes from instance cache
Expand All @@ -191,10 +193,13 @@ def _coerce_link(self, value, includes=None, errors=None, resources=None, defaul
includes_for_single=includes,
errors_for_single=errors,
reuse_entries=bool(resources),
resources=resources
resources=resources,
includes_index=includes_index,
error_ids=error_ids
).build()

def _coerce_block(self, value, includes=None, errors=None, resources=None, default_locale='en-US', locale=None):
def _coerce_block(self, value, includes=None, errors=None, resources=None,
default_locale='en-US', locale=None, includes_index=None, error_ids=None):
if not (isinstance(value, dict) and 'content' in value):
return value

Expand All @@ -212,7 +217,9 @@ def _coerce_block(self, value, includes=None, errors=None, resources=None, defau
errors=errors,
resources=resources,
default_locale=default_locale,
locale=locale
locale=locale,
includes_index=includes_index,
error_ids=error_ids
)
if link:
node['data']['target'] = link
Expand All @@ -225,7 +232,9 @@ def _coerce_block(self, value, includes=None, errors=None, resources=None, defau
errors=errors,
resources=resources,
default_locale=default_locale,
locale=locale
locale=locale,
includes_index=includes_index,
error_ids=error_ids
)

for node_index, coerced_node in coerced_nodes.items():
Expand All @@ -236,7 +245,8 @@ def _coerce_block(self, value, includes=None, errors=None, resources=None, defau

return value

def coerce(self, value, includes=None, errors=None, resources=None, default_locale='en-US', locale=None):
def coerce(self, value, includes=None, errors=None, resources=None,
default_locale='en-US', locale=None, includes_index=None, error_ids=None):
"""Coerces Rich Text properly."""

if includes is None:
Expand All @@ -250,5 +260,7 @@ def coerce(self, value, includes=None, errors=None, resources=None, default_loca
errors=errors,
resources=resources,
default_locale=default_locale,
locale=locale
locale=locale,
includes_index=includes_index,
error_ids=error_ids
)
46 changes: 32 additions & 14 deletions contentful/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,42 +21,49 @@ class Entry(FieldsResource):
API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/entries
"""

def _coerce(self, field_id, value, localized, includes, errors, resources=None):
def _coerce(self, field_id, value, localized, includes, errors, resources=None,
includes_index=None, error_ids=None):
if is_link(value):
if unresolvable(value, errors):
if unresolvable(value, errors, error_ids=error_ids):
return None
return self._build_nested_resource(
value,
localized,
includes,
errors,
resources=resources
resources=resources,
includes_index=includes_index,
error_ids=error_ids
)
elif is_link_array(value):
items = []
for link in value:
if unresolvable(link, errors):
if unresolvable(link, errors, error_ids=error_ids):
continue
items.append(
self._build_nested_resource(
link,
localized,
includes,
errors,
resources=resources
resources=resources,
includes_index=includes_index,
error_ids=error_ids
)
)

return items
elif is_resource_link(value):
if unresolvable(value, errors):
if unresolvable(value, errors, error_ids=error_ids):
return None
return self._build_nested_resource(
value,
localized,
includes,
errors,
resources=resources
resources=resources,
includes_index=includes_index,
error_ids=error_ids
)

content_type = ContentTypeCache.get(
Expand All @@ -71,7 +78,9 @@ def _coerce(self, field_id, value, localized, includes, errors, resources=None):
errors=errors,
resources=resources,
default_locale=self.default_locale,
locale=self.sys.get('locale', '*')
locale=self.sys.get('locale', '*'),
includes_index=includes_index,
error_ids=error_ids
)

return super(Entry, self)._coerce(
Expand All @@ -80,10 +89,13 @@ def _coerce(self, field_id, value, localized, includes, errors, resources=None):
localized,
includes,
errors,
resources
resources,
includes_index=includes_index,
error_ids=error_ids
)

def _build_nested_resource(self, value, localized, includes, errors, resources=None):
def _build_nested_resource(self, value, localized, includes, errors, resources=None,
includes_index=None, error_ids=None):
# Maximum include Depth is 10 in the API, but we raise it to 20 (default),
# in case one of the included items has a reference in an upper level,
# so we can keep the include chain for that object as well
Expand All @@ -95,7 +107,8 @@ def _build_nested_resource(self, value, localized, includes, errors, resources=N
value,
includes,
resources=resources,
locale=self.sys.get('locale', '*')
locale=self.sys.get('locale', '*'),
includes_index=includes_index
)

if isinstance(resource, FieldsResource): # Resource comes from instance cache
Expand All @@ -108,12 +121,15 @@ def _build_nested_resource(self, value, localized, includes, errors, resources=N
localized,
includes,
errors,
resources=resources
resources=resources,
includes_index=includes_index,
error_ids=error_ids
)

return self._build_link(value)

def _resolve_include(self, resource, localized, includes, errors, resources=None):
def _resolve_include(self, resource, localized, includes, errors, resources=None,
includes_index=None, error_ids=None):
from .resource_builder import ResourceBuilder
return ResourceBuilder(
self.default_locale,
Expand All @@ -124,7 +140,9 @@ def _resolve_include(self, resource, localized, includes, errors, resources=None
reuse_entries=bool(resources),
resources=resources,
depth=self._depth + 1,
max_depth=self._max_depth
max_depth=self._max_depth,
includes_index=includes_index,
error_ids=error_ids
).build()

def incoming_references(self, client=None, query=None):
Expand Down
66 changes: 54 additions & 12 deletions contentful/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ def __init__(
localized=False,
resources=None,
depth=0,
max_depth=20):
import copy
self.raw = copy.deepcopy(item)
max_depth=20,
includes_index=None,
error_ids=None):
# Lazy deep copy: store reference, copy only when raw is accessed
self._raw_source = item
self._raw_copy = None
self.default_locale = default_locale
self._depth = depth
self._max_depth = max_depth
Expand All @@ -51,6 +54,20 @@ def __init__(
)
resources[cache_key] = self

@property
def raw(self):
"""Lazily deep copy the raw item only when accessed."""
if self._raw_copy is None:
import copy
self._raw_copy = copy.deepcopy(self._raw_source)
return self._raw_copy

@raw.setter
def raw(self, value):
"""Allow setting raw directly for backwards compatibility."""
self._raw_copy = value
self._raw_source = value

def _hydrate_sys(self, item):
sys = {}
for k, v in item.get('sys', {}).items():
Expand Down Expand Up @@ -103,19 +120,31 @@ def __init__(
errors=None,
localized=False,
resources=None,
includes_index=None,
error_ids=None,
**kwargs):
super(FieldsResource, self).__init__(
item,
includes=includes,
errors=errors,
localized=localized,
resources=resources,
includes_index=includes_index,
error_ids=error_ids,
**kwargs
)

self._fields = self._hydrate_fields(item, localized, includes, errors, resources=resources)
self._includes_index = includes_index
self._error_ids = error_ids
self._fields = self._hydrate_fields(
item, localized, includes, errors,
resources=resources,
includes_index=includes_index,
error_ids=error_ids
)

def _hydrate_fields(self, item, localized, includes, errors, resources=None):
def _hydrate_fields(self, item, localized, includes, errors, resources=None,
includes_index=None, error_ids=None):
if 'fields' not in item:
return {}

Expand All @@ -128,12 +157,19 @@ def _hydrate_fields(self, item, localized, includes, errors, resources=None):
locale = self._locale()
fields = {locale: {}}
if localized:
self._hydrate_localized_entry(fields, item, includes, errors, resources)
self._hydrate_localized_entry(
fields, item, includes, errors, resources,
includes_index=includes_index, error_ids=error_ids
)
else:
self._hydrate_non_localized_entry(fields, item, includes, errors, resources)
self._hydrate_non_localized_entry(
fields, item, includes, errors, resources,
includes_index=includes_index, error_ids=error_ids
)
return fields

def _hydrate_localized_entry(self, fields, item, includes, errors, resources=None):
def _hydrate_localized_entry(self, fields, item, includes, errors, resources=None,
includes_index=None, error_ids=None):
for k, locales in item['fields'].items():
for locale, v in locales.items():
if locale not in fields:
Expand All @@ -144,21 +180,27 @@ def _hydrate_localized_entry(self, fields, item, includes, errors, resources=Non
True,
includes,
errors,
resources=resources
resources=resources,
includes_index=includes_index,
error_ids=error_ids
)

def _hydrate_non_localized_entry(self, fields, item, includes, errors, resources=None):
def _hydrate_non_localized_entry(self, fields, item, includes, errors, resources=None,
includes_index=None, error_ids=None):
for k, v in item['fields'].items():
fields[self._locale()][snake_case(k)] = self._coerce(
snake_case(k),
v,
False,
includes,
errors,
resources=resources
resources=resources,
includes_index=includes_index,
error_ids=error_ids
)

def _coerce(self, field_id, value, localized, includes, errors, resources=None):
def _coerce(self, field_id, value, localized, includes, errors, resources=None,
includes_index=None, error_ids=None):
return value

def fields(self, locale=None):
Expand Down
Loading