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
Cls
is a subclass ofEntity
):
Cls.get(Cls.prop1 == val, Cls.prop2 <= Cls.prop3)
: returns aSELECT
query.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
obj
is an instance of a subclass ofEntity
):
await obj.insert(db)
: inserts the object in the database. Will also fill in theKeyProperty
if 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.Error
Raised 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:
object
Central 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.Queryable
A reference to other properties that define the key of this object.
-
class
sparrow.entity.
KeyProperty
[source]¶ Bases:
sparrow.entity.SingleKey
,sparrow.entity.Property
A 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:
object
Interface 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:
type
Metaclass 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.Error
Raised 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.Error
Raised 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.Entity
Subclass 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.Reference
Reference 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.SingleReference
Version 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.Queryable
A reference to another Entity type.
-
class
sparrow.entity.
SingleKey
(*props)[source]¶ Bases:
sparrow.entity.Key
Version 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.Reference
Version 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')¶
-