model Module

Rom - the Redis object mapper for Python

Copyright 2013-2020 Josiah Carlson

Released under the LGPL license version 2.1 and version 3 (you can choose which you’d like to be bound under).

class rom.model.Model(**kwargs)

Bases: object

This is the base class for all models. You subclass from this base Model in order to create a model with columns. As an example:

class User(Model):
    email_address = String(required=True, unique=True)
    salt = String(default='')
    hash = String(default='')
    created_at = Float(default=time.time, index=True)

Which can then be used like:

user = User(email_addrss='user@domain.com')
user.save() # session.commit() or session.flush() works too
user = User.get_by(email_address='user@domain.com')
user = User.get(5)
users = User.get([2, 6, 1, 7])

To perform arbitrary queries on entities involving the indices that you defined (by passing index=True on column creation), you access the .query class property on the model:

query = User.query
query = query.filter(created_at=(time.time()-86400, time.time()))
users = query.execute()

Note

You can perform single or chained queries against any/all columns that were defined with index=True.

Composite/multi-column unique constraints

As of version 0.28.0 and later, rom supports the ability for you to have a unique constraint involving multiple columns. Individual columns can be defined unique by passing the ‘unique=True’ specifier during column definition as always.

The attribute unique_together defines those groups of columns that when taken together must be unique for .save() to complete successfully. This will work almost exactly the same as Django’s unique_together, and is comparable to SQLAlchemy’s UniqueConstraint().

Usage:

class UniquePosition(Model):
    x = Integer()
    y = Integer()

    unique_together = [
        ('x', 'y'),
    ]

Note

If one or more of the column values on an entity that is part of a unique constraint is None in Python, the unique constraint won’t apply. This is the typical behavior of nulls in unique constraints inside both MySQL and Postgres.

copy()

Creates a shallow copy of the given entity (any entities that can be retrieved from a OneToMany relationship will not be copied).

delete(**kwargs)

Deletes the entity immediately. Also performs any on_delete operations specified as part of column definitions.

classmethod does_not_endwith(attr, values, blocksize=100)

This iterates over all entities with an attribute that does not end with the provided values. This is only available to models with a suffix index on the given attribute; values must be normalized the same as with the indexed inputs for this to work properly (lowercased, etc.).

Args:
  • attr - name of the attribute/column on the entity.

  • values - list of values to exclude.

This method will auto-forget items from the session after yielding them, so if you want to change data, you’ll have to handle saving and deleting outside of the session.

..note: values <= 7 characters long will be fast, values >= 8 characters

will require round trips and will be substantially slower.

classmethod does_not_startwith(attr, values, blocksize=100)

This iterates over all entities with an attribute that does not start with the provided values. This is only available to models with a prefix index on the given attribute; values must be normalized the same as with the indexed inputs for this to work properly (lowercased, etc.).

Args:
  • attr - name of the attribute/column on the entity.

  • values - list of values to exclude.

This method will auto-forget items from the session after yielding them, so if you want to change data, you’ll have to handle saving and deleting outside of the session.

..note: values <= 7 characters long will be fast, values >= 8 characters

will require round trips and will be substantially slower.

classmethod get(ids)

Will fetch one or more entities of this type from the session or Redis.

Used like:

MyModel.get(5)
MyModel.get([1, 6, 2, 4])

Passing a list or a tuple will return multiple entities, in the same order that the ids were passed.

classmethod get_by(**kwargs)

This method offers a simple query method for fetching entities of this type via attribute numeric ranges (such columns must be indexed), or via unique columns.

Some examples:

user = User.get_by(email_address='user@domain.com')
# gets up to 25 users created in the last 24 hours
users = User.get_by(
    created_at=(time.time()-86400, time.time()),
    _limit=(0, 25))

Optional keyword-only arguments:

  • _limit - A 2-tuple of (offset, count) that can be used to paginate or otherwise limit results returned by a numeric range query

  • _numeric - An optional boolean defaulting to False that forces the use of a numeric index for .get_by(col=val) queries even when col has an existing unique index

If you would like to make queries against multiple columns or with multiple criteria, look into the Model.query class property.

Note

rom will attempt to use a unique index first, then a numeric index if there was no unique index. You can explicitly tell rom to only use the numeric index by using .get_by(..., _numeric=True).

Note

Ranged queries with get_by(col=(start, end)) will only work with columns that use a numeric index.

id

This is a primary key column, used when you want the primary key to be named something other than ‘id’. If you omit a PrimaryKey column on your Model classes, one will be automatically created for you.

Only the index argument will be used. You may want to enable indexing on this column if you want to be able to perform queries and sort the results by primary key.

Used via:

class MyModel(Model):
    id = PrimaryKey()
query = <rom.query.Query object>
refresh(force=False)
save(full=False, force=False)

Saves the current entity to Redis. Will only save changed data by default, but you can force a full save by passing full=True.

If the underlying entity was deleted and you want to re-save the entity, you can pass force=True to force a full re-save of the entity.

to_dict()

Returns a copy of all data assigned to columns in this entity. Useful for returning items to JSON-enabled APIs. If you want to copy an entity, you should look at the .copy() method.

transfer(other, attr, value, txn_model, txn_attr, decimal_places=0, refresh_entities=True, refresh_index=True)

Transfer some numeric value from one entity to another.

This can (for example) be used to transfer money as part of an in-game transaction, or other sort of value transfer.

  • other - the other entity you would like to participate in this transaction (must both have the same db connection)

  • attr - the name of the attribute to transfer value on

  • value - the value to transfer (rounded to decimal_places)

  • txn_model - the entity that represents the value transfer to perform (must have the same db connection as self, and other)

  • txn_attr - the attribute on the entity that represents if the value has been transferred

  • decimal_places - the number of decimal places to the right of the decimal to round to inside Redis / Lua; note that for values >0, this will introduce binary/decimal rounding problems; so small epsilon credit may go away, and you will want to explicitly round on the client on read + display. Or better yet; stick to integers.

  • refresh_entities - will refresh the entity data on transfer if True-ish

  • refresh_index - will refresh the update any relevant indexes after the transfer, if True-ish; implies refresh_entities

..warning: This doesn’t magically create more bits for you. Values in

Redis are either stored as up-to 64 bit integers (0 decimal places) or 64 bit doubles with 53 bits of precision. For doubles, that means 15-16 decimal digits. For 64 bit integers, that is 19+ digits, but only integers. So if you want to maximize both precision, and your range of values for “gold”, “points”, “experience”, “value”, etc.; use your smallest denomination as your 1, and divmod on the client for display if you need to.

update(*args, **kwargs)

Updates multiple attributes in a model. If args are provided, this method will assign attributes in the order returned by list(self._columns) until one or both are exhausted.

If kwargs are provided, this method will assign attributes to the names provided, after args have been processed.

rom.model.redis_writer_lua(conn, pkey, namespace, id, unique, udelete, delete, data, keys, scored, prefix, suffix, geo, old_data, is_delete, keys_to_delete)

… Actually write data to Redis. This is an internal detail. Please don’t call me directly.