Model Document Structure

Structure definition dictionary is the most important part of your model document. They are similar to table fields in relational SQL systems in a way. You simply need to define them as key-value that corresponds to field_name: field_type. Keep reading...

Allowed Types

To begin, you may simply define those field types as standard Python types, see couchbasekit.schema.ALLOWED_TYPES for the list of them.

Document Relations

You can define a field type as another model document (or even recursively) within your structure. This simulates kind of foreign key scenario in the relational systems but you must know that every related document will be fetched separately from couchbase server as the nature of the non-relational systems.

The good news is, these relations are lazy-loaded, fetched on-demand and couchbasekit caches them during the object’s life time.

>>> lonely_galaxy = Publisher('lonely_galaxy')
>>> dna = Author('douglas_adams')
>>> dna.publisher = lonely_galaxy
>>> dna.save()
4535519295771
>>> dna = Author('douglas_adams') # retrieve the same doc
>>> dna.get('publisher')
u'publisher_lonely_galaxy'
>>> dna.publisher # or dna.load()
{u'doc_type': u'publisher', u'created_at': u'2012-11-18 16:24:16.784474+00:00', 'slug': u'lonely_galaxy', u'name': u'Lonely Galaxy Press'}
>>> dna.get('publisher') # no more raw, already cached
{u'doc_type': u'publisher', u'created_at': u'2012-11-18 16:24:16.784474+00:00', 'slug': u'lonely_galaxy', u'name': u'Lonely Galaxy Press'}
>>>

Custom Fields

With couchbasekit, of course you can have your specific field types and a few of them may be already defined in couchbasekit.fields. Creating your own custom field is quite easy, please refer to couchbasekit.fields.CustomField.

As an example, the password fields are salted randomly and encrypted on the fly, thus cannot be decrypted back:

>>> from couchbasekit.fields import PasswordField
>>> raw = '123456'
>>> PasswordField(raw)
'$2a$12$1nshsN7Nt8e3.dPd1ZcA7uJnesu2sg52nZl6CX1N0ETZwc2UYCGYS'
>>> PasswordField(raw)
'$2a$12$UyLdw0QwHJmONipuyQ3Mq.NA4YteHZ8NDwXFpaJP.xi9ZnUjmxvWa'
>>> hashed = PasswordField(raw)
>>> hashed.value
'$2a$12$r490Tn0zEaMYTf.dfjBNoe0I729Ej2Z18xTJLbwfqZyOXeabXZUky'
>>> hashed.check_password('incorrect')
False
>>> hashed.check_password('123456')
True
>>>

List (Multi Value) Fields

You can also define a list() of values. For example:

class Book(Document):
    __bucket_name__ = 'couchbasekit_samples'
    doc_type = 'book'
    structure = {
        'title': unicode,
        'published_at': datetime.date,
        'pictures': list,
        'tags': [unicode],
    }

Note that if you are sure what type of elements a List Field will have, you should explicitly specify it as an instance with a single value in it (i.e. 'tags': [unicode]). Otherwise just let it be list then it can have any combination of values in it.

However, be careful if you define your field as a plain list (such as 'pictures': list). You will always get a list of basic types (e.g. unicode, int, float, bool etc..) as couchbasekit doesn’t know if they’re any of the advanced ones (e.g. custom fields, document relations, datetime.date, etc..). For example, if you save a document relation in them, you will get its couchbasekit.document.Document.doc_id() as unicode but not the document itself, which actually would be useful for performance tuning.

Schemaless Fields

Some of your model documents may need complicated structure, such as pre-defined item types of a dictionary, deeply nested dictionary or totally schemaless sub-structures.

Warning

One downside of such free dictionary models is that you can’t use attribute access (a.k.a. dot notation), so you have to use dictionary-like item assignment and the same rule applies for retrieving of your data.

First and easiest example would be a total schemaless model document:

class FreeModel(Document):
    __bucket_name__ = 'couchbasekit_samples'
    doc_type = 'free'
    structure = {}

free = FreeModel()
# that does NOT work because 'somefield' wasn't defined in the structure
free.somefield = 'some value'
# but that will work:
free['somefield'] = 'some value'
# and those also will work as the Document class is a dictionary itself!
free = FreeModel(somefield='some value', listfield=['list', 'of', 'items'])
# or that's ok too:
data = {'somefield': 'some value', 'listfield': ['list', 'of', 'items']}
free = FreeModel(data)

If you want a semi schemaless structure on a specific field that means you know it will be dictionary and what type for its keys and values will be, you may define only types for its key-value pair:

class User(Document):
    __bucket_name__ = 'couchbasekit_samples'
    doc_type = 'user'
    structure = {
        'username': unicode,
        'email': EmailField,
        'password': PasswordField,
        'logins': {
            # datetime: ip
            datetime.datetime: unicode,
        },
    }

Finally, deeply nested dictionary fields:

class Book(Document):
    __bucket_name__ = 'couchbasekit_samples'
    doc_type = 'book'
    structure = {
        'title': unicode,
        'published_at': datetime.date,
        'pictures': list,
        'tags': [unicode],
        'category': {
            u'History': bool,
            u'Sci-Fiction': bool,
            u'Cooking': {
                u'Turkish': bool,
                u'Italian': bool,
                u'Fast Food': bool,
                u'Dessert': bool,
            },
        },
    }

Note

Please note that again; dot notation does not work for deeply nested dictionaries either. So you can’t check or set of a book’s Dessert category by dot notation:

>>> book = Book('ad45556b3ba4')
>>> book.category.Cooking.Dessert # wrong!
>>> book.category.Cooking[u'Dessert'] # wrong!
>>> book.category is None
True
>>> book.category['Cooking']['Dessert'] = False # wrong, as 'category' is not assigned yet
>>> book.category = {u'Cooking': {u'Dessert': True}} # correct
>>> book.category['Cooking']['Dessert'] = True # it was created, so it's ok now
>>> book['category']['Cooking']['Dessert'] = True # correct, same as above
>>> book.category['History'] # wrong, you'll get a KeyError
>>> 'History' in book.category # that's the way
False
>>> book.category[u'History'] = True # correct, only assigns the u'History'
>>> book['category'] = {u'History': True} # correct, but overwrites the 'category'
>>>

Project Versions

Table Of Contents

Previous topic

Model Document

Next topic

API Documentation

This Page