Attributes
Base
- class sheraf.attributes.Attribute(default=None, key=None, lazy=True, read_memoization=None, write_memoization=None, store_default_value=True)[source]
Bases:
objectBase 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.Falseby default.write_memoization (
bool) – Whether this attribute should be memoized on write.Trueby 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
Indexobject on the current attribute. It takes the same arguments asIndexexcept 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:
TypedAttributeStore a
boolobject.
- class sheraf.attributes.simples.DateAttribute(default=None, **kwargs)[source]
Bases:
IntegerAttributeStores a
datetime.dateobject.
- class sheraf.attributes.simples.DateTimeAttribute(default=None, key=None, lazy=True, read_memoization=None, write_memoization=None, store_default_value=True)[source]
Bases:
AttributeStore a
datetime.datetimeobject.
- class sheraf.attributes.simples.FloatAttribute(**kwargs)[source]
Bases:
TypedAttributeStores a
floatobject.
- class sheraf.attributes.simples.IntegerAttribute(**kwargs)[source]
Bases:
TypedAttributeStores an
intobject.
- class sheraf.attributes.simples.SimpleAttribute(default=None, key=None, lazy=True, read_memoization=None, write_memoization=None, store_default_value=True)[source]
Bases:
AttributeStore a primitive data.
- class sheraf.attributes.simples.StringAttribute(**kwargs)[source]
Bases:
TypedAttributeStores a
strobject.
- class sheraf.attributes.simples.StringUUIDAttribute(default=None, key=None, lazy=True, read_memoization=None, write_memoization=None, store_default_value=True)[source]
Bases:
UUIDAttributeStores an
uuid.UUIDbut data is handled as a string.
- class sheraf.attributes.simples.TimeAttribute(default=None, **kwargs)[source]
Bases:
IntegerAttributeStores a
datetime.timeobject.
- class sheraf.attributes.simples.TypedAttribute(**kwargs)[source]
Bases:
AttributeStore 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:
AttributeStores 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:
AttributeStores 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:
AttributeTakes an
Enumand an optionalAttribute, filters the data with theEnumand 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
FOOBARdefined 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:
IntegerAttributeCounterAttribute is very like
SimpleAttributewith concurrency-proof operations.It acts as a drop-in replacement for
SimpleAttributewithincrementanddecrementmethods that automatically solve conflicts. It supports all mathematical operations, but only solves conflicts usingincrementanddecrement. 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.incrementordecrementmust 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_typethat is the persistent data structure that will store the data. For instanceListAttributeusually usessheraf.types.SmallListorLargeList.An
accessor_typethat helps handling thepersistent_typewith an interface similar to the native type it refers. For instanceListAttributeusesListAccessorthat behaves like the pythonlist.An optional
attributethat helps theaccessor_typeserialize and deserialize the data stored in thepersistent_type. This allows pairing collections with other kinds of attributes. For example you can easilly handleBloblists 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:
AttributeAttribute 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:
DictAttributeShortcut 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:
ListAttributeShortcut 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:
AttributeAttribute 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
ListAttributeis 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:
TypedAttributeAttribute 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
SetAttributeis 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:
DictAttributeShortcut 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:
ListAttributeShortcut 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,AttributeModelAttributebehaves 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
AttributeModelmust 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,AttributeModelAttributebehaves 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,AttributeThis 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
ModelAttributeare 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:
objectLoads 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,AttributeInverse reference to a
ModelAttribute.- Parameters:
model – The
Modelto refer to.attribute – The
Attributein the model to refer to. This model must be aModelAttributeor 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:
InlineModelBlob objects wrap the raw data from regular files.
ZODB
ZODB.blob.Blobare 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
datais not set, data will be read from thestreamstream.kwargs – optional attributes to set
- Returns:
A sheraf object wrapping a
ZODB.blob.Blobobject, orNoneif 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:
InlineModelAttributeThis attribute stores binary files. It is mainly an interface that handles a
Blobobject.
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:
objectIndexes should be either created as
IndexedModelclass parameters, or with the attributesindex()method.- Parameters:
attributes – The attributes being indexed. They can be either
Attributeor 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
IndexedModelattribute. If theindex()is used, the key is theAttributename.unique (bool) – If the index is unique, and two models have the same value for this model, a
UniqueIndexExceptionis raised when trying to write the second one. Automatically set toTrueif 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
Indexparameters, 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.