Attributes
Base
- class sheraf.attributes.Attribute(default=None, key=None, lazy=True, read_memoization=None, write_memoization=None, store_default_value=True)[source]
Bases:
object
Base type of all attributes of a base model.
- Parameters:
default (a callable object or a simple object) – The value this attribute will be initialized with. If it is a callable object, it will be called on initialization, else it will simply be copied. The callable object can take either no argument, or one argument that will be the parent model.
key – The key to identify the attribute in its parent persistent mapping.
lazy (
bool
) – If True, the objet carried by the attribute is created on the first read or write access. If False, it is created when the model object is created. Default is True.read_memoization (
bool
) – Whether this attribute should be memoized on read.False
by default.write_memoization (
bool
) – Whether this attribute should be memoized on write.True
by default.
When an attribute is memoized, its next reading will not result in a new database access. Attributes: - indexes: a dictionary of Indexes. The key with value None stands for this attribute’s name.
- create(parent)[source]
- Returns:
The attribute’s defined default value. If it is a callable, return the result of calling it instead.
- default(func)[source]
Decorator for a callback to call to generate a default value for the attribute.
>>> class Cowboy(sheraf.Model): ... table = "default_cowboys" ... age = sheraf.IntegerAttribute() ... ... @age.default ... def default_age(self): ... return 42 ... >>> with sheraf.connection(commit=True): ... george = Cowboy.create() ... george.age 42
- index(unique=False, key=None, index_keys_func=None, search_keys_func=None, values=None, search=None, mapping=None, primary=False, nullok=None, noneok=None)[source]
This method is a shortcut that inits a
Index
object on the current attribute. It takes the same arguments asIndex
except that by default:the index name will be this attribute name;
the mapping parameter will be this attribute default_index_mapping parameter;
the index_keys_func parameter will be this attribute
index_keys()
method;the search_keys_func parameter will be this attribute
search_keys()
method;the noneok parameter will be this attribute noneok parameter
the nullok parameter will be this attribute nullok parameter
- index_keys(value)[source]
The default transformation that will be applied when storing data if this attribute is indexed but the
index()
values_func parameter is not provided.By default no transformation is applied, and the value parameter is returned in a
set
.This method can be overload so a custom transformation is applied.
- index_keys_func(*args, **kwargs)[source]
Shortcut for
index_keys_func()
for the index that has the same name than this attribute.
- is_created(parent)[source]
- Parameters:
parent – The owner of this attribute
- Returns:
true if the object is effectively created
- on_change(*args, **kwargs)[source]
Shortcut for
on_creation()
,on_edition()
andon_deletion()
at the same time.
- on_creation(*args, **kwargs)[source]
Decorator for callbacks to call on an attribute creation. The callback will be executed before the attribute is created. If the callback yields, the part after the yield will be executed after the creation.
The callback will be passed the new value of the attribute.
The callback can be freely named.
>>> class Cowboy(sheraf.Model): ... table = "old_cowboys_creation" ... age = sheraf.IntegerAttribute() ... ... @age.on_creation ... def create_age(self, new): ... print("New cowboy aged of", new) ... >>> with sheraf.connection(): ... george = Cowboy.create(age=50) New cowboy aged of 50
- on_deletion(*args, **kwargs)[source]
Decorator for callbacks to call on an attribute deletion. The callback will be executed before the attribute is deleted. If the callback yields, the part after the yield will be executed after the deletion.
The callback will be passed the old value of the attribute.
The callback can be freely named.
>>> class Cowboy(sheraf.Model): ... table = "old_cowboys_deletion" ... age = sheraf.IntegerAttribute() ... ... @age.on_deletion ... def delete_age(self, old): ... print("Deleting age of", 50) ... >>> with sheraf.connection(): ... george = Cowboy.create(age=50) ... del george.age Deleting age of 50
- on_edition(*args, **kwargs)[source]
Decorator for callbacks to call on an attribute edition. The callback will be executed before the attribute is edited. If the callback yields, the part after the yield will be executed after the update.
The callback will be passed the old and the new value of the attribute.
The callback can be freely named.
>>> class Cowboy(sheraf.Model): ... table = "old_cowboys_edition" ... age = sheraf.IntegerAttribute() ... ... @age.on_edition ... def update_age(self, new, old): ... print("I was", old, "years old") ... yield ... print("Now I am", new, "years old") ... >>> with sheraf.connection(): ... george = Cowboy.create(age=50) ... george.age = 51 I was 50 years old Now I am 51 years old
- search_keys(value)[source]
The default transformation that will be applied when searching for data if this attribute is indexed but the
index()
search_keys_func parameter is not provided.By default this calls
values()
.This method can be overload so a custom transformation is applied.
- search_keys_func(*args, **kwargs)[source]
Shortcut for
search_keys_func()
for the index that has the same name than this attribute.
- update(old_value, new_value, addition=True, edition=True, deletion=False, replacement=False)[source]
Updates the value of the attribute.
- Parameters:
old_value – The previous value the attribute had.
new_value – The new value that the attribute should be updated with.
addition – On collections, adds elements to the attribute if they are present in new_value and not in old_value, defaults to True.
edition – On collections, edits the attribute element if present on both old_value and new_value, defaults to True.
deletion – On collections, removes an element if present in old_value and absent from new_value, default to False.
replacement – Replace sub-collections or sub-models instead of editing them in-place, defaults to False.
Simple attributes
- class sheraf.attributes.simples.BooleanAttribute(**kwargs)[source]
Bases:
TypedAttribute
Store a
bool
object.
- class sheraf.attributes.simples.DateAttribute(default=None, **kwargs)[source]
Bases:
IntegerAttribute
Stores a
datetime.date
object.
- class sheraf.attributes.simples.DateTimeAttribute(default=None, key=None, lazy=True, read_memoization=None, write_memoization=None, store_default_value=True)[source]
Bases:
Attribute
Store a
datetime.datetime
object.
- class sheraf.attributes.simples.FloatAttribute(**kwargs)[source]
Bases:
TypedAttribute
Stores a
float
object.
- class sheraf.attributes.simples.IntegerAttribute(**kwargs)[source]
Bases:
TypedAttribute
Stores an
int
object.
- class sheraf.attributes.simples.SimpleAttribute(default=None, key=None, lazy=True, read_memoization=None, write_memoization=None, store_default_value=True)[source]
Bases:
Attribute
Store a primitive data.
- class sheraf.attributes.simples.StringAttribute(**kwargs)[source]
Bases:
TypedAttribute
Stores a
str
object.
- class sheraf.attributes.simples.StringUUIDAttribute(default=None, key=None, lazy=True, read_memoization=None, write_memoization=None, store_default_value=True)[source]
Bases:
UUIDAttribute
Stores an
uuid.UUID
but data is handled as a string.
- class sheraf.attributes.simples.TimeAttribute(default=None, **kwargs)[source]
Bases:
IntegerAttribute
Stores a
datetime.time
object.
- class sheraf.attributes.simples.TypedAttribute(**kwargs)[source]
Bases:
Attribute
Store a persistent dict of primitive data.
Keys and values can be
str
,int
,float
.If the value set is
None
, it will not be casted.
- class sheraf.attributes.simples.UUIDAttribute(default=None, key=None, lazy=True, read_memoization=None, write_memoization=None, store_default_value=True)[source]
Bases:
Attribute
Stores an
uuid.UUID
.- index_keys(value)[source]
The default transformation that will be applied when storing data if this attribute is indexed but the
index()
values_func parameter is not provided.By default no transformation is applied, and the value parameter is returned in a
set
.This method can be overload so a custom transformation is applied.
- class sheraf.attributes.password.PasswordAttribute(**kwargs)[source]
Bases:
Attribute
Stores crypted password in the database. Once a password has been crypted, it cannot be read back in plain text, however, comparisions are still possible. Under the hood, the python
crypt()
method is used.The arguments passed to this attribute are passed to
crypt.mksalt()
.>>> class Cowboy(sheraf.Model): ... table = "cautious_cowboy" ... email = sheraf.StringAttribute() ... password = sheraf.PasswordAttribute() ... >>> with sheraf.connection(commit=True): ... cowboy = Cowboy.create(email="george@abitbol.com", password="$up3r$3cur3") ... assert cowboy.password == "$up3r$3cur3" ... str(cowboy.password) '$6$vQzraMQc3G9Mf/zy$IfPSCPO81IzvGwo6AYoS6K6fen552B22DDz.ARwoxmvyFJ9He7.wVLIWbw0RWEIw/oGUblY9YbGvhwUQbtYEV.'
- class sheraf.attributes.enum.EnumAttribute(enum, attribute=None, **kwargs)[source]
Bases:
Attribute
Takes an
Enum
and an optionalAttribute
, filters the data with theEnum
and serialize it with the methods of theAttribute
.>>> import enum ... >>> class Cowboy(sheraf.Model): ... table = "enum_cowboys" ... ... class Status(enum.IntEnum): ... FARMER = 0 ... COWBOY = 1 ... SHERIF = 2 ... ... status = sheraf.EnumAttribute(Status, sheraf.IntegerAttribute()) ... >>> with sheraf.connection(commit=True): ... george = Cowboy.create(status=Cowboy.Status.SHERIF) ... ... assert george.status == 2 ... assert george.status == Cowboy.Status.SHERIF
Note
For every value
FOOBAR
defined in the enum, the attributes defines an accessoris_foobar
.>>> with sheraf.connection(commit=True): ... assert george.status.is_sherif ... assert not george.status.is_farmer
- class sheraf.attributes.counter.CounterAttribute(default=0, **kwargs)[source]
Bases:
IntegerAttribute
CounterAttribute is very like
SimpleAttribute
with concurrency-proof operations.It acts as a drop-in replacement for
SimpleAttribute
withincrement
anddecrement
methods that automatically solve conflicts. It supports all mathematical operations, but only solves conflicts usingincrement
anddecrement
. Every other destructive operation are sensible to conflicts.>>> class MyModel(sheraf.Model): ... table = "mymodel" ... # You can just replace SimpleAttribute or IntegerAttribute with CounterAttribute ... counter = sheraf.CounterAttribute() # initialized with 0 ... >>> with sheraf.connection(commit=True): ... m = MyModel.create() ... m.counter.increment(1) ... m.counter.decrement(1)
Here is how to produce a conflict case, and how CounterAttributes behaves to solve it:
>>> with sheraf.connection(commit=True): ... sheraf.Database.get().nestable = True ... m1 = MyModel.read(m.id) ... ... with sheraf.connection(commit=True): ... m2 = MyModel.read(m.id) ... m2.counter.decrement(10) ... ... m1.counter.increment(100) ... >>> with sheraf.connection(): ... m3 = MyModel.read(m.id) ... assert 90 == m3.counter
The conflict resolution understands that a transaction is adding 100 and another transaction is substracting 10 to the counter at the same time, and finally adds 90. The formula used is: new_state_a + new_state_b - old_state where old_state is 0 as it was the previous value registered in the database, and new_state_a and new_state_b are the conflicting new values (here -10 and 100).
Using
+=
and-=
operators would have raised a conflicts.increment
ordecrement
must be called explicitely.>>> with sheraf.connection(commit=True): ... sheraf.Database.get().nestable = True ... m1 = MyModel.read(m.id) ... ... with sheraf.connection(commit=True): ... m2 = MyModel.read(m.id) ... m2.counter -= 10 ... ... m1.counter += 100 Traceback (most recent call last): ... ZODB.POSException.ConflictError: database conflict error ...
Regular assignments and operations on the counter also raise conflicts for automatic conflict resolution.
>>> with sheraf.connection(commit=True): ... sheraf.Database.get().nestable = True ... m1 = MyModel.read(m.id) ... ... with sheraf.connection(commit=True): ... m2 = MyModel.read(m.id) ... m2.counter = 10 ... ... m1.counter.increment(100) Traceback (most recent call last): ... ZODB.POSException.ConflictError: database conflict error ...
Collection attributes
Collection attributes are attributes that behave like native python
collections such as dict
, list
or set
. They usually
combine different objects:
A
persistent_type
that is the persistent data structure that will store the data. For instanceListAttribute
usually usessheraf.types.SmallList
orLargeList
.An
accessor_type
that helps handling thepersistent_type
with an interface similar to the native type it refers. For instanceListAttribute
usesListAccessor
that behaves like the pythonlist
.An optional
attribute
that helps theaccessor_type
serialize and deserialize the data stored in thepersistent_type
. This allows pairing collections with other kinds of attributes. For example you can easilly handleBlob
lists or dictionaries ofInlineModelAttributes
.
>>> class Horse(sheraf.InlineModel):
... table = "horse"
... name = sheraf.SimpleAttribute()
...
>>> class Cowboy(sheraf.Model):
... table = "cowboy"
... name = sheraf.SimpleAttribute()
... favorite_numbers = sheraf.SmallListAttribute(
... sheraf.IntegerAttribute(),
... )
... horses = sheraf.LargeDictAttribute(
... sheraf.InlineModelAttribute(Horse),
... )
...
>>> with sheraf.connection(commit=True):
... george = Cowboy.create(
... name="George Abitbol",
... favorite_numbers=[1, 13, 21, 34],
... horses = {
... "first": {"name": "Jolly Jumper"},
... "second": {"name": "Polly Pumper"},
... },
... )
...
... assert 21 in george.favorite_numbers
... assert "Jolly Jumper" == george.horses["first"].name
You can also nest collections as you like, and play for instance with
DictAttribute
or
ListAttribute
.
>>> class Cowboy(sheraf.Model):
... table = "cowboy"
... name = sheraf.SimpleAttribute()
... dice_results = sheraf.LargeDictAttribute(
... sheraf.SmallListAttribute(
... sheraf.IntegerAttribute()
... )
... )
...
>>> with sheraf.connection(commit=True):
... george = Cowboy.create(
... name="George Abitbol",
... dice_results={
... "monday": [2, 6, 4],
... "tuesday": [1, 1, 3],
... }
... )
... assert 6 == george.dice_results["monday"][1]
- class sheraf.attributes.collections.DictAttribute(attribute=None, persistent_type=None, accessor_type=<class 'sheraf.attributes.collections.DictAttributeAccessor'>, **kwargs)[source]
Bases:
Attribute
Attribute mimicking the behavior of
dict
.>>> class Gun(sheraf.InlineModel): ... nb_amno = 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={ ... "rita": {"nb_amno": 6}, ... "carlotta": {"nb_amno": 5}, ... } ... ) ... ... assert george.guns["rita"].nb_amno == 6 ... for gun in george.guns.values(): ... assert gun.nb_amno >= 5
- update(old_value, new_value, addition=True, edition=True, deletion=False, replacement=False)[source]
Updates the value of the attribute.
- Parameters:
old_value – The previous value the attribute had.
new_value – The new value that the attribute should be updated with.
addition – On collections, adds elements to the attribute if they are present in new_value and not in old_value, defaults to True.
edition – On collections, edits the attribute element if present on both old_value and new_value, defaults to True.
deletion – On collections, removes an element if present in old_value and absent from new_value, default to False.
replacement – Replace sub-collections or sub-models instead of editing them in-place, defaults to False.
- class sheraf.attributes.collections.LargeDictAttribute(attribute=None, persistent_type=None, accessor_type=<class 'sheraf.attributes.collections.DictAttributeAccessor'>, **kwargs)[source]
Bases:
DictAttribute
Shortcut for
DictAttribute(persistent_type=LargeDict)
- class sheraf.attributes.collections.LargeListAttribute(attribute=None, persistent_type=None, accessor_type=<class 'sheraf.attributes.collections.ListAttributeAccessor'>, **kwargs)[source]
Bases:
ListAttribute
Shortcut for
ListAttribute(persistent_type=LargeList)
.
- class sheraf.attributes.collections.ListAttribute(attribute=None, persistent_type=None, accessor_type=<class 'sheraf.attributes.collections.ListAttributeAccessor'>, **kwargs)[source]
Bases:
Attribute
Attribute mimicking the behavior of
list
.>>> class Cowboy(sheraf.Model): ... table = "cowboy" ... name = sheraf.SimpleAttribute() ... faxes = sheraf.LargeListAttribute( ... sheraf.BlobAttribute() ... ) ... >>> with sheraf.connection(): ... george = Cowboy.create( ... name="George Abitbol", ... faxes=[ ... sheraf.Blob.create(filename="peter1.txt", data=b"Can you give me my pin's back please?"), ... ], ... ) ... ... george.faxes.append(sheraf.Blob.create(filename="peter2.txt", data=b"Hey! Did you receive my last fax?")) ... assert "peter1.txt" == george.faxes[0].original_name ... assert b"fax" in george.faxes[1].data ...
- index_keys(list_)[source]
By default, every items in a
ListAttribute
is indexed.>>> class Cowboy(sheraf.Model): ... table = "cowboy" ... favorite_colors = sheraf.LargeListAttribute( ... sheraf.StringAttribute() ... ).index() ... >>> with sheraf.connection(): ... george = Cowboy.create(favorite_colors=[ ... "red", "green", "blue", ... ]) ... assert george in Cowboy.search(favorite_colors="red") ... assert george in Cowboy.search(favorite_colors="blue") ... assert george not in Cowboy.search(favorite_colors="yellow")
Warning
A current limitation is that indexed lists won’t update their indexes if they are edited through their accessor. For the indexes to be updated, the whole list must be re-assigned.
>>> with sheraf.connection(): ... george.favorite_colors.append("purple") ... george in Cowboy.search(favorite_colors="purple") False >>> with sheraf.connection(): ... george.favorite_colors = list(george.favorite_colors) + ["brown"] ... george in Cowboy.search(favorite_colors="brown") True
- search_keys(value)[source]
The default transformation that will be applied when searching for data if this attribute is indexed but the
index()
search_keys_func parameter is not provided.By default this calls
values()
.This method can be overload so a custom transformation is applied.
- update(old_value, new_value, addition=True, edition=True, deletion=False, replacement=False)[source]
Updates the value of the attribute.
- Parameters:
old_value – The previous value the attribute had.
new_value – The new value that the attribute should be updated with.
addition – On collections, adds elements to the attribute if they are present in new_value and not in old_value, defaults to True.
edition – On collections, edits the attribute element if present on both old_value and new_value, defaults to True.
deletion – On collections, removes an element if present in old_value and absent from new_value, default to False.
replacement – Replace sub-collections or sub-models instead of editing them in-place, defaults to False.
- class sheraf.attributes.collections.SetAttribute(attribute=None, persistent_type=None, accessor_type=<class 'sheraf.attributes.collections.SetAttributeAccessor'>, **kwargs)[source]
Bases:
TypedAttribute
Attribute mimicking the behavior of
set
.>>> class Cowboy(sheraf.Model): ... table = "cowboy" ... name = sheraf.SimpleAttribute() ... favorite_numbers = sheraf.SetAttribute( ... sheraf.IntegerAttribute() ... ) ... >>> with sheraf.connection(commit=True): ... george = Cowboy.create( ... name="George Abitbol", ... favorite_numbers={1, 8, 13} ... ) ... ... assert 13 in george.favorite_numbers ... george.favorite_numbers.add(8) ... assert {1, 8, 13} == set(george.favorite_numbers)
- index_keys(set_)[source]
By default, every items in a
SetAttribute
is indexed.>>> class Cowboy(sheraf.Model): ... table = "cowboy" ... favorite_colors = sheraf.SetAttribute( ... sheraf.StringAttribute() ... ).index() ... >>> with sheraf.connection(): ... george = Cowboy.create(favorite_colors={ ... "red", "green", "blue", ... }) ... assert george in Cowboy.search(favorite_colors="red") ... assert george in Cowboy.search(favorite_colors="blue") ... assert george not in Cowboy.search(favorite_colors="yellow")
Warning
A current limitation is that indexed sets won’t update their indexes if they are edited through their accessor. For the indexes to be updated, the whole set must be re-assigned.
>>> with sheraf.connection(): ... george.favorite_colors.add("purple") ... george in Cowboy.search(favorite_colors="purple") False >>> with sheraf.connection(): ... george.favorite_colors = set(george.favorite_colors) | {"brown"} ... george in Cowboy.search(favorite_colors="brown") True
- search_keys(value)[source]
The default transformation that will be applied when searching for data if this attribute is indexed but the
index()
search_keys_func parameter is not provided.By default this calls
values()
.This method can be overload so a custom transformation is applied.
- update(old_value, new_value, addition=True, edition=True, deletion=False, replacement=False)[source]
Updates the value of the attribute.
- Parameters:
old_value – The previous value the attribute had.
new_value – The new value that the attribute should be updated with.
addition – On collections, adds elements to the attribute if they are present in new_value and not in old_value, defaults to True.
edition – On collections, edits the attribute element if present on both old_value and new_value, defaults to True.
deletion – On collections, removes an element if present in old_value and absent from new_value, default to False.
replacement – Replace sub-collections or sub-models instead of editing them in-place, defaults to False.
- class sheraf.attributes.collections.SmallDictAttribute(attribute=None, persistent_type=None, accessor_type=<class 'sheraf.attributes.collections.DictAttributeAccessor'>, **kwargs)[source]
Bases:
DictAttribute
Shortcut for
DictAttribute(persistent_type=SmallDict)
- class sheraf.attributes.collections.SmallListAttribute(attribute=None, persistent_type=None, accessor_type=<class 'sheraf.attributes.collections.ListAttributeAccessor'>, **kwargs)[source]
Bases:
ListAttribute
Shortcut for
ListAttribute(persistent_type=SmallList)
.- persistent_type
alias of
PersistentList
Model attributes
- class sheraf.attributes.models.AttributeLoader(attribute=None, **kwargs)[source]
Bases:
ModelLoader
- class sheraf.attributes.models.IndexedModelAttribute(*args, **kwargs)[source]
Bases:
ModelLoader
,Attribute
ModelAttribute
behaves like a classic model, including the indexation capabilities. The child attribute mapping and all the index mappings are is stored in the parent mapping.- Parameters:
model (
AttributeModel
) – The model type to store.
Note
The
AttributeModel
must have a primary index.>>> class Horse(sheraf.AttributeModel): ... name = sheraf.StringAttribute().index(primary=True) ... size = sheraf.IntegerAttribute().index() ... >>> class Cowboy(sheraf.Model): ... table = "cowboy_indexer" ... name = sheraf.StringAttribute() ... horses = sheraf.IndexedModelAttribute(Horse) ... >>> with sheraf.connection(commit=True): ... george = Cowboy.create(name="George Abitbol") ... jolly = george.horses.create(name="Jolly Jumper", size=32) ... ... assert jolly == george.horses.read("Jolly Jumper") ... assert jolly in george.horses.search(size=32)
- index_keys(model)[source]
The default transformation that will be applied when storing data if this attribute is indexed but the
index()
values_func parameter is not provided.By default no transformation is applied, and the value parameter is returned in a
set
.This method can be overload so a custom transformation is applied.
- class sheraf.attributes.models.InlineModelAttribute(model=None, **kwargs)[source]
Bases:
ModelLoader
,Attribute
ModelAttribute
behaves like a basic model (i.e. have no indexation capability). The child attribute mapping is stored in the parent mapping.- Parameters:
model (
InlineModel
) – The model type to store.
>>> class Horse(sheraf.InlineModel): ... name = sheraf.StringAttribute() ... >>> class Cowboy(sheraf.Model): ... table = "cowboy_inliner" ... name = sheraf.StringAttribute() ... horse = sheraf.InlineModelAttribute(Horse) ... >>> with sheraf.connection(commit=True): ... jolly = Horse.create(name="Jolly Jumper") ... george = Cowboy.create(name="George", horse=jolly) ... george.horse.name 'Jolly Jumper'
- update(old_value, new_value, addition=True, edition=True, deletion=False, replacement=False)[source]
Updates the value of the attribute.
- Parameters:
old_value – The previous value the attribute had.
new_value – The new value that the attribute should be updated with.
addition – On collections, adds elements to the attribute if they are present in new_value and not in old_value, defaults to True.
edition – On collections, edits the attribute element if present on both old_value and new_value, defaults to True.
deletion – On collections, removes an element if present in old_value and absent from new_value, default to False.
replacement – Replace sub-collections or sub-models instead of editing them in-place, defaults to False.
- class sheraf.attributes.models.ModelAttribute(model=None, **kwargs)[source]
Bases:
ModelLoader
,Attribute
This attribute references another
Model
.>>> class Horse(sheraf.Model): ... table = "horse" ... name = sheraf.SimpleAttribute() ... >>> class Cowboy(sheraf.Model): ... table = "cowboy" ... name = sheraf.SimpleAttribute() ... mount = sheraf.ModelAttribute(Horse) ... >>> with sheraf.connection(commit=True): ... jolly = Horse.create(name="Jolly Jumper") ... george = Cowboy.create(name="George Abitbol", mount=jolly) ... ... george.mount.name 'Jolly Jumper'
The referenced model can be dynamically created if its structure is passed through as a dict:
>>> with sheraf.connection(commit=True): ... peter = Cowboy.create(name="Peter", mount={"name": "Polly Pumper"}) ... assert isinstance(peter.mount, Horse) ... peter.mount.name 'Polly Pumper'
When the referenced model is deleted, the value of the attribute becomes
None
.>>> with sheraf.connection(commit=True): ... george = Cowboy.read(george.id) ... jolly.delete() ... assert george.mount is None
Several model classes can be used, but this will be more memory consuming in the database.
>>> class Pony(sheraf.Model): ... table = "pony" ... name = sheraf.SimpleAttribute() ... >>> class Cowboy(sheraf.Model): ... table = "cowboy" ... name = sheraf.SimpleAttribute() ... mount = sheraf.ModelAttribute((Horse, Pony)) ... >>> with sheraf.connection(commit=True): ... superpony = Pony.create(name="Superpony") ... peter = Cowboy.create(name="Peter", mount=superpony)
When several models are set, the first one is considered to be the default model. The default model is used when there is a doubt on the read data, or in the case of model creation with a dict.
- index_keys(model)[source]
By default
ModelAttribute
are indexed on their identifier.
- update(old_value, new_value, addition=True, edition=True, deletion=False, replacement=False)[source]
Updates the value of the attribute.
- Parameters:
old_value – The previous value the attribute had.
new_value – The new value that the attribute should be updated with.
addition – On collections, adds elements to the attribute if they are present in new_value and not in old_value, defaults to True.
edition – On collections, edits the attribute element if present on both old_value and new_value, defaults to True.
deletion – On collections, removes an element if present in old_value and absent from new_value, default to False.
replacement – Replace sub-collections or sub-models instead of editing them in-place, defaults to False.
- class sheraf.attributes.models.ModelLoader(model=None, **kwargs)[source]
Bases:
object
Loads models from the base in a cache.
Inherited by most model types (
Model[Attribute|List|Set|(Large)Dict]
)
- class sheraf.attributes.models.ReverseModelAttribute(model, attribute, **kwargs)[source]
Bases:
AttributeLoader
,Attribute
Inverse reference to a
ModelAttribute
.- Parameters:
model – The
Model
to refer to.attribute – The
Attribute
in the model to refer to. This model must be aModelAttribute
or a collection ofModelAttribute
.
The referenced attribute must be indexed.
>>> class Cowboy(sheraf.Model): ... table = "reverse_cowboys" ... name = sheraf.StringAttribute() ... horse = sheraf.ModelAttribute("Horse").index() ... >>> class Horse(sheraf.Model): ... table = "reverse_horses" ... name = sheraf.StringAttribute() ... cowboy = sheraf.ReverseModelAttribute("Cowboy", "horse") ... >>> with sheraf.connection(): ... george = Cowboy.create(name="George") ... horse = Horse.create(name="Jolly", cowboy=george) ... george.horse.name "Jolly"
Collection attributes are also supported:
>>> class Cowboy(sheraf.Model): ... table = "reverse_multicowboys" ... name = sheraf.StringAttribute() ... horses = sheraf.LargeListAttribute(ModelAttribute("Horse").index()) ... >>> class Horse(sheraf.Model): ... table = "reverse_multihorses" ... name = sheraf.StringAttribute() ... cowboy = sheraf.ReverseModelAttribute("Cowboy", "horses") ... >>> with sheraf.connection(): ... george = Cowboy.create(name="George") ... jolly = Horse.create(name="Jolly", cowboy=george) ... polly = Horse.create(name="Polly", cowboy=george) ... george.horses[0].name ... george.horses[1].name "Jolly" "Polly"
File attributes
- class sheraf.attributes.blobs.Blob(**kwargs)[source]
Bases:
InlineModel
Blob objects wrap the raw data from regular files.
ZODB
ZODB.blob.Blob
are used to store the data.- classmethod create(data=None, filename=None, stream=None, **kwargs)[source]
- Parameters:
data – the data to store in the blob
filename – the name of the original file
stream – If
data
is not set, data will be read from thestream
stream.kwargs – optional attributes to set
- Returns:
A sheraf object wrapping a
ZODB.blob.Blob
object, orNone
if this is an empty and unnamed file.
- property data
The file binary data.
- edit(value, addition=True, edition=True, deletion=False, replacement=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.
- property file_extension
The original filename extension.
- property filename
The name of the blob file.
- property original_name
The original filename.
- class sheraf.attributes.blobs.BlobAttribute(model=<class 'sheraf.attributes.blobs.Blob'>, **kwargs)[source]
Bases:
InlineModelAttribute
This attribute stores binary files. It is mainly an interface that handles a
Blob
object.
Index
- class sheraf.attributes.index.Index(*attributes, unique=False, key=None, index_keys_func=None, search_keys_func=None, values=None, search=None, mapping=None, primary=False, nullok=None, noneok=None, auto=True)[source]
Bases:
object
Indexes should be either created as
IndexedModel
class parameters, or with the attributesindex()
method.- Parameters:
attributes – The attributes being indexed. They can be either
Attribute
or strings representing the attributes names in the model.key – The key the index will use. By default, it takes the name it has as a
IndexedModel
attribute. If theindex()
is used, the key is theAttribute
name.unique (bool) – If the index is unique, and two models have the same value for this model, a
UniqueIndexException
is raised when trying to write the second one. Automatically set toTrue
if primary isTrue
.index_keys_func – A callable that takes the current attribute value and returns a single key, or a collection of keys, where the model instance will be indexed. The keys will be regenerated each time this attribute will be edited, and the index will be updated accordingly. It may take time if the generated collection is large. By default the attribute
index_keys()
method is applied.search_keys_func – A callable that takes some raw data and returns a collection of keys to search in the index. By default, the
search_keys_func()
method is used.mapping – The mapping object to be used to store the indexed values. By default the index_mapping class attribute is used. If you know that the keys of the index will always be a specific type, like integers, you might way to use this attribute to use specialized datastructures.
nullok – If True, None or empty values can be indexed. True by default.
noneok – Ignored in if nullok is True. Else, if noneok is True, None values can be indexed. False by default.”
auto – Defaults to True, enable the automatic index update. When set to False the index won’t be updated when the attributes are updated.
>>> class People(sheraf.Model): ... table = "index_people" ... ... # Simple indexing ... name = sheraf.SimpleAttribute() ... nameindex = sheraf.Index("name") ... ... # Indexing with the .index() shortcut ... size = sheraf.IntegerAttribute().index() ... ... # Emails can only be owned once ... email = sheraf.SimpleAttribute().index(unique=True) ... ... # Indexing people by their decade ... age = sheraf.SimpleAttribute().index(key="decade", index_keys_func=lambda age: {age // 10}) ... >>> with sheraf.connection(commit=True): ... m = People.create( ... name="George Abitbol", ... size=180, ... email="george@abitbol.com", ... age=55, ... ) ... >>> with sheraf.connection(): ... assert [m] == People.filter(nameindex="George Abitbol") ... assert [m] == People.filter(size=180) ... assert [m] == People.filter(decade=5) ... >>> with sheraf.connection(): ... People.create(name="Peter", size=175, email="george@abitbol.com", age=35) Traceback (most recent call last): ... UniqueIndexException
- index_keys_func(*args, **kwargs)[source]
This decorator sets a index_keys method, for one, several or all of the index attributes.
If no positionnal argument is passed, then this sets a default index_keys function for the index.
You can pass one or several attributes or attributes names to set a specific index_keys method for those attributes.
If you do set a specific index_keys method for one or several attributes, the decorated function will be given the attribute index_keys as positionnal arguments, in the order they were passed to the decorator.
The decorated function must return a single key, or a collection of keys, where the index will store the current model instance. Depending on the noneok and nullok
Index
parameters, None and falsy index keys might be ignored.>>> class Cowboy(sheraf.Model): ... table = "any_values_table" ... first_name = sheraf.StringAttribute() ... last_name = sheraf.StringAttribute() ... surname = sheraf.StringAttribute() ... name_parts = sheraf.Index(first_name, last_name, surname) ... ... @name_parts.index_keys_func ... def default_name_indexation(self, values): ... return values.lower() ... ... @name_parts.index_keys_func(first_name, "last_name") ... def full_name(self, first, last): ... return f"{first} {last}".lower() ... >>> with sheraf.connection(): ... m = Cowboy.create(first_name="George", last_name="Abitbol", surname="Georgy") ... assert m in Cowboy.search(name_parts="george abitbol") ... assert m in Cowboy.search(name_parts="georgy")
Note
You can use
index_keys()
to check the index keys your custom function generates.
- search_keys_func(*args, **kwargs)[source]
This decorator sets the search_keys method for the index. It should return a single key to search in the index, or a collection of keys to search in the index. If it returns an indexed collection, by default the model instances will be returned in the order the index keys will be iterated.
>>> class Model(sheraf.Model): ... table = "any_search_table" ... foo = sheraf.StringAttribute() ... bar = sheraf.StringAttribute() ... theindex = sheraf.Index(foo, bar) ... ... @theindex.search_keys_func ... def the_search_method(self, values): ... return values.lower() ... >>> with sheraf.connection(): ... m = Model.create(foo="foo", bar="bar") ... assert m in Model.search(theindex="foo") ... assert m in Model.search(theindex="BAR")
Note
You can use
search_keys()
to check the index keys your custom function generates.