Models

Base

class sheraf.models.base.BaseModel[source]

Bases: object

BaseModel is the base class for every other model classes. This is where the attribute reading and writing are handled.

Models can be used as dictionaries:

>>> class Cowboy(sheraf.Model):
...     table = "cowboy"
...     name = sheraf.SimpleAttribute()
...
>>> with sheraf.connection(): 
...     dict(Cowboy.create(name="George Abitbol"))
{'name': 'George Abitbol', '_creation': ...}
assign(**kwargs)[source]

Takes an arbitrary number of keywords arguments, and updates the instance attributes matching the arguments.

This functions recursively calls sheraf.attributes.Attribute.edit() with addition, edition and deletion to True.

>>> class Arm(sheraf.InlineModel):
...     name = sheraf.SimpleAttribute()
...
>>> class Cowboy(sheraf.Model):
...     table = "people"
...     name = sheraf.SimpleAttribute()
...     arms = sheraf.SmallListAttribute(sheraf.InlineModelAttribute(Arm))
...
>>> with sheraf.connection(commit=True):
...     george = Cowboy.create(name="Three arms cowboy", arms=[
...         {"name": "Arm 1"}, {"name": "Arm 2"}, {"name": "Arm 3"},
...     ])
...     len(george.arms)
3
>>> with sheraf.connection(commit=True):
...     len(george.assign(name="George Abitbol", arms=[
...         {"name": "Superarm 1"}, {"name": "Superarm 2"},
...     ]).arms)
2
>>> with sheraf.connection():
...     george.arms[0].name
'Superarm 1'

George passed from 3 arms to only 2 because assign does remove sub instances. If we had called update() instead, George would have his two first arms be renamed superarms but, the third one would not have been removed.

copy(**kwargs)[source]
Parameters:

**kwargs – Keywords arguments will be passed to create() and thus wont be copied.

Returns:

a copy of this instance.

classmethod create(default=None, *args, **kwargs)[source]

Create a model instance.

Parameters:
  • default – The data structure that will be used to store the instance state.

  • **kwargs – Any model attribute can be initialized with the matching keyword.

Returns:

The newly created instance.

>>> class Cowboy(sheraf.Model):
...    table = "cowboy"
...    name = sheraf.SimpleAttribute(default="John Doe")
...
>>> with sheraf.connection(commit=True):
...    cowboy = Cowboy.create()
...    assert "John Doe" == cowboy.name
...
...    cowboy = Cowboy.create(name="George Abitbol")
...    assert "George Abitbol" == cowboy.name
...
...    Cowboy.create(this_attribute_does_not_exist="something") #  raises a TypeError
Traceback (most recent call last):
    ...
TypeError: TypeError: create() got an unexpected keyword argument 'this_attribute_does_not_exist'

The function can also create sub-instances recursively:

>>> class Horse(sheraf.InlineModel):
...     name = sheraf.SimpleAttribute()
...
>>> class Cowboy(sheraf.Model):
...     table = "cowboy"
...     name = sheraf.SimpleAttribute(default="John Doe")
...     horse = sheraf.InlineModelAttribute(Horse)
...
>>> with sheraf.connection():
...     cowboy = Cowboy.create(name="George Abitbol", horse={"name": "Jolly Jumper"})
...     cowboy.horse.name
'Jolly Jumper'
default_mapping

alias of SmallDict

delete()[source]

Delete the current model instance.

>>> class MyModel(sheraf.Model):
...     table = "my_model"
...
>>> with sheraf.connection():
...    m = MyModel.create()
...    assert m == MyModel.read(m.id)
...    m_id = m.id
...    m.delete()
...    m.read(m_id)
Traceback (most recent call last):
    ...
sheraf.exceptions.ModelObjectNotFoundException: Id '...' not found in MyModel
edit(value, addition=True, edition=True, deletion=False, replacement=False, strict=False)[source]

Take a dictionary and a set of options, and try to applies the dictionary values to the instance structure.

Parameters:
  • value – The dictionary containing the values. The dictionary elements that do not match the instance attributes will be ignored.

  • addition – If True, elements present in value and absent from the instance attributes will be added.

  • edition – If True, elements present in both value and the instance will be updated.

  • deletion – If True, elements present in the instance and absent from value will be deleted.

  • replacement – Like edition, but create a new element instead of updating one.

  • strict – If strict is True, every keys in value must be sheraf attributes of the current model. Default is False.

keys()[source]
Returns:

The instance attribute names.

classmethod on_creation(*args, **kwargs)[source]

Decorator for callbacks to call on an instance creation. The callback will be executed before the instance is created. If the callback yields, the part after the yield will be executed after the creation.

The callback should take one attribute that is the model instance.

The callback can be freely named.

>>> class Cowboy(sheraf.Model):
...     table = "cowboy_cb_creation"
...     name = sheraf.StringAttribute()
...
>>>
... @Cowboy.on_creation
... def welcome_new_cowboys(cowboy):
...     yield
...     print(f"Welcome {cowboy.name}!")
...
>>> with sheraf.connection():
...     george = Cowboy.create(name="George Abitbol")
Welcome George Abitbol!
classmethod on_deletion(*args, **kwargs)[source]

Decorator for callbacks to call on an instance deletion. The callback will be executed before the instance is deleted. If the callback yields, the part after the yield will be executed after the creation.

The callback should take one attribute that is the model instance.

The callback can be freely named.

>>> class Cowboy(sheraf.Model):
...     table = "cowboy_cb_creation"
...     name = sheraf.StringAttribute()
...
>>>
... @Cowboy.on_deletion
... def goodby_old_cowboys(cowboy):
...     print(f"So long {cowboy.name}!")
...
>>> with sheraf.connection():
...     george = Cowboy.create(name="George Abitbol")
...     george.delete()
So long George Abitbol!
reset(attribute)[source]

Resets an attribute to its default value.

update(**kwargs)[source]

Takes an arbitrary number of keywords arguments, and updates the instance attributes matching the arguments.

This functions recursively calls sheraf.attributes.Attribute.edit() with addition and edition to True.

>>> class Horse(sheraf.InlineModel):
...     name = sheraf.SimpleAttribute()
...
>>> class Cowboy(sheraf.Model):
...     table = "people"
...     name = sheraf.SimpleAttribute()
...     horse = sheraf.InlineModelAttribute(Horse)
...
>>> with sheraf.connection(commit=True):
...     george = Cowboy.create(name="George", horse={"name": "Centaurus"})
...     george.update(name="*incognito*", horse={"name": "Jolly Jumper"})
...     george.name
'*incognito*'
>>> with sheraf.connection():
...     george.horse.name
'Jolly Jumper'

Note that sub-instances are also edited.

class sheraf.models.base.BaseModelMetaclass(name, bases, attrs)[source]

Bases: type

Internal metaclass. Contains the mapping of attribute names with their corresponding data (of type Attribute)

Indexation mechanism

class sheraf.models.indexation.BaseIndexedModel(*args, **kwargs)[source]

Bases: BaseModel

This class handles the whole indexation mechanism. The mechanisms for reading or iterating over models in the database are handled here.

classmethod all(index_name=None)[source]
Parameters:

index_name – Can be any index name. By default, this is the primary key.

Returns:

A QuerySet containing all the registered models in the given index.

copy(**kwargs)[source]

Copies a model. The attributes carrying an unique index wont be copied, they will be resetted instead.

Parameters:

**kwargs – Keywords arguments will be passed to create() and thus wont be copied.

Returns:

a copy of this instance.

classmethod count(index_name=None)[source]

Counts the number of elements in an index. :param index_name: The name of the index to count. By default

the primary index is used

classmethod filter(predicate=None, **kwargs)[source]

Shortcut for sheraf.queryset.QuerySet.filter().

Returns:

sheraf.queryset.QuerySet

classmethod get(*args, **kwargs)[source]

Shortcut for sheraf.queryset.QuerySet.filter() and sheraf.queryset.QuerySet.get(). Cowboy.get(name="Peter") and Cowboy.filter(name="Peter").get() are equivalent.

Returns:

The instance of the model if the filter matches exactly one instance. Otherwise, it raises a QuerySetUnpackException.

>>> class Cowboy(sheraf.Model):
...     table = "people"
...     name = sheraf.SimpleAttribute()
...     age = sheraf.SimpleAttribute()
...
>>> with sheraf.connection(commit=True):
...     peter = Cowboy.create(name="Peter", age=30)
...     steven = Cowboy.create(name="Steven", age=30)
...     assert peter == Cowboy.get(name="Peter")
...
>>> with sheraf.connection():
...     Cowboy.get()
Traceback (most recent call last):
    ...
sheraf.exceptions.TooManyValuesSetUnpackException: Trying to unpack a QuerySet with multiple elements <QuerySet model=Cowboy>
>>> with sheraf.connection():
...     Cowboy.get(age=30)
Traceback (most recent call last):
    ...
sheraf.exceptions.TooManyValuesSetUnpackException: Trying to unpack a QuerySet with multiple elements <QuerySet model=Cowboy filters=(age=30)>
>>> with sheraf.connection():
...     Cowboy.get(name="Unknown cowboy")
Traceback (most recent call last):
    ...
sheraf.exceptions.EmptyQuerySetUnpackException: Trying to unpack an empty QuerySet
property identifier

The identifier is the value of the primary_key for the current instance. If the primary_key is ‘id’, then the identifier might be an UUID.

index_keys(index_name)[source]

Returns the values generated for a given index. This method is a helper to help debugging custom index_keys() methods.

Parameters:

index_name – The name of the index.

>>> class Horse(sheraf.Model):
...     table = "index_keys_horse"
...     name = sheraf.StringAttribute().index(
...         index_keys_func=lambda name: name.lower()
...     )
...
>>> with sheraf.connection():
...     jolly = Horse.create(name="Jolly Jumper")
...     jolly.index_keys("name")
{'jolly jumper'}
classmethod index_table_rebuild(*args, callback=None, reset=True, start=None, end=None)[source]

Resets a model indexation tables.

This method should be called if an attribute became indexed in an already populated database.

Parameters:
  • *args

    A list of index names to reset. If None, all the indexes will be reseted. The primary index cannot be resetted.

  • callback – A callback that is called each item a model instance is iterated.

  • reset – If True the index tables are deleted before reindexaxtion. Defaults to True.

is_indexed_with(**kwargs)[source]

Checks if a model would be returned if a given query would be done. This method does not make an actual query, and thus should be faster than making a real one.

Params **kwargs**kwargs:

A list of indexes to check.

>>> class Horse(sheraf.Model):
...     table = "is_indexed_with_horse"
...     name = sheraf.StringAttribute().index(
...         index_keys_func=lambda name: name.lower()
...     )
...
>>> with sheraf.connection():
...     jolly = Horse.create(name="Jolly Jumper")
...     jolly.is_indexed_with(name="Jolly JUMPER")
True
classmethod order(*args, **kwargs)[source]

Shortcut for sheraf.queryset.QuerySet.order().

Returns:

sheraf.queryset.QuerySet

classmethod read(*args, **kwargs)[source]

Get a model instance from its identifier. If the model identifier is not valid, a ModelObjectNotFoundException is raised.

The function takes only one parameter which key is the index where to search, and which value is the index identifier. If the index is multiple, a MultipleIndexException is raised. By default the index used is the id index.

Parameters:
  • args – The identifier of the model. There can be only one positionnal or keyword argument.

  • kwargs – The identifier of the model. There can be only one positionnal or keyword argument.

Returns:

The BaseIndexedModel matching the id.

>>> class MyModel(sheraf.Model):
...     table = "my_model"
...     unique = sheraf.SimpleAttribute().index(unique=True)
...     multiple = sheraf.SimpleAttribute().index()
...
>>> with sheraf.connection():
...     m = MyModel.create(unique="A", multiple="B")
...     assert MyModel.read(m.id) == m
...     assert MyModel.read(unique="A") == m
...
>>> with sheraf.connection():
...     MyModel.read("invalid")
Traceback (most recent call last):
    ...
ModelObjectNotFoundException
>>> with sheraf.connection():
...     MyModel.read(multiple="B")
Traceback (most recent call last):
    ...
MultipleIndexException
classmethod read_these(*args, **kwargs)[source]

Get model instances from their identifiers. Unlike read_these(),If an instance identifiers does not exist, a ModelObjectNotFoundException is raised.

The function takes only one parameter which key is the index where to search, and which values are the index identifier. By default the index used is the id index.

Returns:

A generator over the models matching the keys.

>>> class MyModel(sheraf.IntIndexedNamedAttributesModel):
...     table = "my_model"
...
>>> with sheraf.connection():
...     m1 = MyModel.create(id=1)
...     m2 = MyModel.create(id=2)
...
...     assert [m1, m2] == list(MyModel.read_these([m1.id, m2.id]))
...     list(MyModel.read_these(["invalid"]))
Traceback (most recent call last):
    ...
sheraf.exceptions.ModelObjectNotFoundException: Id 'invalid' not found in MyModel, 'id' index
classmethod read_these_valid(*args, **kwargs)[source]

Return model instances from an index. Unlike read_these(), invalid index values are ignored.

The function takes only one parameter which key is the index where to search, and which values are the index identifier. By default the index used is the id index.

Returns:

A generator over the models matching the keys.

>>> class MyModel(sheraf.IntIndexedNamedAttributesModel):
...     table = "my_model"
...
>>> with sheraf.connection():
...     m1 = MyModel.create(id=1)
...     m2 = MyModel.create(id=2)
...
...     assert [m1, m2] == list(MyModel.read_these_valid([m1.id, m2.id]))
...     assert [m1, m2] == list(MyModel.read_these_valid([m1.id, 42, m2.id]))
classmethod search(*args, **kwargs)[source]

Shortcut for sheraf.queryset.QuerySet.search().

Returns:

sheraf.queryset.QuerySet

classmethod search_keys(**kwargs)[source]

Returns the values generated for a given index query. This method is a helper to help debugging custom search_keys() methods.

Parameters:

index_name – The name of the index.

>>> class Horse(sheraf.Model):
...     table = "search_keys_horse"
...     name = sheraf.StringAttribute().index(
...         index_keys_func=lambda name: name.lower()
...     )
...
>>> Horse.search_keys(name="Jolly Jumper")
{'jolly jumper'}
class sheraf.models.indexation.BaseIndexedModelMetaclass(name, bases, attrs)[source]

Bases: BaseModelMetaclass

class sheraf.models.indexation.IndexedModel(*args, **kwargs)[source]

Bases: BaseIndexedModel

IndexedModel are the top-level models in the database. They come with one or several indexes, stored in a table at the root of the database. They must have a table parameter defined and an id attribute.

They can have a database_name attribute. If it is set, then in a default connection context:

  • create() will store the new model instances in this database;

  • read() and all() (etc.) will read in priority in this database, and then in the default database.

  • delete() will try to delete the model from this database, and by default in the default database.

However, if a database_name is explicitly passed to sheraf.databases.connection(), then every action will be performed on this database, ignoring the model database_name attribute.

class sheraf.models.indexation.IndexedModelMetaclass(name, bases, attrs)[source]

Bases: BaseIndexedModelMetaclass

Contains the mapping of tables (name of models) to their corresponding model definitions

class sheraf.models.indexation.SimpleIndexedModel(*args, **kwargs)[source]

Bases: BaseIndexedModel

Model flavors

class sheraf.models.AttributeModel(*args, **kwargs)[source]

Bases: NamedAttributesModel, SimpleIndexedModel

This model is expected to be used with IndexedModelAttribute. Its usage is mainly the same as any BaseIndexedModel.

class sheraf.models.IntIndexedIntAttributesModel(*args, **kwargs)[source]

Bases: IntAttributesModel, IntIndexedModel, IndexedModel

The ids of this models are integers, and the ids of its attributes are also integers.

class sheraf.models.IntIndexedModel[source]

Bases: object

Model using integers as ids.

By default ids are 64bits integers.

>>> class MyIntModel(sheraf.IntIndexedModel):
...     table = "my_int_model"
...
>>> with sheraf.connection():  
...     MyIntModel.create().id
383428472384721983
class sheraf.models.IntIndexedNamedAttributesModel(*args, **kwargs)[source]

Bases: NamedAttributesModel, IntIndexedModel, IndexedModel

The ids of this model are integers, and attributes are named.

class sheraf.models.IntOrderedIndexedModel[source]

Bases: object

Model using increasing integers as ids.

>>> class MyIntModel(sheraf.IntOrderedIndexedModel):
...     table = "my_int_model"
...
>>> with sheraf.connection():  
...     MyIntModel.create().id
...     MyIntModel.create().id
0
1
class sheraf.models.IntOrderedNamedAttributesModel(*args, **kwargs)[source]

Bases: NamedAttributesModel, IntOrderedIndexedModel, IndexedModel

The ids are 64bits integers, distributed ascendently starting at 0.

sheraf.models.Model

alias of UUIDIndexedDatedNamedAttributesModel

class sheraf.models.UUIDIndexedDatedNamedAttributesModel(*args, **kwargs)[source]

Bases: DatedNamedAttributesModel, UUIDIndexedModel, IndexedModel

The ids of this model are UUID4, the attributes are named, and any modification on the model will update its modification datetime.

class sheraf.models.UUIDIndexedModel[source]

Bases: object

Model using uuid4 as ids. Ids are stored as strings.

>>> class MyUUIDModel(sheraf.IntIndexedModel):
...     table = "my_uuid_model"
...
>>> with sheraf.connection():  
...     MyIntModel.create().id
"e4bb714e-b5a8-40d6-bb69-ab3b932fbfe0"
class sheraf.models.UUIDIndexedNamedAttributesModel(*args, **kwargs)[source]

Bases: NamedAttributesModel, UUIDIndexedModel, IndexedModel

The ids of this model are UUID4, and attributes are named.

class sheraf.models.attributes.DatedNamedAttributesModel[source]

Bases: NamedAttributesModel

Model with creation and modification datetimes.

Creation date is automatically saved. It will not change during object life. Date of modification is automatically saved when an attribute is modified and refers to the moment the transaction is committed. At creation time, date of modification and date of creation are equal.

creation_datetime()[source]

The date the object has been created. By now it refers to the date the method create() has been called, and not the date the transaction has been committed.

Returns:

datetime.datetime or None if the object has not been committed yet.

last_update_datetime()[source]

The date of the last transaction commit involving a modification in this object.

Returns:

datetime.datetime or None if the object has not been committed yet.

save()[source]

Updates last_update_datetime() value and saves all the model attributes.

class sheraf.models.attributes.IntAttributesModel[source]

Bases: BaseModel

class sheraf.models.attributes.NamedAttributesModel[source]

Bases: BaseModel

class sheraf.models.inline.InlineModel(**kwargs)[source]

Bases: NamedAttributesModel

InlineModel behaves like a regular model, but it is not indexed by itself. This has several consequences: - The table attribute is not needed. - The read() method is not available. InlineModel aims to be used in combination with InlineModelAttribute.

>>> class Gun(sheraf.InlineModel):
...     nb_bullets = sheraf.IntegerAttribute()
...
>>> class Cowboy(sheraf.Model):
...     table = "cowboy"
...     name = sheraf.SimpleAttribute()
...     gun = sheraf.InlineModelAttribute(Gun)
...
>>> with sheraf.connection(commit=True):
...     george = Cowboy.create(
...         name="George Abitbol",
...         gun=Gun.create(nb_bullets=5),
...     )
...     assert 5 == george.gun.nb_bullets

You can manage your own indexation by combining InlineModelAttribute with a collection attribute, like DictAttribute.

>>> class Gun(sheraf.InlineModel):
...     nb_bullets = sheraf.IntegerAttribute()
...
>>> class Cowboy(sheraf.Model):
...     table = "cowboy"
...     name = sheraf.SimpleAttribute()
...     guns = sheraf.LargeDictAttribute(
...         sheraf.InlineModelAttribute(Gun)
...     )
...
>>> with sheraf.connection(commit=True):
...     george = Cowboy.create(
...         name="George Abitbol",
...         guns = {
...             "martha": Gun.create(nb_bullets=6),
...             "gretta": Gun.create(nb_bullets=5),
...         }
...     )
...     assert 6 == george.guns["martha"].nb_bullets

InlineModel can also be anonymous. To create an anonymous model instance, just pass the attributes list as parameter of the class.

>>> class Cowboy(sheraf.Model):
...     table = "cowboy"
...     name = sheraf.SimpleAttribute()
...     horse = sheraf.InlineModelAttribute(
...         sheraf.InlineModel(
...             name=sheraf.SimpleAttribute()
...         )
...     )
...
>>> with sheraf.connection(commit=True):
...     george = Cowboy.create(horse=dict(name="Jolly Jumper"))
...     george.horse.name
'Jolly Jumper'