Source code for lighty.db.models

from ..utils import with_metaclass
from ..monads import NoneMonad
from . import fields, query
from .backend import datastore


[docs]def get_attr_source(name, cls): '''Helper method used to get the class where atribute was defined first ''' for src_cls in cls.mro(): if name in src_cls.__dict__: return src_cls
[docs]class ModelBase(type): """Metaclass used to ORM class generation from definitions """ def __new__(cls, name, bases, attrs): """Process new class used ModelBase as metaclass. """ super_new = super(ModelBase, cls).__new__ parents = [b for b in bases if isinstance(b, ModelBase)] if not parents: # If this isn't a subclass of Model, don't do anything special. return super_new(cls, name, bases, attrs) # Get attributers new_attrs = {} new_attrs['_fields'] = set() field_source = {} defined = set() for parent in parents: if hasattr(parent, '_fields'): duplicate_field_keys = defined & parent._fields for dupe_field_name in duplicate_field_keys: field_source[dupe_field_name] = get_attr_source( dupe_field_name, field_source[dupe_field_name]) old_source = field_source[dupe_field_name] new_source = get_attr_source(dupe_field_name, parent) if old_source != new_source: raise AttributeError('Duplicate field, %s, is ' 'inherited from both %s and %s.' % ( dupe_field_name, old_source.__name__, new_source.__name__)) defined |= parent._fields field_source.update(dict.fromkeys(parent._fields, parent)) new_attrs.update(parent._fields) new_attrs['_key_name'] = None for attr_name in attrs.keys(): attr = attrs[attr_name] if isinstance(attr, fields.Field): if attr_name in defined: raise AttributeError('Duplicate field: %s. It was defined ' 'in %s already' % (attr_name, get_attr_source(attr_name, field_source[attr_name]))) defined.add(attr_name) attr.__config__(name, attr_name) new_attrs[attr_name] = attr new_attrs['_fields'] = defined # Initialize properties new_attrs['_unindexed_properties'] = frozenset( new_attrs[field].name for field in new_attrs['_fields'] if not (new_attrs[field].primary_key or new_attrs[field].db_index)) # Create class instance return super_new(cls, name, bases, new_attrs)
[docs]class Model(with_metaclass(ModelBase)): """Model is the superclass of all object entities in the datastore. The programming model is to declare Python subclasses of the Model class, declaring datastore properties as class members of that class. So if you want to publish a story with title, body, and created date, you would do it like this: class Story(db.Model): title = db.CharField(max_length=255) body = db.TextField() created = db.DateTimeField(auto_now_add=True) """ def __init__(self, key_name=None, is_new=True, **kwds): """Creates a new instance of this model. To create a new entity, you instantiate a model and then call put(), which saves the entity to the datastore: person = Person() person.name = 'Bret' person.put() You can initialize properties in the model in the constructor with keyword arguments: person = Person(name='Bret') We initialize all other properties to the default value (as defined by the properties in the model definition) if they are not provided in the constructor. Args: key_name: Name for new model instance. is_new: Indicates all the newly created objects. kwds: Keyword arguments mapping to properties of model. """ self._app = None self._key_name = key_name or '_id' self._is_saved = not is_new # Update value self.__dict__.update(kwds) # Set the default values for unsetted fields cls = self.__class__ for field_name in self._fields: if field_name not in kwds: self.__dict__[field_name] = cls.__dict__[field_name].default
[docs] def key(self): """Unique key for this entity. This property is only available if this entity is already stored in the datastore or if it has a full key, so it is available if this entity was fetched returned from a query, or after put() is called the first time for new entities, or if a complete key was given when constructed. Returns: Datastore key of persisted entity. Raises: AttributeError when entity is not persistent. """ if self._is_saved: return self.__dict__[self._key_name] raise AttributeError()
[docs] def put(self): """Writes this model instance to the datastore. If this instance is new, we add an entity to the datastore. Otherwise, we update this instance, and the key will remain the same. Returns: The key of the instance (either the existing key or a new key). """ fields = dict([(field, self.__dict__[field]) for field in self._fields]) datastore.put(self.__class__, fields) return self
save = put
[docs] def delete(self): """Deletes this entity from the datastore """ return datastore.delete(self, **{self._key_name: self.key()})
@classmethod
[docs] def entity_name(cls): '''Get the table name ''' return (hasattr(cls, '_app') and '%s_%s' % (cls._app, cls.__name__) or cls.__name__)
@classmethod
[docs] def get(cls, **keys): """Fetch instance from the datastore of a specific Model type using key. We support Key objects and string keys (we convert them to Key objects automatically). Useful for ensuring that specific instance types are retrieved from the datastore. It also helps that the source code clearly indicates what kind of object is being retreived. Example: story = Story.get(story_key) Args: keys: Key within datastore entity collection to find; or string key; or list of Keys or string keys. Returns: a Model instance associated with key for provided class if it exists in the datastore, otherwise NoneMonad; """ item = datastore.get(cls, **keys) if item: return cls(is_new=False, **item) else: return (item if isinstance(item, NoneMonad) else NoneMonad('Item not found for: %s' % keys))
@classmethod
[docs] def all(cls, **kwds): """Returns a query over all instances of this model from the datastore. Returns: Query that will retrieve all instances from entity collection. """ return query.Query(model=cls)
@classmethod
[docs] def fields(cls): """Get fields list """ return cls._fields
@classmethod
[docs] def properties(cls): """Alias for fields. """ return cls.fields()