Entities¶
How? Simple, inherit from Entity and define some properties. Define a property with its type and possibly an extra string that gets included in the ‘CREATE TABLE’ statement. Example:
class User(Entity):
name = Property(str)
mail = Property(str, sql_extra="UNIQUE")
(In what follows I will often skip properties that are not essential to exemplify the topic at hand.)
Keys¶
If you try the above example, sparrow will complain, because there is no key. An Entity class
can only have 1 key, and you define it with key. So if we for example want to have a UID
attribute and define it as key, we would do:
class User(Entity):
name = Property(str)
UID = Property(int)
key = Key(UID)
However, this would be irritating. We would have to come up with a unique UID every time we
create a User. To solve this problem, we have a KeyProperty. This will use Postgres’ SERIAL
type to create the property. Use it like this:
class User(Entity):
name = Property(str)
UID = KeyProperty()
key = UID
We can shorten this even further:
class User(Entity):
name = Property(str)
key = UID = KeyProperty()
Note that for a KeyProperty, its value will be None until you insert the object into
the database (which will provide the value for it).
A Key can also contain multiple properties:
class User(Entity):
firstname = Property(str)
lastname = Property(str)
key = Key(firstname, lastname)
You can always use a key attribute of an object as if it was an entity:
u = User(...)
u.key = ("Evert", "Heylen")
In case of multiple properties, you need to put it in a tuple. Otherwise (also in the case of a
KeyProperty, it has to be a simple value.
Constraints¶
There are two types of constraints: constraints for properties and object-wide constraints.
The latter is only checked when calling __init__, update and insert. An example:
class User(Entity):
name = Property(str)
password = Property(str, constraint=lambda p: len(p) >= 8) # Password of minimum length
constraint = lambda u: u.name != u.password # Don't use your name as password
References¶
Often, you want to keep a reference to other objects. In sparrow, you use a Reference to do so.
A Reference will automatically create properties to fully save a key of another Entity class.
Note that a Reference can not be constrained, but it can be used in a Key.:
class User(Entity):
firstname = Property(str)
lastname = Property(str)
key = Key(firstname, lastname)
class Message(Entity):
msg = Property(str)
to = Reference(User)
from = Reference(User)
In this case, the table created for Message will have 5 attributes: msg, to_firstname,
to_lastname, from_firstname and from_lastname. It will also be constrained (in the DB) so
so that always refers to a User in the database. However, you should not set these attributes
directly, you should rather use to and from as if it were properties.:
>>> msg = Message(msg="Test", to=some_user.key, from=other_user.key)
>>> # Or after initializing
>>> msg.to = another_user.key
Remember to always refer to the key of an object, not the object itself.
JSON¶
To get a JSON representation, simply call obj.to_json(). Some options are available to change
this output. You can override json_repr, which has to return some datatype that is convertible
to JSON. By default, it returns a dictionary of all properties. This too you can control:
class User(Entity):
name = Property(str)
password = Property(str, json=False) # We don't want to send passwords
Real-time¶
Sparrow has excellent support for real-time updates. Or you could call it live updates but
‘spalrow’ is not a word. Anyway, in its simplest form, just inherit from RTEntity instead of
Entity. This will allow you to call add_listener (and remove_listener) on the object:
class User(RTEntity):
name = Property(str)
key = UID = KeyProperty()
Whenever update or delete is called, all listeners will get notified of this.
A RTEntity gets an extra method send_update which will trigger all listeners to be notified
of an update without actually writing to the database.
Real-time references¶
The real fancy stuff is a RTReference though. This will make sure that whenever some object
refers to another object, it will automatically call add_reference on all listeners of the
referencing object. For example, with a few modifications we can add live messaging to our
Message class of before:
class Message(Entity):
msg = Property(str)
to = RTReference(User) #
from = Reference(User) # Assuming the sender knows it has sent a message, it doesn't
# need to know it has sent a message again.
A RTReference requires a RTEntity as referencing class.
Both RTReference and RTEntity add some overhead, so only use it when necessary.
The listeners need to following a certain interface, more info about that in RTEntity.
Database¶
More info about this can be found in the docs for the file sql.py. However, some
Entity-specific things are not explained there. A list of the possibilities
- Classmethods (where
Clsis a subclass ofEntity):
Cls.get(Cls.prop1 == val, Cls.prop2 <= Cls.prop3): returns aSELECTquery.Cls.raw("SELECT * FROM ..."): returns whatever query you want.obj = await Cls.find_by_key(some_key, db): returns an instance with that key.
- Methods (where
objis an instance of a subclass ofEntity):
await obj.insert(db): inserts the object in the database. Will also fill in theKeyPropertyif it exists.await obj.update(db): update in the database.await obj.delete(db): delete from the database.
Caching¶
It’s there, and it’s magic. All objects in memory with the same key will, in fact, be the exact
same object (I’m talking about is equality, not == equality). It will regulate itself and you
shouldn’t really care about it. However, I’d like to mention that if an object is in the cache, it
will be crazy fast to call find_by_key, as it will not use the database at all.
Reference¶
-
exception
sparrow.entity.CantSetProperty(obj, propnames)[source]¶ Bases:
sparrow.util.ErrorRaised when trying to set properties you may not edit. Mainly when editing/setting through edit_from_json or Cls(json_dict=...).
-
args¶
-
with_traceback()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
class
sparrow.entity.ConstrainedProperty(typ, constraint: function=None, sql_extra: str='', required: bool=True, json: bool=True)[source]¶ Bases:
sparrow.entity.Property-
sql_def()¶
-
type_sql_def()¶
-
-
class
sparrow.entity.Entity(*args, **kwargs)[source]¶ Bases:
objectCentral class for an Entity. WARNING: Be careful with changing the key as it will fuck with caching. Basically, don’t do it.
-
constraint= None¶
-
classmethod
find_by_key(key, db: sparrow.sql.Database=None) → 'cls'[source]¶ Works different from get, as it will immediatly return the object
-
json_repr() → dict[source]¶ Returns a dictionary of all properties that don’t contain json = False. When overriding this method, you can return anything you want as long as it is convertible to JSON.
-
-
class
sparrow.entity.Enum(*args)[source]¶ Bases:
sparrow.entity.Type-
constraint¶
-
from_sql(obj)¶
-
to_sql(obj: 'self.python_type')¶
-
-
class
sparrow.entity.Key(*props)[source]¶ Bases:
sparrow.entity.QueryableA reference to other properties that define the key of this object.
-
class
sparrow.entity.KeyProperty[source]¶ Bases:
sparrow.entity.SingleKey,sparrow.entity.PropertyA specifically created property to be used as a key. Type in postgres is SERIAL.
-
sql_def()¶
-
type_sql_def()¶
-
-
class
sparrow.entity.List(inner_type)[source]¶ Bases:
sparrow.entity.Type-
constraint= None¶
-
from_sql(obj)¶
-
-
class
sparrow.entity.Listener[source]¶ Bases:
objectInterface for a listener to be used with RTEntity. Main use is documentation, not functionality.
Most implementations will want to define a set of objects they are listening to (listenees).
-
new_reference(obj: sparrow.entity.RTEntity, ref_obj: sparrow.entity.Entity)[source]¶ Handle a new reference from ref_obj to obj. ref_obj does not have to be a RTEntity.
-
-
class
sparrow.entity.MetaEntity[source]¶ Bases:
typeMetaclass for Entity. This does a whole lot of stuff you should not care about as user of this library. If you do want to know how it works I suggest you read the code.
-
mro() → list¶ return a type’s method resolution order
-
-
exception
sparrow.entity.ObjectConstraintFail(obj)[source]¶ Bases:
sparrow.util.ErrorRaised when an object failed to follow its constraint.
-
args¶
-
with_traceback()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
class
sparrow.entity.Property(typ, constraint: function=None, sql_extra: str='', required: bool=True, json: bool=True)[source]¶ Bases:
sparrow.entity.Queryable
-
exception
sparrow.entity.PropertyConstraintFail(obj, prop)[source]¶ Bases:
sparrow.util.ErrorRaised when a property failed to follow its constraint.
-
args¶
-
with_traceback()¶ Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
-
-
class
sparrow.entity.RTEntity(*args, **kwargs)[source]¶ Bases:
sparrow.entity.EntitySubclass of Entity that sends live updates! Listeners should follow the interface of Listener.
-
check()¶ Check object-wide constraint.
-
constraint= None¶
-
edit_from_json(dct: dict)¶
-
find_by_key(key, db: sparrow.sql.Database=None) → 'cls'¶ Works different from get, as it will immediatly return the object
-
get(*where_clauses: list) → sparrow.sql.Sql¶
-
insert(db: sparrow.sql.Database=None, replace=False)¶ Insert in database.
-
json_repr() → dict¶ Returns a dictionary of all properties that don’t contain json = False. When overriding this method, you can return anything you want as long as it is convertible to JSON.
-
raw(text: str, dct={}) → sparrow.sql.RawSql¶ Return a RawSql where the results will be interpreted as objects of cls.
-
to_json() → str¶
-
-
class
sparrow.entity.RTReference(ref: 'MetaEntity')[source]¶ Bases:
sparrow.entity.ReferenceReference that automatically notifies the referencing object.
-
sql_constraint() → str¶ Will only generate the SQL constraint. The metaclass will take care of the properties.
-
-
class
sparrow.entity.RTSingleReference(ref: 'MetaEntity')[source]¶ Bases:
sparrow.entity.RTReference,sparrow.entity.SingleReferenceVersion of RTReference with only one referencing property. (Don’t directly use this, it will be automatic.)
-
single_upgrade()¶
-
sql_constraint() → str¶ Will only generate the SQL constraint. The metaclass will take care of the properties.
-
-
class
sparrow.entity.Reference(ref: 'MetaEntity', json=True, cascade=True)[source]¶ Bases:
sparrow.entity.QueryableA reference to another Entity type.
-
class
sparrow.entity.SingleKey(*props)[source]¶ Bases:
sparrow.entity.KeyVersion of Key with only one property. (Don’t directly use this, it will be automatic.)
-
referencing_props()¶
-
sql_constraint() → str¶ Returns the SQL needed to define the PRIMARY KEY constraint.
-
-
class
sparrow.entity.SingleReference(ref: 'MetaEntity', json=True, cascade=True)[source]¶ Bases:
sparrow.entity.ReferenceVersion of Reference with only one referencing property. (Don’t directly use this, it will be automatic.)
-
single_upgrade()¶
-
sql_constraint() → str¶ Will only generate the SQL constraint. The metaclass will take care of the properties.
-
-
class
sparrow.entity.StaticType(python_type, sql_type=None)[source]¶ Bases:
sparrow.entity.Type-
constraint= None¶
-
from_sql(obj)¶
-
to_sql(obj: 'self.python_type')¶
-