GithubHelp home page GithubHelp logo

contentful / contentful.py Goto Github PK

View Code? Open in Web Editor NEW
45.0 41.0 41.0 847 KB

Python client for the Contentful Content Delivery API https://www.contentful.com/developers/documentation/content-delivery-api/

License: MIT License

Makefile 4.40% Python 92.12% Batchfile 3.48%

contentful.py's Introduction

image

Join Contentful Community Slack   Join Contentful Community Forum

contentful.py - Contentful Python Delivery Library

This repository is actively maintained   MIT License   Build Status

What is Contentful?

Contentful provides a content infrastructure for digital teams to power content in websites, apps, and devices. Unlike a CMS, Contentful was built to integrate with the modern software stack. It offers a central hub for structured content, powerful management and delivery APIs, and a customizable web app that enable developers and content creators to ship digital products faster.

This library is intended to replace the former unofficial Python CDA library. The old library can still be found at: https://github.com/contentful-labs/contentful.py

Core Features

Getting Started

In order to get started with the Contentful Python library you'll need not only to install it, but also to get credentials which will allow you to have access to your content in Contentful.

Installation

Install Contentful from the Python Package Index:

pip install contentful

Your first request

The following code snippet is the most basic one you can use to get some content from Contentful with this library:

import contentful

client = contentful.Client(
  'cfexampleapi',  # This is the space ID. A space is like a project folder in Contentful terms
  'b4c0n73n7fu1'  # This is the access token for this space. Normally you get both ID and the token in the Contentful web app
)

# This API call will request an entry with the specified ID from the space defined at the top, using a space-specific access token.
entry = client.entry('nyancat')

Using this library with the Preview API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This library can also be used with the Preview API. In order to do so, you need to use the Preview API Access token, available on the same page where you get the Delivery API token, and specify the host of the preview API, such as:

client = contentful.Client('cfexampleapi', 'b4c0n73n7fu1', api_url='preview.contentful.com')

You can query for entries, assets, etc. very similar as described in the Delivery API Documentation. Please note, that all methods of the Python client library are snake_cased, instead of JavaScript's camelCase.

Authentication

To get your own content from Contentful, an app should authenticate with an OAuth bearer token.

You can create API keys using the Contentful web interface. Go to the app, open the space that you want to access (top left corner lists all the spaces), and navigate to the APIs area. Open the API Keys section and create your first token. Done.

Don't forget to also get your Space ID.

For more information, check the Contentful REST API reference on Authentication.

Documentation & References

To help you get the most out of this library, we've prepared all available client configuration options, reference documentation, tutorials and other examples that will help you learn and understand how to use this library.

Configuration

The client constructor supports several options you may set to achieve the expected behavior:

client = contentful.Client(
    SPACE_ID,
    ACCESS_TOKEN,
    # ... your options here ...
)
Name Default Description
space_id Required. Your space ID.
access_token Required. Your access token.
environment 'master' Your environment ID.
api_url 'cdn.contentful.com' Set the host used to build the request URIs.
default_locale 'en-US' Defines default locale for the client.
secure True Defines whether to use HTTPS or HTTP. By default we use HTTPS.
authorization_as_header True Sets the authentication mechanisms, if False will send authentication via query string.
raise_errors True Determines whether errors are raised or returned.
content_type_cache True Determines if content type caching is enabled automatically or not, allowing for accessing of fields even when they are not present on the response.
raw_mode False If enabled, API responses are not parsed and the raw response object is returned instead.
gzip_encoded True Enables gzip response content encoding.
max_rate_limit_retries 1 To increase or decrease the retry attempts after a 429 Rate Limit error. Default value is 1. Using 0 will disable retry behaviour. Each retry will be attempted after the value (in seconds) of the X-Contentful-RateLimit-Reset header, which contains the amount of seconds until the next non rate limited request is available, has passed. This is blocking per execution thread.
max_rate_limit_wait 60 Maximum time to wait for next available request (in seconds). Default value is 60 seconds. Keep in mind that if you hit the hourly rate limit maximum, you can have up to 60 minutes of blocked requests. It is set to a default of 60 seconds in order to avoid blocking processes for too long, as rate limit retry behaviour is blocking per execution thread.
timeout_s 1 Maximum time (in seconds) that is allowed for a request before raising a timeout error.
max_include_resolution_depth 20 Maximum amount of levels to resolve includes for library entities (this is independent of API-level includes - it represents the maximum depth the include resolution tree is allowed to resolved before falling back to Link objects). This include resolution strategy is in place in order to avoid having infinite circular recursion on resources with circular dependencies. Note: If you're using an application cache it's advisable to considerably lower this value (around 5 has proven to be a good compromise - but keep it higher or equal than your maximum API-level include parameter if you need the entire tree resolution). Note that when reuse_entries is enabled, the max include resolution depth only affects deep chains of unique objects (ie, not simple circular references).
reuse_entries False When enabled, reuse hydrated Entry and Asset objects within the same request when possible. Can result in a large speed increase and better handles cyclical object graphs. This can be a good alternative to max_include_resolution_depth if your content model contains (or can contain) circular references. Caching may break if this option is enabled, as it may generate stack errors. When caching, deactivate this option and opt for a conservative max_include_resolution_depth value.
proxy_host None To be able to perform a request behind a proxy, this needs to be set. It can be a domain or IP address of the proxy server.
proxy_port None Specify the port number that is used by the proxy server for client connections.
proxy_username None Username for proxy authentication.
proxy_password None Password for proxy authentication.

Reference documentation

Basic queries

content_types = client.content_types()
cat_content_type = client.content_type('cat')
nyancat = client.entry('nyancat')
entries = client.entries()
assets = client.assets()
nyancat_asset = client.asset('nyancat')

Filtering options

You can pass the usual filter options to the query:

client.entries({'content_type': 'cat'}) # query for a content-type by its ID (not name)
client.entries({'sys.id[ne]': 'nyancat'}) # query for all entries except 'nyancat'
client.entries({'include': 1}) # include one level of linked resources
client.entries({'content_type': 'cat', 'include': 1}) # you can also combine multiple parameters

To read more about filtering options you can check our search parameters documentation.

The results are returned as contentful.resource.Resource <contentful.resource.Resource> objects. Multiple results will be returned as list.

Accessing fields and sys properties

content_type = client.content_type('cat')
content_type.description # "Meow."

System Properties behave the same and can be accessed via the #sys method.

content_type.id # => 'cat'
entry.type # => 'Entry'
asset.sys # { 'id': '...', 'type': '...' }

Entry fields also have direct accessors and will be coerced to the type defined in it's content type.

entry = client.entry('nyancat')
entry.lives # 1337
entry.fields() # { 'name': '...', 'lives': '...', ... }

Accessing tags

nyancat = client.entry('nyancat')
nyancat._metadata['tags'] # => [<Link[Tag] id='nyCampaign'>]

Tags can be accessed via the #_metadata method.

Using different locales

Entries can have multiple locales, by default, the client only fetches the entry with only its default locale. If you want to fetch a different locale you can do the following:

entries = client.entries({'locale': 'de-DE'})

Then all the fields will be fetched for the requested locale.

Contentful Delivery API also allows to fetch all locales, you can do so by doing:

entries = client.entries({'content_type': 'cat', 'locale': '*'})

# assuming the entry has a field called name
my_spanish_name = entries.first.fields('es-AR')['name']

When requesting multiple locales, the object accessor shortcuts only work for the default locale.

You can easily request a resource that is represented by a link by calling #resolve:

happycat = client.entry('happycat')
happycat.space
# <Link[Space] id='cfexampleapi'>
happycat.space.resolve(client)
# <Space[Contentful Example API] id='cfexampleapi'>

This works for any kind of Resource.

Assets

There is a helpful method to add image resize options for an asset image:

client.asset('happycat').url()
# => "//images.contentful.com/cfexampleapi/3MZPnjZTIskAIIkuuosCss/
#     382a48dfa2cb16c47aa2c72f7b23bf09/happycatw.jpg"

client.asset('happycat').url(w=300, h=200, fm='jpg', q=100)
# => "//images.contentful.com/cfexampleapi/3MZPnjZTIskAIIkuuosCss/
#     382a48dfa2cb16c47aa2c72f7b23bf09/happycatw.jpg?w=300&h=200&fm=jpg&q=100"

Entries

Entries can have fields in it's default locale accessible with accessor methods:

nyancat = client.entry('nyancat')
nyancat.name
# 'Nyan Cat'

Property Accessors

This library provides a simple API to interact with resources that come from the API, by abstracting the underlying JSON structure of the objects, and exposing all the relevant fields as object properties.

For all resources, sys properties will be available as top level properties, for example:

space = client.space()
space.id
# will return the value of space.sys['id']

In the case of Entries and Assets, as well as having sys available as properties, also all the fields on present on fields will be available as properties, for example:

entry = client.entry('nyancat')
entry.name
# 'Nyan Cat'
# this is equivalent to entry.fields()['name']

asset = client.assets()[0]
asset.file['details']['size']
# will return the size of the image
# this is equivalent to asset.fields()['file']['details']['size']

Other resources, which contain top level properties other than sys or fields, have those available as object properties, for example:

locale = client.locales()[0]
locale.default
# True

Advanced Concepts

Logging

To use the logger, use the standard library logging module:

import logging
logging.basicConfig(level=logging.DEBUG)

client.entries()
# INFO:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): cdn.contentful.com
# DEBUG:requests.packages.urllib3.connectionpool:"GET /spaces/cfexampleapi/entries HTTP/1.1" 200 1994

Proxy example

client = contentful.Client(
    'cfexampleapi',
    'b4c0n7n37fu1',
    proxy_host='127.0.0.1',
    proxy_port=8000,
    proxy_username='username',
    proxy_password='secrect_password'
)

Synchronization

The client also includes a wrapper for the synchronization endpoint. You can call it either with initial=True or with a previous sync_token, additional options are described in the API Documentation:

sync = client.sync({'initial': True}) # Returns all content currently in space
# <SyncPage next_sync_token='w5ZGw6JFwqZmVcKsE8Kow4grw45QdybCnV_Cg8OASMKpwo1UY8K8bsKFwqJrw7DDhcKnM2RDOVbDt1E-wo7CnDjChMKKGsK1wrzCrBzCqMOpZAwOOcOvCcOAwqHDv0XCiMKaOcOxZA8BJUzDr8K-wo1lNx7DnHE'>

sync.items
# [<Entry[1t9IbcfdCk6m04uISSsaIK] id='5ETMRzkl9KM4omyMwKAOki'>,
#   <Entry[1t9IbcfdCk6m04uISSsaIK] id='7qVBlCjpWE86Oseo40gAEY'>,
#   <Entry[1t9IbcfdCk6m04uISSsaIK] id='ge1xHyH3QOWucKWCCAgIG'>,
#   <Entry[1t9IbcfdCk6m04uISSsaIK] id='4MU1s3potiUEM2G4okYOqw'>,
#   <Asset id='1x0xpXu4pSGS4OukSyWGUK' url='//images.contentful.com/cfexampleapi/1x0xpXu4pSGS4OukSyWGUK/cc1239c6385428ef26f4180190532818/doge.jpg'>,
#   <Entry[dog] id='jake'>,
#   <Entry[cat] id='happycat'>,
#   <Entry[dog] id='6KntaYXaHSyIw8M6eo26OK'>,
#   <Entry[human] id='finn'>,
#   <Entry[cat] id='nyancat'>,
#   <Asset id='jake' url='//images.contentful.com/cfexampleapi/4hlteQAXS8iS0YCMU6QMWg/2a4d826144f014109364ccf5c891d2dd/jake.png'>,
#   <Asset id='happycat' url='//images.contentful.com/cfexampleapi/3MZPnjZTIskAIIkuuosCss/382a48dfa2cb16c47aa2c72f7b23bf09/happycatw.jpg'>,
#   <Asset id='nyancat' url='//images.contentful.com/cfexampleapi/4gp6taAwW4CmSgumq2ekUm/9da0cd1936871b8d72343e895a00d611/Nyan_cat_250px_frame.png'>,
#   <Entry[cat] id='garfield'>]


sync = client.sync({'initial': True, 'type': 'Deletion'}) # Only returns deleted entries and assets
# <SyncPage next_sync_token='w5ZGw6JFwqZmVcKsE8Kow4grw45QdybCnV_Cg8OASMKpwo1UY8K8bsKFwqJrw7DDhcKnM2RDOVbDt1E-wo7CnDjChMKKGsK1w5zCrA3CnU7CgEvDtsK6w7B2wrRZwrwPIgDCjVo8PMOoUcK2wqTCl8O1wpY8wpjCkGM'>

sync.items
# [<DeletedEntry id='4rPdazIwWkuuKEAQgemSmO'>,
#    <DeletedAsset id='5c6VY0gWg0gwaIeYkUUiqG'>,
#    <DeletedAsset id='finn'>,
#    <DeletedAsset id='3MZPnjZTIskAIIkuuosCss'>,
#    <DeletedAsset id='4gp6taAwW4CmSgumq2ekUm'>,
#    <DeletedAsset id='1uf1qqyZuEuiwmigoUYkeu'>,
#    <DeletedAsset id='4hlteQAXS8iS0YCMU6QMWg'>,
#    <DeletedEntry id='CVebBDcQsSsu6yKKIayy'>]

sync = sync.next(client) # equivalent to client.sync(sync_token=sync.next_sync_token)

Tutorials & other resources

  • This library is a wrapper around our Contentful Delivery REST API. Some more specific details such as search parameters and pagination are better explained on the REST API reference, and you can also get a better understanding of how the requests look under the hood.
  • Check the Contentful for Python page for Tutorials, Demo Apps, and more information on other ways of using Python with Contentful

Reach out to us

You have questions about how to use this library?

  • Reach out to our community forum: Contentful Community Forum
  • Jump into our community slack channel: Contentful Community Slack

You found a bug or want to propose a feature?

  • File an issue here on GitHub: File an Issue . Make sure to remove any credential from your code before sharing it.

You need to share confidential information or have other questions?

  • File a support ticket at our Contentful Customer Support: File support ticket

Get involved

PRs Welcome

We appreciate any help on our repositories. For more details about how to contribute see our CONTRIBUTING.md document.

License

Copyright (c) 2016 Contentful GmbH. See LICENSE for further details.

Code of Conduct

We want to provide a safe, inclusive, welcoming, and harassment-free space and experience for all participants, regardless of gender identity and expression, sexual orientation, disability, physical appearance, socioeconomic status, body size, ethnicity, nationality, level of experience, age, religion (or lack thereof), or other identity markers.

Read our full Code of Conduct

contentful.py's People

Contributors

cf-allstar[bot] avatar dlitvakb avatar ghepting avatar giacomomagini avatar harshcasper avatar jacobbudin avatar jjolton-contentful avatar johbo avatar loudmouth avatar marcolink avatar mariatta avatar nkalmak avatar peter-bertuglia avatar raskasa avatar roblinde avatar rubydog avatar ruderngespra avatar spring3 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

contentful.py's Issues

Question about version pinning

I am currently integrating the client into a web project and I faced some challenges due to the version pinning inside of the file setup.py:

    'requests==2.12.1',
    'python-dateutil==2.6.0'

This is making it tricky to install it together with a set of dependencies which I have already. Would it be an option to relax this and only provide the pinning inside of the file requirements.txt as something like a "known good set" ?

Accessor methods return locale identifier

When I use the api.contentful.com to retrieve an entry, it loads the following dict into _fields:

{'en-US': {'title': "{'en-US': 'Prepare the Big Wall Estimation (1)'}", 'moderated': True, 'image': {'en-US': {'sys': {'type': 'Link', 'linkType': 'Asset', 'id': '6jIWlcxic8IAA0qYqAqymI'}}}, 'description': "{'en-US': 'This preparation allows you to do a the Big Wall Estimation with your team.'}", 'tags': ['en-US'], 'steps': ['en-US'], 'creator': {'en-US': {'sys': {'type': 'Link', 'linkType': 'Entry', 'id': '5oGDjpcUNymKUuIqkko0aQ'}}}, 'practice': {'en-US': {'sys': {'type': 'Link', 'linkType': 'Entry', 'id': 'LWRx0vUZYk6EU2imIAuI8'}}}}}

The locale is used as a key in the main dictionary as well as for each field.

In the Python wrapper, this means that I cannot use the accessor methods. E.g. entry.title returns "{'en-US': 'Prepare the Big Wall Estimation (1)'}" where I'd expect it to return simply the string 'Prepare the Big Wall Estimation (1)'.

I'm not sure if this is a bug or if I'm doing something wrong.

I'm using version 1.4.3 of contentful.py.

Sometimes Asset.url() return empty string

Sometimes (I cannot detect the conditions) asset-field in Entry have no attached file, so asset.utl() return empty string.

For example

import contentful

client = contentful.Client(
     Config.CONTENTFUL_SPACE_ID,
     Config.CONTENTFUL_PREVIEW_ACCESS_TOKEN,
     api_url="preview.contentful.com")
 
# first approach  
entry = client.entry('xyz', {'locale': '*'})
asset = entry.fields('en')['some_image']  # here asset is <Asset id='xyz' url=''>
asset.url()  # this one returns empty string

but if I create the same asset directly, .url() works fine

# second approach  
client.asset('xyz')  # here asset is <Asset id='xyz' url='//images.ctfassets.net/...'>

First approach definitely worked before and it works now (at least sometimes), so I really cannot understand what is going on.

UPD:
The reason was that we renamed default locale en-US > en and after that entries which were requested with locale="*" had empty Assets. So we had to pass our new default locale to the Client class.

receiving ascii codec when attempting to fetch entries

After successfully connecting to the client, there is an issue running client.entries() where I get an error.
"UnicodeEncodeError: 'ascii' codec can't encode character u'\u2019' in position 189: ordinal not in range(128)"

python 3.8 support?

Unsure if its related to 3.8, but getting this error when importing contentful:

In [1]: import contentful
Traceback (most recent call last):

  File "/Users/ek/.pyenv/versions/3.8.0/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3319, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)

  File "<ipython-input-1-1391c0406839>", line 1, in <module>
    import contentful

  File "/Users/ek/.pyenv/versions/3.8.0/lib/python3.8/site-packages/contentful/__init__.py", line 1, in <module>
    from .client import Client  # noqa: F401

  File "/Users/ek/.pyenv/versions/3.8.0/lib/python3.8/site-packages/contentful/client.py", line 7, in <module>
    from .resource_builder import ResourceBuilder

  File "/Users/ek/.pyenv/versions/3.8.0/lib/python3.8/site-packages/contentful/resource_builder.py", line 2, in <module>
    from .entry import Entry

  File "/Users/ek/.pyenv/versions/3.8.0/lib/python3.8/site-packages/contentful/entry.py", line 1, in <module>
    from .resource import FieldsResource

  File "/Users/ek/.pyenv/versions/3.8.0/lib/python3.8/site-packages/contentful/resource.py", line 1, in <module>
    import dateutil.parser

  File "/Users/ek/.pyenv/versions/3.8.0/lib/python3.8/site-packages/dateutil/parser.py", line 158
    l.append("%s=%s" % (attr, `value`))
                              ^
SyntaxError: invalid syntax

maximum recursion depth exceeded issue when trying to retrieve entries

Hi we're using the python SDK and trying to retrieve list of entries of a specific type using this method

data = client.entries({"content_type": 'video'})

there are around 150 contents of this type and we're getting the following error

File "/Users/mesadat/Documents/content-recommender/venv/lib/python3.7/site-packages/contentful/resource.py", line 54, in _hydrate_sys
for k, v in item.get('sys', {}).items():
RecursionError: maximum recursion depth exceeded while calling a Python object

we are using the same method on other content types without an issue, they don't have as many entries so we're suspecting maybe the large number of content may be causing this error

Add more None checks to FieldType coerce methods

Some FieldType classes have a None check, like so;

class IntegerField(BasicField):
    """Integer Coercion Class"""

    def coerce(self, value):
        """Coerces value to int"""

        return int(value) if value is not None else None

However many do not. TextField does not so it will coerce None to "None".

>>> text_field = TextField()
>>> text_field.coerce(None)
>>> "None"

Having None in a DateField will result in an exception.

>>> date_field = DateField()
>>> date_field.coerce(None)
>>> TypeError: Parser must be a string or character stream, not NoneType

Should not all FieldType classes support None values safely? I would be happy to add tests and implement None checks for every FieldType. Perhaps by moving the None check up to BasicField.

Fields with same names overwrite Entry attribute values and types (e.g. content_types)

If a Contentful field shares the name of an internal class, retrieving the attribute from Entry that is meant to return that corresponding object Type returns instead the values of that Contentful field. Specifically, if a field in Contentful is named "Content Type", getting Entry.content_type returns the value of that field -- usually a str or int -- instead of the expected ContentType object. No error or warning seems to be triggered.

This name collision may apply to other internal classes as well, e.g. Entry, Asset, etc. Other spellings may also trigger this issue ("ContentType", content_type", etc.). I have not been able to test these.

This may be related to the open issue here: #73

Fetching an entity recursive builds forever - hanging forever

When trying to fetch an entity via

workout = self.client.entry(workout_id)

The program hung and continued to grow in memory usage. The id put in the URL of contentful entity and it appears just fine.

Looking into the code I've found that the call to build which is called from the _get function is the recursive issue in resource_builder.py. It will unpack everything in the entity and try to build its descendants.

In this instance, the entity has a link to similar workouts entities which may recursively link back to the original entity which I believe is the likely cause of this problem.

Though this obviously isn't an issue on the website, and is totally permitted behavior.

There looks like there are things in place to detect the depth of entities being built to try and address this issue but obviously there is a bug in this code and it fails to do so, or exit with exception.

In the order of execution build is called first and _build_array is run, and then subsequently, _build_single() is called over and over again.

Courting the number of times the original id appears in the ids list, I can see that it appear 7 times in the first 100 runs of the build single.

def build(self):
        """Creates the objects from the JSON response"""

        if self.json['sys']['type'] == 'Array':
            if any(k in self.json for k in ['nextSyncUrl', 'nextPageUrl']):
                print('sync page')
                return SyncPage(
                    self.json,
                    default_locale=self.default_locale,
                    localized=True
                )
            print('build array')
            return self._build_array()
        print('build single')
        return self._build_single()

    def _build_single(self):
        includes = []
        errors = []
        if self.includes_for_single is not None:
            includes = self.includes_for_single
        if self.errors_for_single is not None:
            errors = self.errors_for_single

        return self._build_item(
            self.json,
            includes=includes,
            errors=errors
        )

    def _build_array(self):

        includes = self._includes()
        print('build includes')
        errors = self._errors()
        print('build errors')

        items = [self._build_item(
                    item,
                    includes=includes,
                    errors=errors
                 ) for item in self.json['items']
                 if not unresolvable(item, self._errors())]

        print('build array items')

        return Array(self.json, items)


    ids = list()

    def _build_item(self, item, includes=None, errors=None):
        print(f'building item {item}')
        if includes is None:
            includes = []
        if errors is None:
            errors = []

        buildables = {
            'Entry': Entry,
            'Asset': Asset,
            'ContentType': ContentType,
            'Space': Space,
            'DeletedEntry': DeletedEntry,
            'DeletedAsset': DeletedAsset,
            'Locale': Locale
        }


        print(self.reuse_entries)
        resource = self._resource_from_cache(item) if self.reuse_entries else None
        if resource is not None:
            return resource

        print('cache')

        if item['sys']['type'] in buildables:

            print('check item', item)
            self.ids.append(item['sys']['id'])
            if len(self.ids) > 100:
                print(self.ids)
                s = set(self.ids)
                print(len(s), s)

                exit()

            return buildables[item['sys']['type']](
                item,
                default_locale=self.default_locale,
                localized=self.localized,
                includes=includes,
                errors=errors,
                resources=self.resources,
                depth=self.depth,
                max_depth=self.max_depth
            )

Cannot retrieve all localized fields

Hi there:)

We are consume Preview API by using the client and we try to localize our entries with Field-level localization.

There is our simple content type with 3 fields:

MyContentType:
- title - Short Text
- text - Rich Text [localization enabled]
- story_background - Asset Image

Below is short example that shows a bit strange behavior for me:

In [3]: client = contentful.Client('xxxx', 'yyyy', api_url="preview.contentful.com")
In [4]: e = c.entry('piu-piu-xyz')
In [5]: e.fields()
Out[5]: 
{'title': 'My Super Entry',
 'story_background': <Asset id='7sXi0s40LopvArQWPOEugJ' url='//images.ctfassets.net/y0gz53fkm5u0/7sXi0s40LopvArQWPOEugJ/161695276187cdd36f7232d4df167845/Back_1.jpg'>,
 'text': {} # some object with English text 
 }
In [7]: e = c.entry('piu-piu-xyz', {'locale': '*'})
In [8]: e.fields()
Out[8]: {}
In [9]: e.fields(locale='ru')
Out[9]: 
{
 'text': {...}  # Some Russian text, but where is "story_background" and "title"?
}

So, my goal is to read all entry fields in all available locales with single API call. Is it possible?

Embedded Asset in rich text field is not localized when using locale's wildcard

When I'm requesting Entry or building it with ResourceBuilder().build() I'm facing strange behavior -- embedded assets are not localized.

During debugging I've checked raw API response and I see correct asset's URLs are there, but seems like there is some sort of a bug.

Complete example how to reproduce the bug is bellow.

def foo():
    client = contentful.Client(
        "xyz",
        "xyz",
        default_locale="en-US",
        api_url="preview.contentful.com",
    )
    entry = client.entry('qwerty12345', {"locale": "*"})

    # here rich text field contains Portuguese text, but embedded Asset is for default language (en-US)
    rich_text_field = entry.fields(locale='pt')['text']

Client version is contentful==1.13.1

[feature] Access `validations` information for a ContentType

The Content Delivery API returns some basic information about a content type (required, omitted, ...), but validations doesn't seem to be included. This will be a useful thing when we try to dynamically generate forms for a ContentType.

Is there a way of retrieving the same raw JSON we can see at the content type management page?

Example

Return objects total count

I would like to organize a pagination on my page. I can pass parameters limit and skip with query. But, when I receive a response I don't recieve total count of records, although this field is returned folowing by CD API.
I can get all records and then make a slice, but it's a bad practice.
I propose to return total count with objects list.
For example:

...
>>> client.entries({'limit':10, 'skip': 20})
{
  "items": [
    <Entry[2PqfXUJwE8qSYKuM0U6w8M] id='6dbjWqNd9SqccegcqYq224'>,
    ...
  ],
  "total": 2564
}

For now, I have to make additional request to Content Management API with parameter limit=0 and get total count from response.

Option to disable automatic snake_case transformation

I know that this an attempt to be helpful but it's usually not, in my experience. The SDK uses key_name and the webhook uses keyName, so how are we supposed to abstract the two without a horrific mess of:

  • storing two key names for each field, or
  • on-the-fly transformation back to camel case on the already-transformed key names?

The other Contentful SDKs seem to have added an option for disabling this. I can't seem to find it in the Python one, so is it hidden somewhere? I guess raw_mode might work? But that's presumably going to disable all the other useful processing too.

Personally, I'm storing objects in a relational DB + data warehouse too so I've got at least 3 different keys to deal with. :(

Please add this if not already available. Thanks!

Documentation for upgrading from contentful.py 0.9.2?

Hello,

In upgrading from Python 3.4 to 3.6, we are currently using contentful.py 0.9.2 which does not seem to like py3.6, and we wish to upgrade to 1.6.0 which explicitly lists py3.6 as supported.

However I've not had any luck finding lists of deprecations or breaking changes in the changelogs, and simply upgrading the library has resulted in some errors. Do you have any documentation or lists of breaking changes between these versions?

Thanks!

Content type caching breaks for multiple spaces with same content type names

Since ContentTypeCache has a static field __CACHE__ = [], this __CACHE__ object is reused across clients.

ContentTypeCache's methods get and update_cache are called statically instead of on different instances of ContentTypeCache

So if I have two spaces, and need two clients like

client_for_space_1 = contentful.Client(<credentials>...,space_id = 1)
client_for_space_2 = contentful.Client(<credentials>..., space_id = 2)

When update_cache is called for the second client, it will replace the cache made by the first client.

This might go undetected if the spaces have different content type ids or content types with different field names. But if the spaces have the same content type ids, then when the cache is called like:

content_type = ContentTypeCache.get(
            self.sys['content_type'].id
        )

It will happily return a content type which might not have the same field type. This causes errors if for example, it expects a dict, but instead gets a string type.

So either new instances of ContentTypeCache should be created per client, or the key to __CACHE__ should be prefixed with the space id.

Thanks for reading!

Deprecation warning due to invalid escape sequences

Deprecation warnings are raised due to invalid escape sequences. This can be fixed by using raw strings or escaping the literals. pyupgrade also helps in automatic conversion : https://github.com/asottile/pyupgrade/

find . -iname '*.py' | grep -v example | xargs -P4 -I{} python3.8 -Wall -m py_compile {}
./tests/client_test.py:225: DeprecationWarning: invalid escape sequence \/
  self.assertTrue(re.search('os (Windows|macOS|Linux)(\/.*)?;', header))
./tests/client_test.py:316: DeprecationWarning: invalid escape sequence \/
  self.assertTrue(re.search('os (Windows|macOS|Linux)(\/.*)?;', header))
./tests/client_test.py:340: DeprecationWarning: invalid escape sequence \/
  self.assertTrue(re.search('os (Windows|macOS|Linux)(\/.*)?;', header))

Incorrect documentation for Search parameters

I need to filter entries by Content Type and fields existence. So, the expected code, that API documentation suggests is:

client.entries({'content_type': '<content_type_id>', 'fields.tags[exists]': True})

or, if real-life example:

client.entries(query={"content_type": "surveyTemplate", "fields.survey_id[exists]": True})

But the query doesn't work, because [exists] part is being ignored. To make it work, you need to change True to "true", which is a bit unexpected. So, the code needs to look like this:

client.entries(query={"content_type": "surveyTemplate", "fields.survey_id[exists]": "true"})

I assume, that's not the only example of incorrect bool to text conversion, but one of the easiest ones to check. So, I suggest either to fix the documentation to mirror the actually expected input, or fixing the library to make bool values work.

IndexError using client.entry when no entry with given id exists

This will throw a somewhat confusing IndexError when no entry with the id exists.

client.entry('1234')

This is because the entry function assumes something will always exist and tries to return the first item in the result.

return self._get(
    '/entries',
    query
)[0]

I propose adding a more descriptive exception, i.e. EntryNotFound. If you like this idea I can submit a PR for it.

How can I include the version header?

How can I include the X-Contentful-Version header using the Python client?

        entry_id = id  
        entry = management_client.entries(space_id, environment_id).create(entry_id, {
            'content_type_id': experiment_content_id,
            'fields': {
                'contentMarkdown': {
                    'de': body
                }
            }
        })

Without this header, I get the error:

contentful_management.errors.VersionMismatchError: HTTP status code: 409
Message: Version mismatch error. The version you specified was incorrect. This may be due to someone else editing the content.

Optional linked assets cannot be represented

Asset.__repr__() utilises Asset.url() which depends on file instance attribute.

If the asset is optional and not set, the object cannot be represented: 'contentful.asset.Asset object' has no attribute 'file'.

python 3?

Does this lib apply to python 3.6?

Cannot delete an Entry

Just going through the docs here and I can't see any examples of the SDK deleting Entries despite it being supported by the REST API. I'm searching things like "Delete", "Delete entries", "Deleting an entry", "Deleting entries".

Can anyone advise of a workaround or point me to the correct piece of documentation if this is indeed possible.

Locale is lost when using Link.resolve()

# "include": 0 is to keep this example shorter, the bug affects any include level
>>> question = client.entry("1dBxUVU0IY2O5gQVCINMJB", {"locale": "pt", "include": 0})
>>> question.locale
'pt'
>>> question.group
<Link[Entry] id='2znJjza39lwrgkTOPFDSh4'>
>>> question.group.resolve().locale
'en'

I would expect the initially requested locale to be preserved when resolving linked entities.

Unicode payloads are not being encoded correctly

Hello, we encountered a problem where uploading a just under 500KB payload to a json field, consisting mostly of Russian Characters was hitting the 1MB limit. On looking at the web traffic, it looks like the payload is using ascii encoding rather than utf-8. like this '\u041d\u0435 \u0432'

Uploading the same data via requests with ensure_ascii=False on the json convert works fine. It looks correct in the admin portal and publishes fine

json.dumps(content_dictionary,ensure_ascii=False)

Is this a bug or expected? is there a workaround other than not using the sdk?

Im on version: 2.13.1 installed with pip3
Python version 3.11.1
This happens on both macOS and ubuntu

Thank you

Martin

Incorrect documentation for sync

In README.rst, the Synchronization section has this code sample:

sync = sync.next(client) # equivalent to client.sync(sync_token=sync.next_sync_token)

The example code given in the comment doesn't work. It needs to be:

client.sync({'sync_token': sync.next_sync_token})

Python EOL Version Support

Hello, I see there is very little active maintenance here, I assume because this client is stable. However, I have a few concerns after reviewing the library:

  1. Python 2-3.7 are EOL - do you intend to maintain strict support for versions which are no longer maintained?
  2. Related to the above, the library is unable to provide type annotations because of supporting versions 2-3.6 of Python, so the developer experience is quite lacking.
  3. Also related, there is no asyncio support, so this library is dangerous to use on asyncio-native applications, since all requests are sync and you have implemented retry mechanisms using time.sleep - is asyncio support on your roadmap?

The implementation looks flexible enough that adding support for (2) and (3) would be possible, but that largely depends upon your vision regarding (1). I'm happy to help support a transition if it's something you're open to.

TypeError: int() argument must be a string, a bytes-like object or a number, not 'dict'

from this code:
import contentful
import logging

logging.basicConfig(level=logging.DEBUG)

client = contentful.Client(...)

entries = client.entries()
sync = client.sync({'initial': True})

Stacktrace:
DEBUG:urllib3.connectionpool:https://cdn.contentful.com:443 "GET /spaces/wsn2eby0by7h/sync?initial=true HTTP/1.1" 200 57767
Traceback (most recent call last):
File "", line 1, in
File "/usr/local/lib/python3.6/site-packages/contentful/client.py", line 314, in sync
query
File "/usr/local/lib/python3.6/site-packages/contentful/client.py", line 533, in _get
max_depth=self.max_include_resolution_depth
File "/usr/local/lib/python3.6/site-packages/contentful/resource_builder.py", line 51, in build
return self._build_array()
File "/usr/local/lib/python3.6/site-packages/contentful/resource_builder.py", line 65, in _build_array
) for item in self.json['items']]
File "/usr/local/lib/python3.6/site-packages/contentful/resource_builder.py", line 65, in
) for item in self.json['items']]
File "/usr/local/lib/python3.6/site-packages/contentful/resource_builder.py", line 87, in _build_item
max_depth=self.max_depth
File "/usr/local/lib/python3.6/site-packages/contentful/resource.py", line 84, in init
self._fields = self._hydrate_fields(item, localized, includes)
File "/usr/local/lib/python3.6/site-packages/contentful/resource.py", line 112, in _hydrate_fields
includes
File "/usr/local/lib/python3.6/site-packages/contentful/entry.py", line 50, in _coerce
return content_type_field.coerce(value)
File "/usr/local/lib/python3.6/site-packages/contentful/content_type_field.py", line 39, in coerce
return self._coercion.coerce(value)
File "/usr/local/lib/python3.6/site-packages/contentful/content_type_field_types.py", line 62, in coerce
return int(value)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'dict'

client always returns Keyerror: RichTextField

Following the documentation I use the following code to retrieve all entries with certain content type:

client = Client(space_id=space, access_token=token, environment='master')

entries_by_content_type = client.entries({'content_type': 'article'})

it always returns KeyError: 'RichTextField'.
Any help?

Converting Entry to JSON does not include field values for multi-nested resources

I am currently using the Contentful client in Python within one of our APIs to get entries matching a query and return the entries as a response using ujson.

When we get an entry using client.entries(), we are able to see all linked resources are hydrated properly in each entry's _fields property, however Python does not include properties for objects that are prefixed with _ when serializing to json using ujson or json.

https://github.com/contentful/contentful.py/blob/master/contentful/resource.py#L113

This means that when we return the response of a list of entries with multiple nested resources from our API, we are unable to use the nested entries due to not having the hydrated fields resources, despite them having originally been hydrated

Roughly, for example:

When looking at the only entry in a list in the response from client.entries(), where the content is multiple levels of nested references

a_entry.fields() = {
  title: "Entry A",
  content: [
    B_Entry: {
      sys: {...},
      _fields: {
         title: "Entry B",
         content: [
           D_Entry: {
               sys: {...},
              _fields: { 
                  title: "Entry D",
                   ...
              }
           }
        ]
      }
    },
    C_Entry: {
      sys: {...},
      _fields: {
        title: "Entry C",
        ....  
        }
     },
  ]
}

serializes to json:

 {
  title: "Entry A",
  content: [
  {
   "sys": {...},
  },
  {
   "sys": {...},
  }
]

}

Where sys only holds references to the link IDs, and all actual fields within _fields are lost that aren't in the top-level entry

Is there something that I am missing here to be able to easily have the field values included as JSON?

I understand that we can set the value of each entry to a new key by doing [entry].fields(), but when we have many levels of nested resources included, it feels complex to have to go back through each level to set a key like field to be the value of _field after it was already built

TypeError: 'Link' object is not subscriptable

Contentful 1.11.3 on Python 3.6.6 throwing TypeError when trying to answer the following call:

client.entries({'content_type': 'blogPost'})

The content model is roughly this:

blogPost
  - title : short text
  - slug : short text
  - hero_image : media
  - description : long text
  - body : references, many, accept only 'blogPostLayout'
  - author : reference, accept only 'person'
  - tags : short text, list
  - publish date : datetime
  - external comments id : number, unique

blogPostLayout
  - slug : short text, unique
  - main body : rich text, allow linking other entries, allow only 'blogPostLayout', 'blogPostMarkdown'

blogPostMarkdown
  - content : long text, format Markdown

blogPostRichContent
  - content : rich text, allow linking inline entries

This issue was caused by a blogPost that had in its body references to the same blogPostLayout twice, and additionally one other blogPostLayout. These blogPostLayouts themselves contained only an embedded entry to one blogPostMarkdown each.

The issue would clear after removing the second reference to the same blogPostLayout

I can provide further details if you need, and am happy to be contacted by Contentful support directly.
I'm not sure if the library includes a way to dump the schema?

My best guess for the cause is that the client library is trying to populate something it has already 'hydrated' (if I understand the nomenclature)

Full traceback:

content_client.entries({'content_type': 'blogPost'})

~/web/VENV/lib64/python3.6/site-packages/contentful/client.py in entries(self, query)
    254         return self._get(
    255             self.environment_url('/entries'),
--> 256             query
    257         )
    258 

~/web/VENV/lib64/python3.6/site-packages/contentful/client.py in _get(self, url, query)
    573             response.json(),
    574             max_depth=self.max_include_resolution_depth,
--> 575             reuse_entries=self.reuse_entries
    576         ).build()
    577 

~/web/VENV/lib64/python3.6/site-packages/contentful/resource_builder.py in build(self)
     59                     localized=True
     60                 )
---> 61             return self._build_array()
     62         return self._build_single()
     63 

~/web/VENV/lib64/python3.6/site-packages/contentful/resource_builder.py in _build_array(self)
     84                     includes=includes,
     85                     errors=errors
---> 86                  ) for item in self.json['items']
     87                  if not unresolvable(item, self._errors())]
     88 

~/web/VENV/lib64/python3.6/site-packages/contentful/resource_builder.py in <listcomp>(.0)
     85                     errors=errors
     86                  ) for item in self.json['items']
---> 87                  if not unresolvable(item, self._errors())]
     88 
     89         return Array(self.json, items)

~/web/VENV/lib64/python3.6/site-packages/contentful/resource_builder.py in _build_item(self, item, includes, errors)
    118                 resources=self.resources,
    119                 depth=self.depth,
--> 120                 max_depth=self.max_depth
    121             )
    122 

~/web/VENV/lib64/python3.6/site-packages/contentful/resource.py in __init__(self, item, includes, errors, localized, resources, **kwargs)
    102         )
    103 
--> 104         self._fields = self._hydrate_fields(item, localized, includes, errors, resources=resources)
    105 
    106     def _hydrate_fields(self, item, localized, includes, errors, resources=None):

~/web/VENV/lib64/python3.6/site-packages/contentful/resource.py in _hydrate_fields(self, item, localized, includes, errors, resources)
    119             self._hydrate_localized_entry(fields, item, includes, errors, resources)
    120         else:
--> 121             self._hydrate_non_localized_entry(fields, item, includes, errors, resources)
    122         return fields
    123 

~/web/VENV/lib64/python3.6/site-packages/contentful/resource.py in _hydrate_non_localized_entry(self, fields, item, includes, errors, resources)
    144                 includes,
    145                 errors,
--> 146                 resources=resources
    147             )
    148 

~/web/VENV/lib64/python3.6/site-packages/contentful/entry.py in _coerce(self, field_id, value, localized, includes, errors, resources)
     42                         includes,
     43                         errors,
---> 44                         resources=resources
     45                     )
     46                 )

~/web/VENV/lib64/python3.6/site-packages/contentful/entry.py in _build_nested_resource(self, value, localized, includes, errors, resources)
     97                     includes,
     98                     errors,
---> 99                     resources=resources
    100                 )
    101 

~/web/VENV/lib64/python3.6/site-packages/contentful/entry.py in _resolve_include(self, resource, localized, includes, errors, resources)
    113             resources=resources,
    114             depth=self._depth + 1,
--> 115             max_depth=self._max_depth
    116         ).build()
    117 

~/web/VENV/lib64/python3.6/site-packages/contentful/resource_builder.py in build(self)
     60                 )
     61             return self._build_array()
---> 62         return self._build_single()
     63 
     64     def _build_single(self):

~/web/VENV/lib64/python3.6/site-packages/contentful/resource_builder.py in _build_single(self)
     73             self.json,
     74             includes=includes,
---> 75             errors=errors
     76         )
     77 

~/web/VENV/lib64/python3.6/site-packages/contentful/resource_builder.py in _build_item(self, item, includes, errors)
    118                 resources=self.resources,
    119                 depth=self.depth,
--> 120                 max_depth=self.max_depth
    121             )
    122 

~/web/VENV/lib64/python3.6/site-packages/contentful/resource.py in __init__(self, item, includes, errors, localized, resources, **kwargs)
    102         )
    103 
--> 104         self._fields = self._hydrate_fields(item, localized, includes, errors, resources=resources)
    105 
    106     def _hydrate_fields(self, item, localized, includes, errors, resources=None):

~/web/VENV/lib64/python3.6/site-packages/contentful/resource.py in _hydrate_fields(self, item, localized, includes, errors, resources)
    119             self._hydrate_localized_entry(fields, item, includes, errors, resources)
    120         else:
--> 121             self._hydrate_non_localized_entry(fields, item, includes, errors, resources)
    122         return fields
    123 

~/web/VENV/lib64/python3.6/site-packages/contentful/resource.py in _hydrate_non_localized_entry(self, fields, item, includes, errors, resources)
    144                 includes,
    145                 errors,
--> 146                 resources=resources
    147             )
    148 

~/web/VENV/lib64/python3.6/site-packages/contentful/entry.py in _coerce(self, field_id, value, localized, includes, errors, resources)
     60                     resources=resources,
     61                     default_locale=self.default_locale,
---> 62                     locale=self.sys.get('locale', '*')
     63                 )
     64 

~/web/VENV/lib64/python3.6/site-packages/contentful/content_type_field.py in coerce(self, value, **kwargs)
     37         if value is None:
     38             return None
---> 39         return self._coercion.coerce(value, **kwargs)
     40 
     41     def _get_coercion(self):

~/web/VENV/lib64/python3.6/site-packages/contentful/content_type_field_types.py in coerce(self, value, includes, errors, resources, default_locale, locale)
    233             resources=resources,
    234             default_locale=default_locale,
--> 235             locale=locale
    236         )

~/web/VENV/lib64/python3.6/site-packages/contentful/content_type_field_types.py in _coerce_block(self, value, includes, errors, resources, default_locale, locale)
    195                     resources=resources,
    196                     default_locale=default_locale,
--> 197                     locale=locale
    198                 )
    199                 if link:

~/web/VENV/lib64/python3.6/site-packages/contentful/content_type_field_types.py in _coerce_link(self, value, includes, errors, resources, default_locale, locale)
    151 
    152     def _coerce_link(self, value, includes=None, errors=None, resources=None, default_locale='en-US', locale=None):
--> 153         if value['data']['target']['sys']['type'] != 'Link':
    154             return value['data']['target']
    155 

TypeError: 'Link' object is not subscriptable

IndexError: list assignment index out of range

Hello. I've just faced strange error from your lib and don't understand what is the cause and how to fix it. I have pretty simple logic to retrieve entries filtered by content_type

entries = client.entries({'content_type': 'release'})

all worked fine before and I had results from contentful. One entry had changes and I pressed "publish it" and now instead of results I see this error in console:

File "/Users/dmnbrest/Documents/Kaptio/kaptio-docs/docs-backend/pages/contentful.py", line 49, in get_categories
entries = client.entries({'content_type': 'release'})
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/client.py", line 260, in entries
query
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/client.py", line 580, in _get
reuse_entries=self.reuse_entries
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/resource_builder.py", line 61, in build
return self._build_array()
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/resource_builder.py", line 86, in _build_array
) for item in self.json['items']
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/resource_builder.py", line 87, in
if not unresolvable(item, self._errors())]
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/resource_builder.py", line 120, in _build_item
max_depth=self.max_depth
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/resource.py", line 104, in init
self._fields = self._hydrate_fields(item, localized, includes, errors, resources=resources)
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/resource.py", line 121, in _hydrate_fields
self._hydrate_non_localized_entry(fields, item, includes, errors, resources)
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/resource.py", line 146, in _hydrate_non_localized_entry
resources=resources
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/entry.py", line 62, in _coerce
locale=self.sys.get('locale', '*')
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/content_type_field.py", line 39, in coerce
return self._coercion.coerce(value, **kwargs)
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/content_type_field_types.py", line 239, in coerce
locale=locale
File "/Users/dmnbrest/.local/share/virtualenvs/docs-backend-8eyOp-ef/lib/python3.7/site-packages/contentful/content_type_field_types.py", line 221, in _coerce_block
del value['content'][node_index]
IndexError: list assignment index out of range

Draft content is returned from entries

Consider the following:

  • ModelA contains optional link to resource of type ModelB
  • nyancat is of type ModelA and contains DRAFT content of type ModelB

Screen Shot 2020-06-24 at 5 11 59 PM

When we fetch the content, we get some metadata in return:

res = client.entries({"content_type": "nyancat"})
content = res[0]
print content.signup

We see this output in reference to the draft content:

{u'sys': {u'linkType': u'Entry', u'type': u'Link', u'id': u'75fmVQxvWvm4UqCOyMR163'}}

For published content, I'd except to see:

<Entry[modelB] id='Xx9XxXnxx9xX99X9xxX9x9'>

Since draft content is not published, this should operate as if the content does not exist.


Draft content should not be returned from the Content Delivery API

Ref: https://www.contentful.com/developers/docs/tutorials/general/determine-entry-asset-state/

content.signup should be omitted altogether since it's in draft state

The problem this causes for me is that there is no way to determine if I should use fallback data.

My check for if content and content.fields().get("signup") should fail allowing me to properly set a default value.
However:

  • content.fields().get('signup') exists
  • and content.signup.fields() does not (since signup is a dict here)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.