source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/contrib/memdb.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 27.8 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4"""
5This file is part of web2py Web Framework (Copyrighted, 2007-2009).
6Developed by Massimo Di Pierro <mdipierro@cs.depaul.edu> and
7Robin B <robi123@gmail.com>.
8License: LGPLv3
9"""
10
11__all__ = ['MEMDB', 'Field']
12
13import re
14import sys
15import os
16import types
17import datetime
18import thread
19import cStringIO
20import csv
21import copy
22import gluon.validators as validators
23from gluon.utils import web2py_uuid
24from gluon.storage import Storage
25from gluon import SQLTABLE
26import random
27
28SQL_DIALECTS = {'memcache': {
29    'boolean': bool,
30    'string': unicode,
31    'text': unicode,
32    'password': unicode,
33    'blob': unicode,
34    'upload': unicode,
35    'integer': long,
36    'double': float,
37    'date': datetime.date,
38    'time': datetime.time,
39    'datetime': datetime.datetime,
40    'id': int,
41    'reference': int,
42    'lower': None,
43    'upper': None,
44    'is null': 'IS NULL',
45    'is not null': 'IS NOT NULL',
46    'extract': None,
47    'left join': None,
48}}
49
50
51def cleanup(text):
52    if re.compile('[^0-9a-zA-Z_]').findall(text):
53        raise SyntaxError('Can\'t cleanup \'%s\': only [0-9a-zA-Z_] allowed in table and field names' % text)
54    return text
55
56
57def assert_filter_fields(*fields):
58    for field in fields:
59        if isinstance(field, (Field, Expression)) and field.type\
60                in ['text', 'blob']:
61            raise SyntaxError('AppEngine does not index by: %s'
62                              % field.type)
63
64
65def dateobj_to_datetime(object):
66
67    # convert dates,times to datetimes for AppEngine
68
69    if isinstance(object, datetime.date):
70        object = datetime.datetime(object.year, object.month,
71                                   object.day)
72    if isinstance(object, datetime.time):
73        object = datetime.datetime(
74            1970,
75            1,
76            1,
77            object.hour,
78            object.minute,
79            object.second,
80            object.microsecond,
81        )
82    return object
83
84
85def sqlhtml_validators(field_type, length):
86    v = {
87        'boolean': [],
88        'string': validators.IS_LENGTH(length),
89        'text': [],
90        'password': validators.IS_LENGTH(length),
91        'blob': [],
92        'upload': [],
93        'double': validators.IS_FLOAT_IN_RANGE(-1e100, 1e100),
94        'integer': validators.IS_INT_IN_RANGE(-1e100, 1e100),
95        'date': validators.IS_DATE(),
96        'time': validators.IS_TIME(),
97        'datetime': validators.IS_DATETIME(),
98        'reference': validators.IS_INT_IN_RANGE(0, 1e100),
99    }
100    try:
101        return v[field_type[:9]]
102    except KeyError:
103        return []
104
105
106class DALStorage(dict):
107
108    """
109    a dictionary that let you do d['a'] as well as d.a
110    """
111
112    def __getattr__(self, key):
113        return self[key]
114
115    def __setattr__(self, key, value):
116        if key in self:
117            raise SyntaxError(
118                'Object \'%s\'exists and cannot be redefined' % key)
119        self[key] = value
120
121    def __repr__(self):
122        return '<DALStorage ' + dict.__repr__(self) + '>'
123
124
125class SQLCallableList(list):
126
127    def __call__(self):
128        return copy.copy(self)
129
130
131class MEMDB(DALStorage):
132
133    """
134    an instance of this class represents a database connection
135
136    Example::
137
138       db=MEMDB(Client())
139       db.define_table('tablename',Field('fieldname1'),
140                                   Field('fieldname2'))
141    """
142
143    def __init__(self, client):
144        self._dbname = 'memdb'
145        self['_lastsql'] = ''
146        self.tables = SQLCallableList()
147        self._translator = SQL_DIALECTS['memcache']
148        self.client = client
149
150    def define_table(
151        self,
152        tablename,
153        *fields,
154        **args
155    ):
156        tablename = cleanup(tablename)
157        if tablename in dir(self) or tablename[0] == '_':
158            raise SyntaxError('invalid table name: %s' % tablename)
159        if not tablename in self.tables:
160            self.tables.append(tablename)
161        else:
162            raise SyntaxError('table already defined: %s' % tablename)
163        t = self[tablename] = Table(self, tablename, *fields)
164        t._create()
165        return t
166
167    def __call__(self, where=''):
168        return Set(self, where)
169
170
171class SQLALL(object):
172
173    def __init__(self, table):
174        self.table = table
175
176
177class Table(DALStorage):
178
179    """
180    an instance of this class represents a database table
181
182    Example::
183
184        db=MEMDB(Client())
185        db.define_table('users',Field('name'))
186        db.users.insert(name='me')
187    """
188
189    def __init__(
190        self,
191        db,
192        tablename,
193        *fields
194    ):
195        self._db = db
196        self._tablename = tablename
197        self.fields = SQLCallableList()
198        self._referenced_by = []
199        fields = list(fields)
200        fields.insert(0, Field('id', 'id'))
201        for field in fields:
202            self.fields.append(field.name)
203            self[field.name] = field
204            field._tablename = self._tablename
205            field._table = self
206            field._db = self._db
207        self.ALL = SQLALL(self)
208
209    def _create(self):
210        fields = []
211        myfields = {}
212        for k in self.fields:
213            field = self[k]
214            attr = {}
215            if not field.type[:9] in ['id', 'reference']:
216                if field.notnull:
217                    attr = dict(required=True)
218            if field.type[:2] == 'id':
219                continue
220            if field.type[:9] == 'reference':
221                referenced = field.type[10:].strip()
222                if not referenced:
223                    raise SyntaxError('Table %s: reference \'%s\' to nothing!' % (
224                        self._tablename, k))
225                if not referenced in self._db:
226                    raise SyntaxError(
227                        'Table: table %s does not exist' % referenced)
228                referee = self._db[referenced]
229                ftype = \
230                    self._db._translator[field.type[:9]](
231                        self._db[referenced]._tableobj)
232                if self._tablename in referee.fields:  # ## THIS IS OK
233                    raise SyntaxError('Field: table \'%s\' has same name as a field '
234                                      'in referenced table \'%s\'' % (
235                                          self._tablename, referenced))
236                self._db[referenced]._referenced_by.append((self._tablename,
237                                                            field.name))
238            elif not field.type in self._db._translator\
239                    or not self._db._translator[field.type]:
240                raise SyntaxError('Field: unknown field type %s' % field.type)
241        self._tableobj = self._db.client
242        return None
243
244    def create(self):
245
246        # nothing to do, here for backward compatility
247
248        pass
249
250    def drop(self):
251
252        # nothing to do, here for backward compatibility
253
254        self._db(self.id > 0).delete()
255
256
257    def insert(self, **fields):
258        # Checks 3 times that the id is new. 3 times is enough!
259        for i in range(3):
260            id = self._create_id()
261            if self.get(id) is None and self.update(id, **fields):
262                return long(id)
263        else:
264            raise RuntimeError("Too many ID conflicts")
265
266    def get(self, id):
267        val = self._tableobj.get(self._id_to_key(id))
268        if val:
269            return Storage(val)
270        else:
271            return None
272
273    def update(self, id, **fields):
274        for field in fields:
275            if not field in fields and self[field].default\
276                    is not None:
277                fields[field] = self[field].default
278            if field in fields:
279                fields[field] = obj_represent(fields[field],
280                                              self[field].type, self._db)
281        return self._tableobj.set(self._id_to_key(id), fields)
282
283    def delete(self, id):
284        return self._tableobj.delete(self._id_to_key(id))
285
286    def _id_to_key(self, id):
287        return '__memdb__/t/%s/k/%s' % (self._tablename, str(id))
288
289    def _create_id(self):
290        return long(web2py_uuid().replace('-',''),16)
291
292    def __str__(self):
293        return self._tablename
294
295    def __call__(self, id, **kwargs):
296        record = self.get(id)
297        if record is None:
298          return None
299        if kwargs and any(record[key]!=kwargs[key] for key in kwargs):
300            return None
301        return record
302
303class Expression(object):
304
305    def __init__(
306        self,
307        name,
308        type='string',
309        db=None,
310    ):
311        (self.name, self.type, self._db) = (name, type, db)
312
313    def __str__(self):
314        return self.name
315
316    def __or__(self, other):  # for use in sortby
317        assert_filter_fields(self, other)
318        return Expression(self.name + '|' + other.name, None, None)
319
320    def __invert__(self):
321        assert_filter_fields(self)
322        return Expression('-' + self.name, self.type, None)
323
324    # for use in Query
325
326    def __eq__(self, value):
327        return Query(self, '=', value)
328
329    def __ne__(self, value):
330        return Query(self, '!=', value)
331
332    def __lt__(self, value):
333        return Query(self, '<', value)
334
335    def __le__(self, value):
336        return Query(self, '<=', value)
337
338    def __gt__(self, value):
339        return Query(self, '>', value)
340
341    def __ge__(self, value):
342        return Query(self, '>=', value)
343
344    # def like(self,value): return Query(self,' LIKE ',value)
345    # def belongs(self,value): return Query(self,' IN ',value)
346    # for use in both Query and sortby
347
348    def __add__(self, other):
349        return Expression('%s+%s' % (self, other), 'float', None)
350
351    def __sub__(self, other):
352        return Expression('%s-%s' % (self, other), 'float', None)
353
354    def __mul__(self, other):
355        return Expression('%s*%s' % (self, other), 'float', None)
356
357    def __div__(self, other):
358        return Expression('%s/%s' % (self, other), 'float', None)
359
360
361class Field(Expression):
362
363    """
364    an instance of this class represents a database field
365
366    example::
367
368        a = Field(name, 'string', length=32, required=False,
369                     default=None, requires=IS_NOT_EMPTY(), notnull=False,
370                     unique=False, uploadfield=True)
371
372    to be used as argument of GQLDB.define_table
373
374    allowed field types:
375    string, boolean, integer, double, text, blob,
376    date, time, datetime, upload, password
377
378    strings must have a length or 512 by default.
379    fields should have a default or they will be required in SQLFORMs
380    the requires argument are used to validate the field input in SQLFORMs
381
382    """
383
384    def __init__(
385        self,
386        fieldname,
387        type='string',
388        length=None,
389        default=None,
390        required=False,
391        requires=sqlhtml_validators,
392        ondelete='CASCADE',
393        notnull=False,
394        unique=False,
395        uploadfield=True,
396    ):
397
398        self.name = cleanup(fieldname)
399        if fieldname in dir(Table) or fieldname[0] == '_':
400            raise SyntaxError('Field: invalid field name: %s' % fieldname)
401        if isinstance(type, Table):
402            type = 'reference ' + type._tablename
403        if not length:
404            length = 512
405        self.type = type  # 'string', 'integer'
406        self.length = length  # the length of the string
407        self.default = default  # default value for field
408        self.required = required  # is this field required
409        self.ondelete = ondelete.upper()  # this is for reference fields only
410        self.notnull = notnull
411        self.unique = unique
412        self.uploadfield = uploadfield
413        if requires == sqlhtml_validators:
414            requires = sqlhtml_validators(type, length)
415        elif requires is None:
416            requires = []
417        self.requires = requires  # list of validators
418
419    def formatter(self, value):
420        if value is None or not self.requires:
421            return value
422        if not isinstance(self.requires, (list, tuple)):
423            requires = [self.requires]
424        else:
425            requires = copy.copy(self.requires)
426        requires.reverse()
427        for item in requires:
428            if hasattr(item, 'formatter'):
429                value = item.formatter(value)
430        return value
431
432    def __str__(self):
433        return '%s.%s' % (self._tablename, self.name)
434
435
436MEMDB.Field = Field  # ## required by gluon/globals.py session.connect
437
438
439def obj_represent(object, fieldtype, db):
440    if object is not None:
441        if fieldtype == 'date' and not isinstance(object,
442                                                  datetime.date):
443            (y, m, d) = [int(x) for x in str(object).strip().split('-')]
444            object = datetime.date(y, m, d)
445        elif fieldtype == 'time' and not isinstance(object, datetime.time):
446            time_items = [int(x) for x in str(object).strip().split(':')[:3]]
447            if len(time_items) == 3:
448                (h, mi, s) = time_items
449            else:
450                (h, mi, s) = time_items + [0]
451            object = datetime.time(h, mi, s)
452        elif fieldtype == 'datetime' and not isinstance(object,
453                                                        datetime.datetime):
454            (y, m, d) = [int(x) for x in
455                         str(object)[:10].strip().split('-')]
456            time_items = [int(x) for x in
457                          str(object)[11:].strip().split(':')[:3]]
458            if len(time_items) == 3:
459                (h, mi, s) = time_items
460            else:
461                (h, mi, s) = time_items + [0]
462            object = datetime.datetime(
463                y,
464                m,
465                d,
466                h,
467                mi,
468                s,
469            )
470        elif fieldtype == 'integer' and not isinstance(object, long):
471            object = long(object)
472
473    return object
474
475
476class QueryException:
477
478    def __init__(self, **a):
479        self.__dict__ = a
480
481
482class Query(object):
483
484    """
485    A query object necessary to define a set.
486    It can be stored or can be passed to GQLDB.__call__() to obtain a Set
487
488    Example:
489    query=db.users.name=='Max'
490    set=db(query)
491    records=set.select()
492    """
493
494    def __init__(
495        self,
496        left,
497        op=None,
498        right=None,
499    ):
500        if isinstance(right, (Field, Expression)):
501            raise SyntaxError(
502                'Query: right side of filter must be a value or entity')
503        if isinstance(left, Field) and left.name == 'id':
504            if op == '=':
505                self.get_one = QueryException(
506                    tablename=left._tablename, id=long(right or 0))
507                return
508            else:
509                raise SyntaxError('only equality by id is supported')
510        raise SyntaxError('not supported')
511
512    def __str__(self):
513        return str(self.left)
514
515
516class Set(object):
517
518    """
519    As Set represents a set of records in the database,
520    the records are identified by the where=Query(...) object.
521    normally the Set is generated by GQLDB.__call__(Query(...))
522
523    given a set, for example
524       set=db(db.users.name=='Max')
525    you can:
526       set.update(db.users.name='Massimo')
527       set.delete() # all elements in the set
528       set.select(orderby=db.users.id,groupby=db.users.name,limitby=(0,10))
529    and take subsets:
530       subset=set(db.users.id<5)
531    """
532
533    def __init__(self, db, where=None):
534        self._db = db
535        self._tables = []
536        self.filters = []
537        if hasattr(where, 'get_all'):
538            self.where = where
539            self._tables.insert(0, where.get_all)
540        elif hasattr(where, 'get_one') and isinstance(where.get_one,
541                                                      QueryException):
542            self.where = where.get_one
543        else:
544
545            # find out which tables are involved
546
547            if isinstance(where, Query):
548                self.filters = where.left
549            self.where = where
550            self._tables = [field._tablename for (field, op, val) in
551                            self.filters]
552
553    def __call__(self, where):
554        if isinstance(self.where, QueryException) or isinstance(where,
555                                                                QueryException):
556            raise SyntaxError('neither self.where nor where can be a QueryException instance')
557        if self.where:
558            return Set(self._db, self.where & where)
559        else:
560            return Set(self._db, where)
561
562    def _get_table_or_raise(self):
563        tablenames = list(set(self._tables))  # unique
564        if len(tablenames) < 1:
565            raise SyntaxError('Set: no tables selected')
566        if len(tablenames) > 1:
567            raise SyntaxError('Set: no join in appengine')
568        return self._db[tablenames[0]]._tableobj
569
570    def _getitem_exception(self):
571        (tablename, id) = (self.where.tablename, self.where.id)
572        fields = self._db[tablename].fields
573        self.colnames = ['%s.%s' % (tablename, t) for t in fields]
574        item = self._db[tablename].get(id)
575        return (item, fields, tablename, id)
576
577    def _select_except(self):
578        (item, fields, tablename, id) = self._getitem_exception()
579        if not item:
580            return []
581        new_item = []
582        for t in fields:
583            if t == 'id':
584                new_item.append(long(id))
585            else:
586                new_item.append(getattr(item, t))
587        r = [new_item]
588        return Rows(self._db, r, *self.colnames)
589
590    def select(self, *fields, **attributes):
591        """
592        Always returns a Rows object, even if it may be empty
593        """
594
595        if isinstance(self.where, QueryException):
596            return self._select_except()
597        else:
598            raise SyntaxError('select arguments not supported')
599
600    def count(self):
601        return len(self.select())
602
603    def delete(self):
604        if isinstance(self.where, QueryException):
605            (item, fields, tablename, id) = self._getitem_exception()
606            if not item:
607                return
608            self._db[tablename].delete(id)
609        else:
610            raise Exception('deletion not implemented')
611
612    def update(self, **update_fields):
613        if isinstance(self.where, QueryException):
614            (item, fields, tablename, id) = self._getitem_exception()
615            if not item:
616                return
617            for (key, value) in update_fields.items():
618                setattr(item, key, value)
619            self._db[tablename].update(id, **item)
620        else:
621            raise Exception('update not implemented')
622
623
624def update_record(
625    t,
626    s,
627    id,
628    a,
629):
630    item = s.get(id)
631    for (key, value) in a.items():
632        t[key] = value
633        setattr(item, key, value)
634    s.update(id, **item)
635
636
637class Rows(object):
638
639    """
640    A wrapper for the return value of a select. It basically represents a table.
641    It has an iterator and each row is represented as a dictionary.
642    """
643
644    # ## this class still needs some work to care for ID/OID
645
646    def __init__(
647        self,
648        db,
649        response,
650        *colnames
651    ):
652        self._db = db
653        self.colnames = colnames
654        self.response = response
655
656    def __len__(self):
657        return len(self.response)
658
659    def __getitem__(self, i):
660        if i >= len(self.response) or i < 0:
661            raise SyntaxError('Rows: no such row: %i' % i)
662        if len(self.response[0]) != len(self.colnames):
663            raise SyntaxError('Rows: internal error')
664        row = DALStorage()
665        for j in xrange(len(self.colnames)):
666            value = self.response[i][j]
667            if isinstance(value, unicode):
668                value = value.encode('utf-8')
669            packed = self.colnames[j].split('.')
670            try:
671                (tablename, fieldname) = packed
672            except:
673                if not '_extra' in row:
674                    row['_extra'] = DALStorage()
675                row['_extra'][self.colnames[j]] = value
676                continue
677            table = self._db[tablename]
678            field = table[fieldname]
679            if not tablename in row:
680                row[tablename] = DALStorage()
681            if field.type[:9] == 'reference':
682                referee = field.type[10:].strip()
683                rid = value
684                row[tablename][fieldname] = rid
685            elif field.type == 'boolean' and value is not None:
686
687                # row[tablename][fieldname]=Set(self._db[referee].id==rid)
688
689                if value == True or value == 'T':
690                    row[tablename][fieldname] = True
691                else:
692                    row[tablename][fieldname] = False
693            elif field.type == 'date' and value is not None\
694                    and not isinstance(value, datetime.date):
695                (y, m, d) = [int(x) for x in
696                             str(value).strip().split('-')]
697                row[tablename][fieldname] = datetime.date(y, m, d)
698            elif field.type == 'time' and value is not None\
699                    and not isinstance(value, datetime.time):
700                time_items = [int(x) for x in
701                              str(value).strip().split(':')[:3]]
702                if len(time_items) == 3:
703                    (h, mi, s) = time_items
704                else:
705                    (h, mi, s) = time_items + [0]
706                row[tablename][fieldname] = datetime.time(h, mi, s)
707            elif field.type == 'datetime' and value is not None\
708                    and not isinstance(value, datetime.datetime):
709                (y, m, d) = [int(x) for x in
710                             str(value)[:10].strip().split('-')]
711                time_items = [int(x) for x in
712                              str(value)[11:].strip().split(':')[:3]]
713                if len(time_items) == 3:
714                    (h, mi, s) = time_items
715                else:
716                    (h, mi, s) = time_items + [0]
717                row[tablename][fieldname] = datetime.datetime(
718                    y,
719                    m,
720                    d,
721                    h,
722                    mi,
723                    s,
724                )
725            else:
726                row[tablename][fieldname] = value
727            if fieldname == 'id':
728                id = row[tablename].id
729                row[tablename].update_record = lambda t = row[tablename], \
730                    s = self._db[tablename], id = id, **a: update_record(t,
731                                                                         s, id, a)
732                for (referee_table, referee_name) in \
733                        table._referenced_by:
734                    s = self._db[referee_table][referee_name]
735                    row[tablename][referee_table] = Set(self._db, s
736                                                        == id)
737        if len(row.keys()) == 1:
738            return row[row.keys()[0]]
739        return row
740
741    def __iter__(self):
742        """
743        iterator over records
744        """
745
746        for i in xrange(len(self)):
747            yield self[i]
748
749    def __str__(self):
750        """
751        serializes the table into a csv file
752        """
753
754        s = cStringIO.StringIO()
755        writer = csv.writer(s)
756        writer.writerow(self.colnames)
757        c = len(self.colnames)
758        for i in xrange(len(self)):
759            row = [self.response[i][j] for j in xrange(c)]
760            for k in xrange(c):
761                if isinstance(row[k], unicode):
762                    row[k] = row[k].encode('utf-8')
763            writer.writerow(row)
764        return s.getvalue()
765
766    def xml(self):
767        """
768        serializes the table using SQLTABLE (if present)
769        """
770
771        return SQLTABLE(self).xml()
772
773
774def test_all():
775    """
776    How to run from web2py dir:
777     export PYTHONPATH=.:YOUR_PLATFORMS_APPENGINE_PATH
778     python gluon/contrib/memdb.py
779
780    Setup the UTC timezone and database stubs
781
782    >>> import os
783    >>> os.environ['TZ'] = 'UTC'
784    >>> import time
785    >>> if hasattr(time, 'tzset'):
786    ...   time.tzset()
787    >>>
788    >>> from google.appengine.api import apiproxy_stub_map
789    >>> from google.appengine.api.memcache import memcache_stub
790    >>> apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()
791    >>> apiproxy_stub_map.apiproxy.RegisterStub('memcache', memcache_stub.MemcacheServiceStub())
792
793        Create a table with all possible field types
794    >>> from google.appengine.api.memcache import Client
795    >>> db=MEMDB(Client())
796    >>> tmp=db.define_table('users',              Field('stringf','string',length=32,required=True),              Field('booleanf','boolean',default=False),              Field('passwordf','password',notnull=True),              Field('blobf','blob'),              Field('uploadf','upload'),              Field('integerf','integer',unique=True),              Field('doublef','double',unique=True,notnull=True),              Field('datef','date',default=datetime.date.today()),              Field('timef','time'),              Field('datetimef','datetime'),              migrate='test_user.table')
797
798   Insert a field
799
800    >>> user_id = db.users.insert(stringf='a',booleanf=True,passwordf='p',blobf='0A',                       uploadf=None, integerf=5,doublef=3.14,                       datef=datetime.date(2001,1,1),                       timef=datetime.time(12,30,15),                       datetimef=datetime.datetime(2002,2,2,12,30,15))
801    >>> user_id != None
802    True
803
804    Select all
805
806    # >>> all = db().select(db.users.ALL)
807
808    Drop the table
809
810    # >>> db.users.drop()
811
812    Select many entities
813
814    >>> tmp = db.define_table(\"posts\",              Field('body','text'),              Field('total','integer'),              Field('created_at','datetime'))
815    >>> many = 20   #2010 # more than 1000 single fetch limit (it can be slow)
816    >>> few = 5
817    >>> most = many - few
818    >>> 0 < few < most < many
819    True
820    >>> for i in range(many):
821    ...     f=db.posts.insert(body='',                total=i,created_at=datetime.datetime(2008, 7, 6, 14, 15, 42, i))
822    >>>
823
824    # test timezones
825    >>> class TZOffset(datetime.tzinfo):
826    ...   def __init__(self,offset=0):
827    ...     self.offset = offset
828    ...   def utcoffset(self, dt): return datetime.timedelta(hours=self.offset)
829    ...   def dst(self, dt): return datetime.timedelta(0)
830    ...   def tzname(self, dt): return 'UTC' + str(self.offset)
831    ...
832    >>> SERVER_OFFSET = -8
833    >>>
834    >>> stamp = datetime.datetime(2008, 7, 6, 14, 15, 42, 828201)
835    >>> post_id = db.posts.insert(created_at=stamp,body='body1')
836    >>> naive_stamp = db(db.posts.id==post_id).select()[0].created_at
837    >>> utc_stamp=naive_stamp.replace(tzinfo=TZOffset())
838    >>> server_stamp = utc_stamp.astimezone(TZOffset(SERVER_OFFSET))
839    >>> stamp == naive_stamp
840    True
841    >>> utc_stamp == server_stamp
842    True
843    >>> rows = db(db.posts.id==post_id).select()
844    >>> len(rows) == 1
845    True
846    >>> rows[0].body == 'body1'
847    True
848    >>> db(db.posts.id==post_id).delete()
849    >>> rows = db(db.posts.id==post_id).select()
850    >>> len(rows) == 0
851    True
852
853    >>> id = db.posts.insert(total='0')   # coerce str to integer
854    >>> rows = db(db.posts.id==id).select()
855    >>> len(rows) == 1
856    True
857    >>> rows[0].total == 0
858    True
859
860    Examples of insert, select, update, delete
861
862    >>> tmp=db.define_table('person', Field('name'), Field('birth','date'), migrate='test_person.table')
863    >>> marco_id=db.person.insert(name=\"Marco\",birth='2005-06-22')
864    >>> person_id=db.person.insert(name=\"Massimo\",birth='1971-12-21')
865    >>> me=db(db.person.id==person_id).select()[0] # test select
866    >>> me.name
867    'Massimo'
868    >>> db(db.person.id==person_id).update(name='massimo') # test update
869    >>> me = db(db.person.id==person_id).select()[0]
870    >>> me.name
871    'massimo'
872    >>> str(me.birth)
873    '1971-12-21'
874
875    # resave date to ensure it comes back the same
876    >>> me=db(db.person.id==person_id).update(birth=me.birth) # test update
877    >>> me = db(db.person.id==person_id).select()[0]
878    >>> me.birth
879    datetime.date(1971, 12, 21)
880    >>> db(db.person.id==marco_id).delete() # test delete
881    >>> len(db(db.person.id==marco_id).select())
882    0
883
884    Update a single record
885
886    >>> me.update_record(name=\"Max\")
887    >>> me.name
888    'Max'
889    >>> me = db(db.person.id == person_id).select()[0]
890    >>> me.name
891    'Max'
892
893    """
894
895SQLField = Field
896SQLTable = Table
897SQLXorable = Expression
898SQLQuery = Query
899SQLSet = Set
900SQLRows = Rows
901SQLStorage = DALStorage
902
903if __name__ == '__main__':
904    import doctest
905    doctest.testmod()
Note: See TracBrowser for help on using the repository browser.