contentful-orm 0.1.0
A Python toolkit for Contentful to let you create/maintain your Content Type and queries in ORM style.

# ContentfulORM
***
A Python toolkit for [Contentful](https://www.contentful.com/) to let you create/maintain your Content Type and queries in ORM style.

## Install
***
- To install:
    ```
    pip install git+https://github.com/Phoenix-Chen/ContentfulORM.git
    ```

## Usage
***

### ORM Environment
***
- Create an ORM Environment:
    ```python
    import contentful_management
    from contentful_orm import ORMEnvironment

    # First create an contentful_management.environment.Environment
    client = contentful_management.Client('CONTENTFUL_MANAGEMENT_TOKEN')
    space = client.spaces().find('CONTENTFUL_SPACE_ID')
    environment = space.environments().find('CONTENTFUL_ENVIRONMENT')

    # Then create ORMEnvironment use contentful_management.environment.Environment
    orm_env = ORMEnvironment.from_parent(environment)
    ```

### Content Type Model
***
- Model your content type:
    ```python
    from datetime import datetime
    from contentful_orm.models import Model
    from contentful_orm.fields import ArrayField, BooleanField, DateField, DecimalField, IntegerField, MediaField, ReferenceField, SymbolField, TextField, LocationField
    from contentful_orm.fields.validations import In, Range, Unique, Size, Regex, ImageDimensions, FileSize

    # class name will become content type name
    # class name in camel case will become content type id
    class Person(Model):
        # docstring will become content type description
        """Person model description
        """
        __display_field__ = 'name'

        # Each field need to be a {SomeType}Field from contentful_orm.fields
        # Most of the fields have keyword argument: disabled, localized, omitted, required, validations and default
        # (disabled, localized, omitted and required default to False. validations defaults to []. default defaults to None)
        email = SymbolField(validations=[Unique, Regex('^\\w[\\w.-]*@([\\w-]+\\.)+[\\w-]+$', error_msg='Invalid email address.')], required=True)
        name = SymbolField(localized=True, required=True)
        # ArrayField takes an argument to specify content type
        title = ArrayField(SymbolField(validations=[In(['Manager', 'Seller'], error_msg='Invalid title')]), localized=True)
        age = IntegerField(validations=[Range(min=1, error_msg='age must be a positive integer.')])
        created_date = DateField(default=datetime.now().strftime("%Y-%m-%dT%H:%M-00:00"))

    class Company(Model):
        """Company model description
        """
        __display_field__ = 'name'

        name = SymbolField(validations=[Unique], required=True)
        employees = ArrayField(ReferenceField(model_set={Person}, error_msg='employee has to be a Person entry'))
        address = LocationField()

    class Product(Model):
        """Product model description
        """
        __display_field__ = 'name'

        name = SymbolField(validations=[Unique], required=True)
        description = TextField(localized=True)
        # ReferenceField takes an additional keyword argument model_set to restrict reference content type.
        # (Currently doesn't support reference to the content type itself)
        seller = ReferenceField(model_set={Person, Company}, error_msg="seller has to be a Person or Company entry")
        images = ArrayField(
            MediaField(
                # validations takes a list of contentful_orm.fields.validations
                # See: https://www.contentful.com/r/knowledgebase/validations/ for details
                validations=[
                    ImageDimensions(max_width=12, max_height=12),
                    FileSize(max=40000, error_msg='Maximum file size is 40000 Bytes')
                ]),
            validations=[Size(max=10, error_msg="At most 10 images")]
        )
        sponsored = BooleanField(required=True)
        price = DecimalField(required=True)
    ```
- Create and publish the content type using model:
    ```python
    orm_env.create(Person).publish()
    # Or
    orm_env.create(Person)
    orm_env.publish(Person)
    ```
- Unpublish and delete the content type using model:
    ```python
    orm_env.unpublish(Product)
    orm_env.delete(Product)
    ```
- Add and publish entry:
    ```python
    person1 = orm_env.add(Person(email='a@a.com', name='a', title=['Manager', 'Seller'], age=13)).publish()
    # You can specify entry id or it will randomly generate a UUID
    person2 = orm_env.add(Person(email='b@a.com', name='b', title=['Seller'], age=66), id='S9SN3JWKN3565D').publish()
    person3 = orm_env.add(Person(email='c@a.com', name='c', title=['Manager'], created_date='2019-08-11T00:00-07:00')).publish()
    company1 = orm_env.add(Company(name='Contentful', employees=[person1.to_link().to_json(), person2.to_link().to_json()])).publish()
    # Or
    product1 = Product(
        name='ContentfulORM',
        description='Write your Contentful in ORM style!',
        seller=company1.to_link().to_json(),
        sponsored=True,
        price=0.01
    )
    orm_env.add(product1).publish()
    ```
- Query:
    ```python
    # Query all entries of a content type
    orm_env.query(Person).all()

    # Filter query
    # ORMContentTypeEntriesProxy extends ContentTypeEntriesProxy with additional filter function
    from contentful_orm.operators import all_, limit, select, skip

    # Filter with field names as keyword arguments for exact search
    orm_env.query(Person).filter(email='c@a.com', name='c')

    # Filter with operators, see operators all at: https://github.com/Phoenix-Chen/ContentfulORM/blob/dev/contentful_orm/operators.py
    orm_env.query(Person).filter(all_(title=['Manager', 'seller']), skip(1), limit(100), select('fields.name'))

    # Use combination of both
    orm_env.query(Person).filter(limit(1), name='c')

    ```
- Serialize:
    ```python
    from contentful_orm.serializers import ModelSerializer

    class ProductSerializer(ModelSerializer):
        class Meta:
            model = Product
            # Specify fields to be serialized or use '__all__' for all the fields
            fields = [
                'name',
                'description',
                'price'
            ]

    # Query the entry/entries you want to serialize
    products = orm_env.query(Product).all()

    # Serialize single entry
    serialized_product = ProductSerializer(products[0])

    # Serialize multiple entries
    serialized_products = ProductSerializer(products, many=True)

    # Currently links won't be recursively serialized, in case of circular references.
    ```