PyModels

Python models for schema-less databases.
Download

PyModels Ranking & Summary

Advertisement

  • Rating:
  • License:
  • LGPL v3
  • Price:
  • FREE
  • Publisher Name:
  • Andrey Mikhaylenko
  • Publisher web site:
  • http://neithere.net

PyModels Tags


PyModels Description

Python models for schema-less databases. PyModels is a lightweight Python framework for mapping classes to schema-less databases. It is not an ORM as it doesn't map existing schemata to Python objects but instead defines them on a higher layer built upon a schema-less storage (key/value or document-oriented). You define models as a valuable subset of the whole database and work with only certain parts of existing entities -- the parts you need.Installation:sudo easy_install pymodelsYou will also need to install pyrant to enable the Tokyo Tyrant backend. In order to use PyModels with Tokyo Cabinet (i.e. directly, not through Tyrant) you will also need a recent snapshot of the tc from github along with pyrant. You might want to use another library (or libraries) with PyModels.BackendsCurrently Tokyo Cabinet and Tokyo Tyrant backends are included. Any other backend can be more or less easily created by wrapping existing bindings in Storage and Query classes which can loosely follow existing guidelines.The PyModels library does not impose a strict API, it only recommends one. In fact, you will only need to have the backend return a query instance (even bindings' native one) and wrap the results in given model's objects. See pymodels.backends.tyrant for examples.Usage:Not surprising to those who had ever used an ORM:import datetimefrom pymodels import *class Country(Model): name = Property(unicode) # any Python type; default is unicode def __unicode__(self): return self.name class Meta: must_have = {'type': 'country'}class Person(Model): first_name = Property(required=True) last_name = Property(required=True) gender = Property() birth_date = Date() birth_place = Property(Country) # reference to another model def __unicode__(self): return self.full_name # full_name is a dynamic attr, see below @property def age(self): return (datetime.datetime.now().date() - self.birth_date).days / 365 @property def full_name(self): return '%s %s' % (self.first_name, self.last_name) class Meta: must_have = {'first_name__exists': True, 'last_name__exists': True}The interesting part is the Meta subclass. It contains a must_have attribute which actually binds the model to a subset of data in the storage. {'first_name__exists': True} states that a data row/document/... must have the field first_name defined (not necessarily non-empty). You can easily define any other query conditions (currently with respect to the backend's syntax but we hope to unify things). When you create an empty model instance, it will have all the "must haves" pre-filled if they are not complex lookups (e.g. Country will have its type set to True, but we cannot do that with Person's constraints). Another interesting example would be:class Woman(Person): class Meta: must_have = {'gender': 'female'}Or even this:today = datetime.datetime.now()day_16_years_back = now - datetime.timedelta(days=16*365)class Child(Person): parent = Reference(Person) class Meta: must_have = {'birth_date__gte': day_16_years_back}Note that our Woman or Child models are subclasses of Person model. They inherit all attributes of Person. Moreover, Person's metaclass is inherited too. The must_have dictionaries of Child and Woman models are merged into the parent model's dictionary, so when we query the database for records described by the Woman model, we get all records that have first_name and last_name defined and gender set to "female". When we edit a Person instance, we do not care about the parent attribute; we actually don't even have access to it.We can even deal with data described above without model inheritance. Consider this valid model -- LivingBeing:class LivingBeing(Model): species = Property() birth_date = Property() class Meta: must_have = {'birth_date__exists': True}The data described by LivingBeing overlaps the data described by Person. Some people have their birth dates not deifined and Person allows that. However, LivingBeing requires this attribute, so not all people will appear in a query by this model. At the same time LivingBeing does not require names, so anybody and anything, named or nameless, but ever born, is a "living being". Updating a record through any of these models will not touch data that the model does not know. For instance, saving an entity as a LivingBeing will not remove its name or parent, and working with it as a Child will neither expose nor destroy the information about species.These examples illustrate how models are more "views" than "schemata".Now let's try these models with a Tokyo Cabinet database:>>> storage = pymodels.get_storage(... backend = 'pymodels.backends.tokyo_cabinet',... kind = 'TABLE',... path = 'test.tct'... )>>> guido = Person(first_name='Guido', last_name='van Rossum')>>> guido< Person Guido van Rossum >>>> guido.first_nameGuido>>> guido.birth_date = datetime.date(1960, 1, 31)>>> guido.save(storage) # returns the autogenerated primary key'person_0'>>> ppl_named_guido = Person.objects(storage).where(first_name='Guido')>>> ppl_named_guido>>> guido = ppl_named_guido>>> guido.age # calculated on the fly -- datetime conversion works49>>> guido.birth_place = Country(name='Netherlands')>>> guido.save() # model instance already knows the storage it belongs to'person_0'>>> guido.birth_place< Country Netherlands >>>> Country.objects(storage) # yep, it was saved automatically with Guido>>> larry = Person(first_name='Larry', last_name='Wall')>>> larry.save(storage)'person_2'>>> Person.objects(storage)...and so on.Note that relations are supported out of the box.Switching backendsThe PyModels library provides backends for both Tokyo Cabinet (pymodels.backends.tokyo_cabinet) and Tokyo Tyrant (pymodels.backends.tokyo_tyrant). You can choose the TC backend to use the DB file directly, or switch to the TT backend to access the same file through the manager. The first option is great for development and some other cases where you would use SQLite; the second option is important for most production environments where multiple connections are expected. The good news is that there's no more import and export, dump/load sequences, create/alter/drop and friends. Having tested the application against the database storage.tct with Cabinet backend, just run ttserver storage.tct and switch the backend config.Let's create our application:import pymodelsimport settingsfrom models import Country, Personstorage = pymodels.get_storage(settings.DATABASE)print Person.objects(storage) # prints all Person objects from DBNow define settings for both backends (settings.py):# direct access to the database (simple, not scalable)TOKYO_CABINET_DATABASE = { 'backend': 'pymodels.backends.tokyo_cabinet', 'kind': 'TABLE', 'path': 'storage.tct',}# access through the Tyrant manager (needs daemon, scalable)TOKYO_TYRANT_DATABASE = { 'backend': 'pymodels.backends.tokyo_tyrant', 'host': 'localhost', 'port': 1978,}# this is the *only* line you need to change in order to change the backendDATABASE = TOKYO_CABINET_DATABASE Requirements: · Python


PyModels Related Software