alkali package¶
alkali.database module¶
from alkali import Database, JSONStorage, Model, fields
class MyModel( Model ):
id = fields.IntField(primary_key=True)
title = fields.StringField()
db = Database(models=[MyModel], storage=JSONStorage, root_dir='/tmp', save_on_exit=True)
m = MyModel(id=1,title='old title')
m.save() # adds model instance to MyModel.objects
db.store() # creates /tmp/MyModel.json
db.load() # read /tmp/MyModel.json
m = MyModel.objects.get(pk=1) # do a search on primary key
m.title = "my new title" # change the title
# don't need to call m.save() since the database "knows" about m
# db.store() is automatically called as db goes out of scope, save_on_exit==True
-
class
alkali.database.
Database
(models=[], **kw)[source]¶ Bases:
object
This is the parent object that owns and coordinates all the different classes and objects defined in this module.
Variables: - _storage_type – default storage type for all models, defaults to
alkali.storage.JSONStorage
- _root_dir – directory where all models are stored, defaults to current working directory
- _save_on_exit – automatically save all models before Database object is destroyed. call
Database.store()
explicitly if_save_on_exit
is false.
Parameters: - models – a list of
alkali.model.Model
classes - kw –
- root_dir: default save path directory
- save_on_exit: save all models to disk on exit
- storage: default storage class for all models
-
models
¶ property: return
list
of models in the database
-
get_model
(model_name)[source]¶ Parameters: model_name – the name of the model, note: all model names are converted to lowercase Return type: alkali.model.Model
-
get_filename
(model, storage=None)[source]¶ get the filename for the specified model. allow models to specify their own filename or generate one based on storage class. prepend Database._root_dir.
eg. <_root_dir>/<model name>.<storage.extension>
Parameters: - model – the model name or model class
- storage – the storage class, uses the database default if None
Returns: returns a filename path
Return type: str
-
set_storage
(model, storage=None)[source]¶ set the storage instance for the specified model
precedence is:
- passed in storage class
- model defined storage class
- default storage class of database (JSONStorage)
Parameters: - model – the model name or model class
- storage (IStorage) – override model storage class
Return type: alkali.storage.Storage
instance or None
-
get_storage
(model)[source]¶ get the storage instance for the specified model
Parameters: model – the model name or model class Return type: alkali.storage.Storage
instance or None
-
store
(force=False)[source]¶ persistantly store all model data
Parameters: force (bool) – force store even if alkali.manager.Manager
thinks data is clean
- _storage_type – default storage type for all models, defaults to
alkali.fields module¶
-
class
alkali.fields.
Field
(field_type, **kw)[source]¶ Bases:
object
Base class for all field types. it tries to hold all the functionality so derived classes only need to override methods in special circumstances.
Field objects are instantiated during model creation.
i = IntField()
All Model instances share the same instantiated Field objects in their Meta class. ie:
id(MyModel().Meta.fields['i']) == id(MyModel().Meta.fields['i'])
Fields are python descriptors (@property is also a descriptor). So when a field is get/set the actual value is stored in the parent model instance.
The actual Field() object is accessable via model().Meta.fields[field_name] or via dynamic lookup of <field_name>__field. eg. m.email__field.
Parameters: - field_type (str/int/float/etc) – the type this field should hold
- kw –
- primary_key: is this field a primary key of parent model
- indexed: is this field indexed (not implemented yet)
-
field_type
¶ property: return
type
of this field (int, str, etc)
-
properties
¶ property: return list of possible Field properties
-
default_value
¶ property: what value does this Field default to during model instantiation
-
cast
(value)[source]¶ Whenever a field value is set, the given value passes through this (or derived class) function. This allows validation plus helpful conversion.
- ::
- int_field = “1” # converts to int(“1”) date_field = “Jan 1 2017” # coverted to datetime()
-
class
alkali.fields.
IntField
(**kw)[source]¶ Bases:
alkali.fields.Field
-
default_value
¶ IntField implements auto_increment, useful for a primary_key. The value is incremented during model instantiation.
-
-
class
alkali.fields.
BoolField
(**kw)[source]¶ Bases:
alkali.fields.Field
-
class
alkali.fields.
FloatField
(**kw)[source]¶ Bases:
alkali.fields.Field
-
class
alkali.fields.
StringField
(**kw)[source]¶ Bases:
alkali.fields.Field
holds a unicode string
-
class
alkali.fields.
DateTimeField
(**kw)[source]¶ Bases:
alkali.fields.Field
-
class
alkali.fields.
SetField
(**kw)[source]¶ Bases:
alkali.fields.Field
-
class
alkali.fields.
ForeignKey
(foreign_model, **kw)[source]¶ Bases:
alkali.fields.Field
A ForeignKey is a special type of field. It stores the same value as a primary key in another field. When the model gets/sets a ForeignKey the appropriate lookup is done in the remote manager to return the remote instance.
Parameters: - foreign_model (
alkali.model.Model
) – the Model that this field is referencing - kw –
- primary_key: is this field a primary key of parent model
-
pk_field
¶ Return type: IField.field_type()
, eg: IntField
- foreign_model (
-
class
alkali.fields.
OneToOneField
(foreign_model, **kw)[source]¶ Bases:
alkali.fields.ForeignKey
Parameters: - foreign_model (
alkali.model.Model
) – the Model that this field is referencing - kw –
- primary_key: is this field a primary key of parent model
- foreign_model (
alkali.manager module¶
-
class
alkali.manager.
Manager
(model_class)[source]¶ Bases:
object
the
Manager
class is the parent/owner of all thealkali.model.Model
instances. EachModel
has it’s own manager.Manager
could rightly be calledTable
.Parameters: model_class (Model) – the model that we should store (not an instance) -
model_class
¶
-
count
¶ property: number of model instances we’re holding
-
pks
¶ property: return all primary keys
Return type: list
-
instances
¶ property: return all model instances
Return type: list
-
dirty
¶ property: return True if any model instances are dirty
Return type: bool
-
static
sorter
(elements, reverse=False)[source]¶ yield model instances in primary key order
Parameters: - elements (Manager.instances) – our instances
- kw –
- reverse: return in reverse order
Return type: generator
-
save
(instance, dirty=True, copy_instance=True)[source]¶ Copy instance into our collection. We make a copy so that caller can’t change its object and affect our version without calling save() again.
Parameters: - instance (Model) –
- dirty – don’t mark us as dirty if False, used during loading
-
clear
()[source]¶ remove all instances of our models. we’ll be marked as dirty if we previously had model instances.
Note: this does not affect on-disk files until
Manager.save()
is called.
-
delete
(instance)[source]¶ remove an instance from our models by calling
del
on itParameters: instance (Model) –
-
cb_create_foreign
(sender, instance)[source]¶ called when our foreign parent (likely OneToOneField) is created
-
store
(storage, force=False)[source]¶ save all our instances to storage
Parameters: - storage (Storage) – an instance
- force (bool) – force save even if we’re not dirty
-
load
(storage)[source]¶ load all our instances from storage
Parameters: storage (Storage) – an instance Raises: KeyError – if there are duplicate primary keys
-
get
(*pk, **kw)[source]¶ perform a query that returns a single instance of a model
Parameters: - pk (value or
tuple
if multi-pk) – optional primary key - kw – optional
field_name=value
Return type: single
alkali.model.Model
instanceRaises: - DoesNotExist – if 0 instances returned
- MultipleObjectsReturned – if more than 1 instance returned
m = MyModel.objects.get(1) # equiv to m = MyModel.objects.get(pk=1) m = MyModel.objects.get(some_field='a unique value') m = MyModel.objects.get(field1='a unique', field2='value')
- pk (value or
-
alkali.metamodel module¶
-
class
alkali.metamodel.
MetaModel
[source]¶ Bases:
type
do not use this class directly
code reviews of this class are very welcome
base class for
alkali.model.Model
.this complicated metaclass is required to convert a stylized class into a useful concrete one. it converts
alkali.fields.Field
variables into their base types as attributes on the instantiated class.Meta: adds a
Meta
class if not already defined inModel
derived classobjects:
alkali.manager.Manager
alkali.model module¶
-
exception
alkali.model.
ObjectDoesNotExist
[source]¶ Bases:
exceptions.Exception
base class for a model specific exception (eg. MyModel.DoesNotExist) raised when a query yields no results
-
class
alkali.model.
Model
(*args, **kw)[source]¶ Bases:
object
main class for the database.
the definition of this class defines a table schema but instances of this class hold a row.
model fields are available as attributes. eg.
m.my_field = 'foo'
the Django docs at https://docs.djangoproject.com/en/1.10/topics/db/models/ will be fairly relevant to alkali
see
alkali.database
for some example code-
set_field
(field, value)[source]¶ set a field value, this method is automatically called when setting a field value. safe to call externally.
fires
alkali.signals.field_update
for any listenersParameters: - field (
alkali.fields.Field
) – instance of Field - value (
Field.field_type
) – the already-cast value to store
- field (
-
dirty
¶ property: return True if our fields have changed since creation
-
schema
¶ property: a string that quickly shows the fields and types
-
pk
¶ property: returns this models primary key value. If the model is comprised of serveral primary keys then return a tuple of them.
Return type: Field.field_type
or tuple-of-Field.field_type
-
valid_pk
¶
-
dict
¶ property: returns a dict of all the fields, the fields are json consumable
Return type: OrderedDict
-
json
¶ property: returns json that holds all the fields
Return type: str
-
save
()[source]¶ add ourselves to our
alkali.manager.Manager
and mark ourselves as no longer dirty.it’s up to our
Manager
to persistently save us
-
alkali.peekorator module¶
from https://gist.github.com/dmckeone/7518335, slightly modified by Kurt Neufeld
Generic Peekorator, modeled after next(), for “looking into the future” of a generator/iterator.
Acknowledgements:
- “plof” for the name: http://stackoverflow.com/a/10576559/589362
- Ned Batchelder for the buffered __peek__: http://stackoverflow.com/a/1517965/589362
-
alkali.peekorator.
peek
(peekorator, n=0, default=<alkali.peekorator.PeekoratorDefault object>)[source]¶ next()-like function to be used with a Peekorator
Parameters: - peekorator – Peekorator to use
- n (int) – Number of items to look ahead
- default – If the iterator is exhausted then a default is given, raise StopIteration if not given
-
class
alkali.peekorator.
Peekorator
(generator)[source]¶ Bases:
object
Wrap a generator (or iterator) and allow the ability to peek at the next element in a lazy fashion. If the user never uses peek(), then the only cost over a regular generator is the proxied function call.
Parameters: generator – a generator or iterator that will be iterated over -
peek
(n=0)¶ Return the peeked element for the generator
Parameters: n (int) – how many iterations into the future to peek
-
next
()¶ Get the next result from the generator
-
alkali.query module¶
from alkali import Database, Model
class MyModel( Model ):
id = fields.IntField(primary_key=True)
title = fields.StringField()
db = Database( models=[MyModel] )
# create 10 instances and save them
for i in range(10):
MyModel(id=i, title='number %d' % i).save()
assert MyModel.objects.count == 10
assert MyModel.objects.filter(id__gt=5).count == 4
assert MyModel.objects.filter(id__gt=5, id__le=7).count == 2
assert MyModel.objects.get(pk=1).title == 'number 1'
assert MyModel.objects.order_by('id')[0].id == 0
assert MyModel.objects.order_by('-id')[0].id == 9
-
class
alkali.query.
Aggregate
(field)[source]¶ Bases:
object
A reducing function that returns a single value
Parameters: str (field) –
-
class
alkali.query.
Count
(field)[source]¶ Bases:
alkali.query.Aggregate
number of objects in query
Parameters: str (field) –
-
class
alkali.query.
Sum
(field)[source]¶ Bases:
alkali.query.Aggregate
sum of given field (numeric field required)
Parameters: str (field) –
-
class
alkali.query.
Max
(field)[source]¶ Bases:
alkali.query.Aggregate
largest field (numeric field required)
Parameters: str (field) –
-
class
alkali.query.
Min
(field)[source]¶ Bases:
alkali.query.Aggregate
smallest field (numeric field required)
Parameters: str (field) –
-
class
alkali.query.
Query
(manager)[source]¶ Bases:
object
this class performs queries on manager instances returns lists of model instances
this class is one of the main reasons to use alkali
the Django docs at https://docs.djangoproject.com/en/1.10/topics/db/queries/ will be fairly relevant to alkali, except for anything related to foreign or many2many fields.
this is an internal class so you shouldn’t have to create it directly. create via Manager.
MyModel.objects
Parameters: manager (Manager) – -
count
¶ property: number of model instances we are currently tracking
-
fields
¶ property: helper function to get dict of model fields
Return type: dict
-
model_class
¶ property: return our managers model class
-
field_names
¶ property: return our model field names
Return type: list
ofstr
-
filter
(**kw)[source]¶ Parameters: kw – field_name__op=value
, note:field_name
can be aproperty
Return type: Query perform a query, keeping model instances that pass the criteria specified in the
kw
parameter.see example code above. see Django page for very thorough docs on this functionality. basically, its field_name ‘__’ operation = value.
# field/property f is 'foo' or 'bar' MyModel.objects.filter( f__in=['foo','bar'] ) # 'foo' is in field/property myset MyModel.objects.filter( myset__rin='foo' )
-
order_by
(*fields)[source]¶ change order of self.instances
Parameters: fields (str) – field names, prefixed with optional ‘-‘ to indicate reverse order Return type: Query warning: because this isn’t a real database and we don’t have grouping, passing in multiple fields will very possibly sort on the last field only. python sorting is stable however, so a multiple field sort may work as intended.
-
group_by
(field)[source]¶ returns a dict of distinct values and Query objects
Parameters: field – field name Return type: dict
MyModel.objects.group_by('str_type') { 's1': <Query MyModel(1), MyModel(3)> 's2': <Query MyModel(2)> }
-
limit
(n)[source]¶ return first(+) or last(-) n elements
this has to be the last call during a query since it returns a list of instances and not a Query. passing in 0 is a no-op and returns all instances
Parameters: n (int) – non-zero integer Return type: list
-
values
(*fields)[source]¶ returns list of dicts, each sub-list contains (field_name, field_value)
Return type: list of OrderedDict MyModel.objects.values('int_type','str_type') # [ OrderedDict([('int_type', 10), ('str_type', u'hi')]), # OrderedDict([('int_type', 11), ('str_type', u'hi')]) ]
-
values_list
(*fields, **kw)[source]¶ returns nested list of values in given
fields
orderif flat=True, returns single list
Parameters: - fields (str) – field names or all fields if empty
- kw (bool) –
flat
Rtye: list
see
alkali.query.Query.annotate()
for example
-
aggregate
(*args, **kw)[source]¶ Aggregate (aka reduce) the query via the given function. Each callable
Aggregate
object takes a field/property name as a parameter.The returned dictionary has key
<field_name>__<agg function>
unless keyword is given.Parameters: - args (Aggregate) –
Count
Sum
Max
Min
- kw –
key_value=Aggregate
, note:field_name
can be aproperty
Return type: dict
MyModel.objects.aggregate( the_count=Count('id'), Sum('size') ) # { 'the_count': 12, 'size__sum': 24957 }
- args (Aggregate) –
-
annotate
(**kw)[source]¶ add a variable to each model instance currently in the query
each element in query is passed into function.
Parameters: kw – variable=function Return type: Query c = itertools.count() Counter = lambda elem, c=c: c.next() MyModel(int_type=10).save() MyModel(int_type=11).save() MyModel.objects.annotate(counter=Counter).values_list('int_type','counter') # [[10, 1], [11, 2]] MyModel.objects.annotate(counter=Counter).values_list('int_type','counter') # [[10, 3], [11, 4]]
-
distinct
(*fields)[source]¶ returns a list of lists, each sub-list contains distinct values of the given field
Parameters: fields (str) – field names Return type: list
MyModel(int_type=10, str_type='hi').save() MyModel(int_type=11, str_type='hi').save() MyModel(int_type=12, str_type='there').save() MyModel.objects.distinct('int_type','str_type') # [[10, 11, 12], [u'there', u'hi']] MyModel.objects.distinct('str_type') # [[u'there', u'hi']]
-
alkali.relmanager module¶
-
class
alkali.relmanager.
RelManager
(foreign, child_class, child_field)[source]¶ Bases:
object
This is an internal class that a user of alkali unlikely to use directly.
The
RelManager
class manages queries/connections between two models that have aalkali.fields.ForeignKey
(or equivalent) field.Parameters: -
foreign
¶
-
child_class
¶
-
child_field
¶
-
count
¶
-
all
()[source]¶ get all objects that point to this instance see
alkali.manager.Manager.all()
for syntaxReturn type: alkali.query.Query
-
get
(**kw)[source]¶ get a single object that refers to this instance see
alkali.manager.Manager.get()
for syntaxReturn type: alkali.model.Model
-
alkali.signals module¶
alkali.storage module¶
-
exception
alkali.storage.
FileAlreadyLocked
[source]¶ Bases:
exceptions.Exception
the exception that is thrown when a storage instance tries and fails to lock its data file
-
interface
alkali.storage.
IStorage
[source]¶ -
extension
¶ class level attr of desired filename extension. eg. json
-
read
(model_class)¶ yield (or return a list) of instantiated model_class objects or dicts up to implementer but likely you want to read filename
-
write
(iterator)¶ accept an iterator that yields elements up to implementer but likely you want to write out to filename
-
-
class
alkali.storage.
Storage
(*args, **kw)[source]¶ Bases:
object
helper base class for the Storage object hierarchy
-
class
alkali.storage.
FileStorage
(filename=None, *args, **kw)[source]¶ Bases:
alkali.storage.Storage
this helper class determines the on-disk representation of the database. it could write out objects as json or plain txt or binary, that’s up to the implementation and should be transparent to any models/database.
-
extension
= 'raw'¶
-
filename
¶
-
-
class
alkali.storage.
JSONStorage
(filename=None, *args, **kw)[source]¶ Bases:
alkali.storage.FileStorage
save models in json format
-
extension
= 'json'¶
-
-
class
alkali.storage.
CSVStorage
(filename=None, *args, **kw)[source]¶ Bases:
alkali.storage.FileStorage
load models in csv format
first line assumed to be column headers (aka: field names)
use remap_fieldnames to change column headers into model field names
-
extension
= 'csv'¶
-
remap_fieldnames
(model_class, row)[source]¶ example of remap_fieldnames that could be defined in derived class or as a stand-alone function.
warning: make sure your header row that contains field names has no spaces in it
def remap_fieldnames(self, model_class, row): fields = model_class.Meta.fields.keys() for k in row.keys(): results_key = k.lower().replace(' ', '_') if results_key not in fields: if k == 'Some Wierd Name': results_key = 'good_name' else: raise RuntimeError( "unknown field: {}".format(k) ) row[results_key] = row.pop(k) return row
-