Switch to unified view

a/Ming/ming/base.py b/Ming/ming/base.py
...
...
15
            return self[name]
15
            return self[name]
16
        except KeyError:
16
        except KeyError:
17
            raise AttributeError, name
17
            raise AttributeError, name
18
18
19
    def __setattr__(self, name, value):
19
    def __setattr__(self, name, value):
20
        if name in self.__class__.__dict__:
21
            dict.__setattr__(self, name, value)
22
        else:
20
        self.__setitem__(name, value)
23
            self.__setitem__(name, value)
21
24
22
    @classmethod
25
    @classmethod
23
    def from_bson(cls, bson):
26
    def from_bson(cls, bson):
24
        if isinstance(bson, dict):
27
        if isinstance(bson, dict):
25
            return Object(
28
            return Object(
...
...
76
        self.instance = instance
79
        self.instance = instance
77
        self.cls = cls
80
        self.cls = cls
78
81
79
    def __call__(self, session):
82
    def __call__(self, session):
80
        '''In order to use an alternate session, just use Class.mgr(other_session)'''
83
        '''In order to use an alternate session, just use Class.mgr(other_session)'''
81
        return Manager(session, self.instance, self.cls)
84
        result = Manager(self.instance, self.cls)
85
        result.session = session
86
        return result
82
87
83
    def get(self, **kwargs):
88
    def get(self, **kwargs):
84
        """
89
        """
85
        Returns one matching record, or None
90
        Returns one matching record, or None
86
        e.g.
91
        e.g.
...
...
214
        mm = cls.__mongometa__ = type('__mongometa__', tuple(mm_bases), mm_dict)
219
        mm = cls.__mongometa__ = type('__mongometa__', tuple(mm_bases), mm_dict)
215
        if not hasattr(mm, 'polymorphic_on'):
220
        if not hasattr(mm, 'polymorphic_on'):
216
            mm.polymorphic_on = None
221
            mm.polymorphic_on = None
217
            mm.polymorphic_registry = None
222
            mm.polymorphic_registry = None
218
        # Make sure mongometa's schema incorporates base schemas
223
        # Make sure mongometa's schema incorporates base schemas
219
        my_schema = schema.Object()
224
        fields = dict()
220
        for base in mm_bases:
225
        for base in mm_bases:
221
            if hasattr(base, 'schema'):
226
            if hasattr(base, 'schema'):
222
                if base.schema:
227
                if base.schema:
223
                    my_schema.extend(schema.SchemaItem.make(base.schema))
228
                    fields.update(base.schema.fields)
224
        if mm.schema:
229
        if mm.schema:
230
            my_schema = schema.SchemaItem.make(mm.schema, inherited_fields=fields)
231
        else:
225
            my_schema.extend(schema.SchemaItem.make(mm.schema))
232
            my_schema = schema.SchemaItem.make(fields)
226
        # Collect fields
233
        # Collect fields
227
        for k,v in dct.iteritems():
234
        for k,v in dct.iteritems():
228
            if isinstance(v, Field):
235
            if isinstance(v, Field):
229
                v.name = k
236
                v.name = k
230
                si = schema.SchemaItem.make(v.type, *v.args, **v.kwargs)
237
                si = schema.SchemaItem.make(v.type, *v.args, **v.kwargs)
...
...
278
            return cls.__mongometa__.schema.validate(
285
            return cls.__mongometa__.schema.validate(
279
                data, allow_extra=allow_extra, strip_extra=strip_extra)
286
                data, allow_extra=allow_extra, strip_extra=strip_extra)
280
        else:
287
        else:
281
            return cls(data)
288
            return cls(data)
282
289
283
class VersionedDocument(Document):
284
    '''Special Document allowing snapshot and rollback.'''
285
286
    class __mongometa__:
287
        '''Extension of the Document class providing
288
289
        snapshot - class to be used as snapshots of the current class
290
        snapshot_untracked - field names to be skipped when snapshotting
291
        '''
292
        name=None
293
        session=None
294
        indexes=[]
295
        snapshot=None
296
        snapshot_untracked=set()
297
        schema=dict(
298
            _last_snapshot_id=str)
299
300
    def snapshot(self, author):
301
        '''Create a snapshot object of the current object with the specified
302
        author field.
303
        '''
304
        mm = self.__mongometa__
305
        # Get last snapshot
306
        snapshot = mm.snapshot(dict(
307
            (k,v) for k,v in self.iteritems()
308
            if k not in mm.snapshot_untracked))
309
        snapshot._original_id = self._id
310
        snapshot._author = author
311
        del snapshot['_id']
312
        snapshot._id = hashlib.sha1(str(snapshot)).hexdigest()
313
        if snapshot._id == self.get('_last_snapshot_id', None):
314
            return
315
        snapshot.m.save()
316
        self._last_snapshot_id = snapshot._id
317
318
    def rollback(self):
319
        '''Revert one snapshot operation, restoring the values from the most
320
        recent snapshot and destroying the most recent snapshot in the process.
321
        '''
322
        mm = self.__mongometa__
323
        snapshot = mm.snapshot.m.find(
324
            dict(_id=self._last_snapshot_id)).one()
325
        fields = dict(snapshot)
326
        del fields['_author']
327
        self.update(fields)
328
        if '_last_snapshot_id' not in snapshot:
329
            del self['_last_snapshot_id']
330
        snapshot.m.delete()
331
332
class Cursor(object):
290
class Cursor(object):
333
    '''Python class proxying a MongoDB cursor, constructing and validating
291
    '''Python class proxying a MongoDB cursor, constructing and validating
334
    objects that it tracks
292
    objects that it tracks
335
    '''
293
    '''
336
294
...
...
344
    def __len__(self):
302
    def __len__(self):
345
        return self.count()
303
        return self.count()
346
304
347
    def next(self):
305
    def next(self):
348
        bson = self.cursor.next()
306
        bson = self.cursor.next()
349
        if bson:
307
        if bson is None: return None
350
            return self.cls.make(bson, allow_extra=False, strip_extra=True)
308
        return self.cls.make(bson, allow_extra=False, strip_extra=True)
351
        else:
352
            return None
353
309
354
    def count(self):
310
    def count(self):
355
        return self.cursor.count()
311
        return self.cursor.count()
356
312
357
    def limit(self, limit):
313
    def limit(self, limit):