Merge pull request #347 from niphlod/enhancement/DAL
first steps to a cleaner DAL and rname integration
This commit is contained in:
+195
-173
@@ -8611,8 +8611,7 @@ class Table(object):
|
|||||||
db,
|
db,
|
||||||
tablename,
|
tablename,
|
||||||
*fields,
|
*fields,
|
||||||
**args
|
**args):
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
Initializes the table and performs checking on the provided fields.
|
Initializes the table and performs checking on the provided fields.
|
||||||
|
|
||||||
@@ -8623,20 +8622,27 @@ class Table(object):
|
|||||||
|
|
||||||
:raises SyntaxError: when a supplied field is of incorrect type.
|
:raises SyntaxError: when a supplied field is of incorrect type.
|
||||||
"""
|
"""
|
||||||
self._actual = False # set to True by define_table()
|
self._actual = False # set to True by define_table()
|
||||||
self._tablename = tablename
|
self._tablename = tablename
|
||||||
self._ot = None # args.get('rname')
|
if (not isinstance(tablename, str) or tablename[0] == '_'
|
||||||
|
or hasattr(DAL, tablename) or '.' in tablename
|
||||||
|
or REGEX_PYTHON_KEYWORDS.match(tablename)
|
||||||
|
):
|
||||||
|
raise SyntaxError('Field: invalid table name: %s, '
|
||||||
|
'use rname for "funny" names' % tablename)
|
||||||
|
self._ot = None
|
||||||
self._rname = args.get('rname')
|
self._rname = args.get('rname')
|
||||||
self._sequence_name = args.get('sequence_name') or \
|
self._sequence_name = (args.get('sequence_name') or
|
||||||
db and db._adapter.sequence_name(self._rname or tablename)
|
db and db._adapter.sequence_name(self._rname
|
||||||
self._trigger_name = args.get('trigger_name') or \
|
or tablename))
|
||||||
db and db._adapter.trigger_name(tablename)
|
self._trigger_name = (args.get('trigger_name') or
|
||||||
|
db and db._adapter.trigger_name(tablename))
|
||||||
self._common_filter = args.get('common_filter')
|
self._common_filter = args.get('common_filter')
|
||||||
self._format = args.get('format')
|
self._format = args.get('format')
|
||||||
self._singular = args.get(
|
self._singular = args.get(
|
||||||
'singular',tablename.replace('_',' ').capitalize())
|
'singular', tablename.replace('_', ' ').capitalize())
|
||||||
self._plural = args.get(
|
self._plural = args.get(
|
||||||
'plural',pluralize(self._singular.lower()).capitalize())
|
'plural', pluralize(self._singular.lower()).capitalize())
|
||||||
# horrible but for backard compatibility of appamdin:
|
# horrible but for backard compatibility of appamdin:
|
||||||
if 'primarykey' in args and args['primarykey'] is not None:
|
if 'primarykey' in args and args['primarykey'] is not None:
|
||||||
self._primarykey = args.get('primarykey')
|
self._primarykey = args.get('primarykey')
|
||||||
@@ -8650,28 +8656,29 @@ class Table(object):
|
|||||||
|
|
||||||
self.add_method = MethodAdder(self)
|
self.add_method = MethodAdder(self)
|
||||||
|
|
||||||
fieldnames,newfields=set(),[]
|
fieldnames, newfields=set(), []
|
||||||
_primarykey = getattr(self, '_primarykey', None)
|
_primarykey = getattr(self, '_primarykey', None)
|
||||||
if _primarykey is not None:
|
if _primarykey is not None:
|
||||||
if not isinstance(_primarykey, list):
|
if not isinstance(_primarykey, list):
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
"primarykey must be a list of fields from table '%s'" \
|
"primarykey must be a list of fields from table '%s'"
|
||||||
% tablename)
|
% tablename)
|
||||||
if len(_primarykey)==1:
|
if len(_primarykey) == 1:
|
||||||
self._id = [f for f in fields if isinstance(f,Field) \
|
self._id = [f for f in fields if isinstance(f, Field)
|
||||||
and f.name==_primarykey[0]][0]
|
and f.name ==_primarykey[0]][0]
|
||||||
elif not [f for f in fields if (isinstance(f,Field) and
|
elif not [f for f in fields if (isinstance(f, Field) and
|
||||||
f.type=='id') or (isinstance(f, dict) and
|
f.type == 'id') or (isinstance(f, dict) and
|
||||||
f.get("type", None)=="id")]:
|
f.get("type", None) == "id")]:
|
||||||
field = Field('id', 'id')
|
field = Field('id', 'id')
|
||||||
newfields.append(field)
|
newfields.append(field)
|
||||||
fieldnames.add('id')
|
fieldnames.add('id')
|
||||||
self._id = field
|
self._id = field
|
||||||
virtual_fields = []
|
virtual_fields = []
|
||||||
|
|
||||||
def include_new(field):
|
def include_new(field):
|
||||||
newfields.append(field)
|
newfields.append(field)
|
||||||
fieldnames.add(field.name)
|
fieldnames.add(field.name)
|
||||||
if field.type=='id':
|
if field.type == 'id':
|
||||||
self._id = field
|
self._id = field
|
||||||
for field in fields:
|
for field in fields:
|
||||||
if isinstance(field, (FieldMethod, FieldVirtual)):
|
if isinstance(field, (FieldMethod, FieldVirtual)):
|
||||||
@@ -8685,7 +8692,7 @@ class Table(object):
|
|||||||
elif isinstance(field, Table):
|
elif isinstance(field, Table):
|
||||||
table = field
|
table = field
|
||||||
for field in table:
|
for field in table:
|
||||||
if not field.name in fieldnames and not field.type=='id':
|
if not field.name in fieldnames and not field.type == 'id':
|
||||||
t2 = not table._actual and self._tablename
|
t2 = not table._actual and self._tablename
|
||||||
include_new(field.clone(point_self_references_to=t2))
|
include_new(field.clone(point_self_references_to=t2))
|
||||||
elif not isinstance(field, (Field, Table)):
|
elif not isinstance(field, (Field, Table)):
|
||||||
@@ -8698,16 +8705,16 @@ class Table(object):
|
|||||||
self.virtualfields = []
|
self.virtualfields = []
|
||||||
fields = list(fields)
|
fields = list(fields)
|
||||||
|
|
||||||
if db and db._adapter.uploads_in_blob==True:
|
if db and db._adapter.uploads_in_blob is True:
|
||||||
uploadfields = [f.name for f in fields if f.type=='blob']
|
uploadfields = [f.name for f in fields if f.type == 'blob']
|
||||||
for field in fields:
|
for field in fields:
|
||||||
fn = field.uploadfield
|
fn = field.uploadfield
|
||||||
if isinstance(field, Field) and field.type == 'upload'\
|
if isinstance(field, Field) and field.type == 'upload'\
|
||||||
and fn is True:
|
and fn is True:
|
||||||
fn = field.uploadfield = '%s_blob' % field.name
|
fn = field.uploadfield = '%s_blob' % field.name
|
||||||
if isinstance(fn,str) and not fn in uploadfields:
|
if isinstance(fn, str) and not fn in uploadfields:
|
||||||
fields.append(Field(fn,'blob',default='',
|
fields.append(Field(fn, 'blob', default='',
|
||||||
writable=False,readable=False))
|
writable=False, readable=False))
|
||||||
|
|
||||||
fieldnames_set = set()
|
fieldnames_set = set()
|
||||||
reserved = dir(Table) + ['fields']
|
reserved = dir(Table) + ['fields']
|
||||||
@@ -8715,8 +8722,8 @@ class Table(object):
|
|||||||
check_reserved = db.check_reserved_keyword
|
check_reserved = db.check_reserved_keyword
|
||||||
else:
|
else:
|
||||||
def check_reserved(field_name):
|
def check_reserved(field_name):
|
||||||
if field_name in reserved:
|
if field_name in reserved:
|
||||||
raise SyntaxError("field name %s not allowed" % field_name)
|
raise SyntaxError("field name %s not allowed" % field_name)
|
||||||
for field in fields:
|
for field in fields:
|
||||||
field_name = field.name
|
field_name = field.name
|
||||||
check_reserved(field_name)
|
check_reserved(field_name)
|
||||||
@@ -8725,8 +8732,8 @@ class Table(object):
|
|||||||
else:
|
else:
|
||||||
fname_item = field_name
|
fname_item = field_name
|
||||||
if fname_item in fieldnames_set:
|
if fname_item in fieldnames_set:
|
||||||
raise SyntaxError("duplicate field %s in table %s" \
|
raise SyntaxError("duplicate field %s in table %s" %
|
||||||
% (field_name, tablename))
|
(field_name, tablename))
|
||||||
else:
|
else:
|
||||||
fieldnames_set.add(fname_item)
|
fieldnames_set.add(fname_item)
|
||||||
|
|
||||||
@@ -8743,7 +8750,8 @@ class Table(object):
|
|||||||
for k in _primarykey:
|
for k in _primarykey:
|
||||||
if k not in self.fields:
|
if k not in self.fields:
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
"primarykey must be a list of fields from table '%s " % tablename)
|
"primarykey must be a list of fields from table '%s " %
|
||||||
|
tablename)
|
||||||
else:
|
else:
|
||||||
self[k].notnull = True
|
self[k].notnull = True
|
||||||
for field in virtual_fields:
|
for field in virtual_fields:
|
||||||
@@ -8753,52 +8761,53 @@ class Table(object):
|
|||||||
def fields(self):
|
def fields(self):
|
||||||
return self._fields
|
return self._fields
|
||||||
|
|
||||||
def update(self,*args,**kwargs):
|
def update(self, *args, **kwargs):
|
||||||
raise RuntimeError("Syntax Not Supported")
|
raise RuntimeError("Syntax Not Supported")
|
||||||
|
|
||||||
def _enable_record_versioning(self,
|
def _enable_record_versioning(self,
|
||||||
archive_db=None,
|
archive_db=None,
|
||||||
archive_name = '%(tablename)s_archive',
|
archive_name='%(tablename)s_archive',
|
||||||
is_active = 'is_active',
|
is_active='is_active',
|
||||||
current_record = 'current_record',
|
current_record='current_record',
|
||||||
current_record_label = None):
|
current_record_label=None):
|
||||||
db = self._db
|
db = self._db
|
||||||
archive_db = archive_db or db
|
archive_db = archive_db or db
|
||||||
archive_name = archive_name % dict(tablename=self._tablename)
|
archive_name = archive_name % dict(tablename=self._tablename)
|
||||||
if archive_name in archive_db.tables():
|
if archive_name in archive_db.tables():
|
||||||
return # do not try define the archive if already exists
|
return # do not try define the archive if already exists
|
||||||
fieldnames = self.fields()
|
fieldnames = self.fields()
|
||||||
same_db = archive_db is db
|
same_db = archive_db is db
|
||||||
field_type = self if same_db else 'bigint'
|
field_type = self if same_db else 'bigint'
|
||||||
clones = []
|
clones = []
|
||||||
for field in self:
|
for field in self:
|
||||||
nfk = same_db or not field.type.startswith('reference')
|
nfk = same_db or not field.type.startswith('reference')
|
||||||
clones.append(field.clone(
|
clones.append(
|
||||||
unique=False, type=field.type if nfk else 'bigint'))
|
field.clone(unique=False, type=field.type if nfk else 'bigint')
|
||||||
|
)
|
||||||
archive_db.define_table(
|
archive_db.define_table(
|
||||||
archive_name,
|
archive_name,
|
||||||
Field(current_record,field_type,label=current_record_label),
|
Field(current_record, field_type, label=current_record_label),
|
||||||
*clones,**dict(format=self._format))
|
*clones, **dict(format=self._format))
|
||||||
|
|
||||||
self._before_update.append(
|
self._before_update.append(
|
||||||
lambda qset,fs,db=archive_db,an=archive_name,cn=current_record:
|
lambda qset, fs, db=archive_db, an=archive_name, cn=current_record:
|
||||||
archive_record(qset,fs,db[an],cn))
|
archive_record(qset, fs, db[an], cn))
|
||||||
if is_active and is_active in fieldnames:
|
if is_active and is_active in fieldnames:
|
||||||
self._before_delete.append(
|
self._before_delete.append(
|
||||||
lambda qset: qset.update(is_active=False))
|
lambda qset: qset.update(is_active=False))
|
||||||
newquery = lambda query, t=self, name=self._tablename: \
|
newquery = lambda query, t=self, name=self._tablename: \
|
||||||
reduce(AND,[db[tn].is_active == True
|
reduce(AND, [db[tn].is_active == True
|
||||||
for tn in db._adapter.tables(query)
|
for tn in db._adapter.tables(query)
|
||||||
if tn==name or getattr(db[tn],'_ot',None)==name])
|
if tn == name or getattr(db[tn],'_ot',None)==name])
|
||||||
query = self._common_filter
|
query = self._common_filter
|
||||||
if query:
|
if query:
|
||||||
newquery = query & newquery
|
newquery = query & newquery
|
||||||
self._common_filter = newquery
|
self._common_filter = newquery
|
||||||
|
|
||||||
def _validate(self,**vars):
|
def _validate(self, **vars):
|
||||||
errors = Row()
|
errors = Row()
|
||||||
for key,value in vars.iteritems():
|
for key, value in vars.iteritems():
|
||||||
value,error = self[key].validate(value)
|
value, error = self[key].validate(value)
|
||||||
if error:
|
if error:
|
||||||
errors[key] = error
|
errors[key] = error
|
||||||
return errors
|
return errors
|
||||||
@@ -8809,28 +8818,29 @@ class Table(object):
|
|||||||
self._referenced_by = []
|
self._referenced_by = []
|
||||||
self._references = []
|
self._references = []
|
||||||
for field in self:
|
for field in self:
|
||||||
fieldname = field.name
|
#fieldname = field.name ##FIXME not used ?
|
||||||
field_type = field.type
|
field_type = field.type
|
||||||
if isinstance(field_type,str) and field_type[:10] == 'reference ':
|
if isinstance(field_type, str) and field_type[:10] == 'reference ':
|
||||||
ref = field_type[10:].strip()
|
ref = field_type[10:].strip()
|
||||||
if not ref:
|
if not ref:
|
||||||
SyntaxError('Table: reference to nothing: %s' %ref)
|
SyntaxError('Table: reference to nothing: %s' % ref)
|
||||||
if '.' in ref:
|
if '.' in ref:
|
||||||
rtablename, throw_it,rfieldname = ref.partition('.')
|
rtablename, throw_it, rfieldname = ref.partition('.')
|
||||||
else:
|
else:
|
||||||
rtablename, rfieldname = ref, None
|
rtablename, rfieldname = ref, None
|
||||||
if not rtablename in db:
|
if not rtablename in db:
|
||||||
pr[rtablename] = pr.get(rtablename,[]) + [field]
|
pr[rtablename] = pr.get(rtablename, []) + [field]
|
||||||
continue
|
continue
|
||||||
rtable = db[rtablename]
|
rtable = db[rtablename]
|
||||||
if rfieldname:
|
if rfieldname:
|
||||||
if not hasattr(rtable,'_primarykey'):
|
if not hasattr(rtable, '_primarykey'):
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
'keyed tables can only reference other keyed tables (for now)')
|
'keyed tables can only reference other keyed tables (for now)')
|
||||||
if rfieldname not in rtable.fields:
|
if rfieldname not in rtable.fields:
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
"invalid field '%s' for referenced table '%s' in table '%s'" \
|
"invalid field '%s' for referenced table '%s'"
|
||||||
% (rfieldname, rtablename, self._tablename))
|
" in table '%s'" % (rfieldname, rtablename, self._tablename)
|
||||||
|
)
|
||||||
rfield = rtable[rfieldname]
|
rfield = rtable[rfieldname]
|
||||||
else:
|
else:
|
||||||
rfield = rtable._id
|
rfield = rtable._id
|
||||||
@@ -8844,7 +8854,6 @@ class Table(object):
|
|||||||
for referee in referees:
|
for referee in referees:
|
||||||
self._referenced_by.append(referee)
|
self._referenced_by.append(referee)
|
||||||
|
|
||||||
|
|
||||||
def _filter_fields(self, record, id=False):
|
def _filter_fields(self, record, id=False):
|
||||||
return dict([(k, v) for (k, v) in record.iteritems() if k
|
return dict([(k, v) for (k, v) in record.iteritems() if k
|
||||||
in self.fields and (self[k].type!='id' or id)])
|
in self.fields and (self[k].type!='id' or id)])
|
||||||
@@ -8860,8 +8869,9 @@ class Table(object):
|
|||||||
query = (self[k] == v)
|
query = (self[k] == v)
|
||||||
else:
|
else:
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
'Field %s is not part of the primary key of %s' % \
|
'Field %s is not part of the primary key of %s' %
|
||||||
(k,self._tablename))
|
(k,self._tablename)
|
||||||
|
)
|
||||||
return query
|
return query
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
@@ -8870,18 +8880,20 @@ class Table(object):
|
|||||||
elif isinstance(key, dict):
|
elif isinstance(key, dict):
|
||||||
""" for keyed table """
|
""" for keyed table """
|
||||||
query = self._build_query(key)
|
query = self._build_query(key)
|
||||||
return self._db(query).select(limitby=(0,1), orderby_on_limitby=False).first()
|
return self._db(query).select(limitby=(0, 1), orderby_on_limitby=False).first()
|
||||||
elif str(key).isdigit() or 'google' in DRIVERS and isinstance(key, Key):
|
elif str(key).isdigit() or 'google' in DRIVERS and isinstance(key, Key):
|
||||||
return self._db(self._id == key).select(limitby=(0,1), orderby_on_limitby=False).first()
|
return self._db(self._id == key).select(limitby=(0, 1), orderby_on_limitby=False).first()
|
||||||
elif key:
|
elif key:
|
||||||
return ogetattr(self, str(key))
|
return ogetattr(self, str(key))
|
||||||
|
|
||||||
def __call__(self, key=DEFAULT, **kwargs):
|
def __call__(self, key=DEFAULT, **kwargs):
|
||||||
for_update = kwargs.get('_for_update',False)
|
for_update = kwargs.get('_for_update', False)
|
||||||
if '_for_update' in kwargs: del kwargs['_for_update']
|
if '_for_update' in kwargs:
|
||||||
|
del kwargs['_for_update']
|
||||||
|
|
||||||
orderby = kwargs.get('_orderby',None)
|
orderby = kwargs.get('_orderby', None)
|
||||||
if '_orderby' in kwargs: del kwargs['_orderby']
|
if '_orderby' in kwargs:
|
||||||
|
del kwargs['_orderby']
|
||||||
|
|
||||||
if not key is DEFAULT:
|
if not key is DEFAULT:
|
||||||
if isinstance(key, Query):
|
if isinstance(key, Query):
|
||||||
@@ -8915,7 +8927,7 @@ class Table(object):
|
|||||||
self._db(query).update(**self._filter_fields(value))
|
self._db(query).update(**self._filter_fields(value))
|
||||||
else:
|
else:
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
'key must have all fields from primary key: %s'%\
|
'key must have all fields from primary key: %s'%
|
||||||
(self._primarykey))
|
(self._primarykey))
|
||||||
elif str(key).isdigit():
|
elif str(key).isdigit():
|
||||||
if key == 0:
|
if key == 0:
|
||||||
@@ -8946,7 +8958,7 @@ class Table(object):
|
|||||||
raise SyntaxError('No such record: %s' % key)
|
raise SyntaxError('No such record: %s' % key)
|
||||||
|
|
||||||
def __contains__(self,key):
|
def __contains__(self,key):
|
||||||
return hasattr(self,key)
|
return hasattr(self, key)
|
||||||
|
|
||||||
has_key = __contains__
|
has_key = __contains__
|
||||||
|
|
||||||
@@ -8960,9 +8972,8 @@ class Table(object):
|
|||||||
def iteritems(self):
|
def iteritems(self):
|
||||||
return self.__dict__.iteritems()
|
return self.__dict__.iteritems()
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Table %s (%s)>' % (self._tablename,','.join(self.fields()))
|
return '<Table %s (%s)>' % (self._tablename, ','.join(self.fields()))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self._ot is not None:
|
if self._ot is not None:
|
||||||
@@ -8987,14 +8998,14 @@ class Table(object):
|
|||||||
return self._db._adapter.sqlsafe_table(self._tablename, self._ot)
|
return self._db._adapter.sqlsafe_table(self._tablename, self._ot)
|
||||||
|
|
||||||
|
|
||||||
def _drop(self, mode = ''):
|
def _drop(self, mode=''):
|
||||||
return self._db._adapter._drop(self, mode)
|
return self._db._adapter._drop(self, mode)
|
||||||
|
|
||||||
def drop(self, mode = ''):
|
def drop(self, mode=''):
|
||||||
return self._db._adapter.drop(self,mode)
|
return self._db._adapter.drop(self,mode)
|
||||||
|
|
||||||
def _listify(self,fields,update=False):
|
def _listify(self,fields,update=False):
|
||||||
new_fields = {} # format: new_fields[name] = (field,value)
|
new_fields = {} # format: new_fields[name] = (field,value)
|
||||||
|
|
||||||
# store all fields passed as input in new_fields
|
# store all fields passed as input in new_fields
|
||||||
for name in fields:
|
for name in fields:
|
||||||
@@ -9007,7 +9018,7 @@ class Table(object):
|
|||||||
value = fields[name]
|
value = fields[name]
|
||||||
if field.filter_in:
|
if field.filter_in:
|
||||||
value = field.filter_in(value)
|
value = field.filter_in(value)
|
||||||
new_fields[name] = (field,value)
|
new_fields[name] = (field, value)
|
||||||
|
|
||||||
# check all fields that should be in the table but are not passed
|
# check all fields that should be in the table but are not passed
|
||||||
to_compute = []
|
to_compute = []
|
||||||
@@ -9015,18 +9026,18 @@ class Table(object):
|
|||||||
name = ofield.name
|
name = ofield.name
|
||||||
if not name in new_fields:
|
if not name in new_fields:
|
||||||
# if field is supposed to be computed, compute it!
|
# if field is supposed to be computed, compute it!
|
||||||
if ofield.compute: # save those to compute for later
|
if ofield.compute: # save those to compute for later
|
||||||
to_compute.append((name,ofield))
|
to_compute.append((name, ofield))
|
||||||
# if field is required, check its default value
|
# if field is required, check its default value
|
||||||
elif not update and not ofield.default is None:
|
elif not update and not ofield.default is None:
|
||||||
value = ofield.default
|
value = ofield.default
|
||||||
fields[name] = value
|
fields[name] = value
|
||||||
new_fields[name] = (ofield,value)
|
new_fields[name] = (ofield, value)
|
||||||
# if this is an update, user the update field instead
|
# if this is an update, user the update field instead
|
||||||
elif update and not ofield.update is None:
|
elif update and not ofield.update is None:
|
||||||
value = ofield.update
|
value = ofield.update
|
||||||
fields[name] = value
|
fields[name] = value
|
||||||
new_fields[name] = (ofield,value)
|
new_fields[name] = (ofield, value)
|
||||||
# if the field is still not there but it should, error
|
# if the field is still not there but it should, error
|
||||||
elif not update and ofield.required:
|
elif not update and ofield.required:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
@@ -9034,7 +9045,7 @@ class Table(object):
|
|||||||
# now deal with fields that are supposed to be computed
|
# now deal with fields that are supposed to be computed
|
||||||
if to_compute:
|
if to_compute:
|
||||||
row = Row(fields)
|
row = Row(fields)
|
||||||
for name,ofield in to_compute:
|
for name, ofield in to_compute:
|
||||||
# try compute it
|
# try compute it
|
||||||
try:
|
try:
|
||||||
row[name] = new_value = ofield.compute(row)
|
row[name] = new_value = ofield.compute(row)
|
||||||
@@ -9047,13 +9058,13 @@ class Table(object):
|
|||||||
|
|
||||||
def _attempt_upload(self, fields):
|
def _attempt_upload(self, fields):
|
||||||
for field in self:
|
for field in self:
|
||||||
if field.type=='upload' and field.name in fields:
|
if field.type == 'upload' and field.name in fields:
|
||||||
value = fields[field.name]
|
value = fields[field.name]
|
||||||
if value is not None and not isinstance(value,str):
|
if value is not None and not isinstance(value, str):
|
||||||
if hasattr(value,'file') and hasattr(value,'filename'):
|
if hasattr(value, 'file') and hasattr(value, 'filename'):
|
||||||
new_name = field.store(value.file,filename=value.filename)
|
new_name = field.store(value.file, filename=value.filename)
|
||||||
elif hasattr(value,'read') and hasattr(value,'name'):
|
elif hasattr(value, 'read') and hasattr(value, 'name'):
|
||||||
new_name = field.store(value,filename=value.name)
|
new_name = field.store(value, filename=value.name)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Unable to handle upload")
|
raise RuntimeError("Unable to handle upload")
|
||||||
fields[field.name] = new_name
|
fields[field.name] = new_name
|
||||||
@@ -9081,7 +9092,7 @@ class Table(object):
|
|||||||
[f(fields,ret) for f in self._after_insert]
|
[f(fields,ret) for f in self._after_insert]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def validate_and_insert(self,**fields):
|
def validate_and_insert(self, **fields):
|
||||||
response = Row()
|
response = Row()
|
||||||
response.errors = Row()
|
response.errors = Row()
|
||||||
new_fields = copy.copy(fields)
|
new_fields = copy.copy(fields)
|
||||||
@@ -9102,22 +9113,22 @@ class Table(object):
|
|||||||
response.errors = Row()
|
response.errors = Row()
|
||||||
new_fields = copy.copy(fields)
|
new_fields = copy.copy(fields)
|
||||||
|
|
||||||
for key,value in fields.iteritems():
|
for key, value in fields.iteritems():
|
||||||
value,error = self[key].validate(value)
|
value, error = self[key].validate(value)
|
||||||
if error:
|
if error:
|
||||||
response.errors[key] = "%s" % error
|
response.errors[key] = "%s" % error
|
||||||
else:
|
else:
|
||||||
new_fields[key] = value
|
new_fields[key] = value
|
||||||
|
|
||||||
if _key is DEFAULT:
|
if _key is DEFAULT:
|
||||||
record = self(**values)
|
record = self(**values)
|
||||||
elif isinstance(_key,dict):
|
elif isinstance(_key, dict):
|
||||||
record = self(**_key)
|
record = self(**_key)
|
||||||
else:
|
else:
|
||||||
record = self(_key)
|
record = self(_key)
|
||||||
|
|
||||||
if not response.errors and record:
|
if not response.errors and record:
|
||||||
row = self._db(self._id==_key)
|
row = self._db(self._id ==_key)
|
||||||
response.id = row.update(**fields)
|
response.id = row.update(**fields)
|
||||||
else:
|
else:
|
||||||
response.id = None
|
response.id = None
|
||||||
@@ -9126,7 +9137,7 @@ class Table(object):
|
|||||||
def update_or_insert(self, _key=DEFAULT, **values):
|
def update_or_insert(self, _key=DEFAULT, **values):
|
||||||
if _key is DEFAULT:
|
if _key is DEFAULT:
|
||||||
record = self(**values)
|
record = self(**values)
|
||||||
elif isinstance(_key,dict):
|
elif isinstance(_key, dict):
|
||||||
record = self(**_key)
|
record = self(**_key)
|
||||||
else:
|
else:
|
||||||
record = self(_key)
|
record = self(_key)
|
||||||
@@ -9147,10 +9158,10 @@ class Table(object):
|
|||||||
ret and [[f(item,ret[k]) for k,item in enumerate(items)] for f in self._after_insert]
|
ret and [[f(item,ret[k]) for k,item in enumerate(items)] for f in self._after_insert]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _truncate(self, mode = None):
|
def _truncate(self, mode=None):
|
||||||
return self._db._adapter._truncate(self, mode)
|
return self._db._adapter._truncate(self, mode)
|
||||||
|
|
||||||
def truncate(self, mode = None):
|
def truncate(self, mode=None):
|
||||||
return self._db._adapter.truncate(self, mode)
|
return self._db._adapter.truncate(self, mode)
|
||||||
|
|
||||||
def import_from_csv_file(
|
def import_from_csv_file(
|
||||||
@@ -9159,7 +9170,7 @@ class Table(object):
|
|||||||
id_map=None,
|
id_map=None,
|
||||||
null='<NULL>',
|
null='<NULL>',
|
||||||
unique='uuid',
|
unique='uuid',
|
||||||
id_offset=None, # id_offset used only when id_map is None
|
id_offset=None, # id_offset used only when id_map is None
|
||||||
*args, **kwargs
|
*args, **kwargs
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@@ -9295,11 +9306,15 @@ class Table(object):
|
|||||||
id_map_self[long(line[cid])] = new_id
|
id_map_self[long(line[cid])] = new_id
|
||||||
|
|
||||||
def as_dict(self, flat=False, sanitize=True):
|
def as_dict(self, flat=False, sanitize=True):
|
||||||
table_as_dict = dict(tablename=str(self), fields=[],
|
table_as_dict = dict(
|
||||||
sequence_name=self._sequence_name,
|
tablename=str(self),
|
||||||
trigger_name=self._trigger_name,
|
fields=[],
|
||||||
common_filter=self._common_filter, format=self._format,
|
sequence_name=self._sequence_name,
|
||||||
singular=self._singular, plural=self._plural)
|
trigger_name=self._trigger_name,
|
||||||
|
common_filter=self._common_filter,
|
||||||
|
format=self._format,
|
||||||
|
singular=self._singular,
|
||||||
|
plural=self._plural)
|
||||||
|
|
||||||
for field in self:
|
for field in self:
|
||||||
if (field.readable or field.writable) or (not sanitize):
|
if (field.readable or field.writable) or (not sanitize):
|
||||||
@@ -9326,15 +9341,16 @@ class Table(object):
|
|||||||
return serializers.yaml(d)
|
return serializers.yaml(d)
|
||||||
|
|
||||||
def with_alias(self, alias):
|
def with_alias(self, alias):
|
||||||
return self._db._adapter.alias(self,alias)
|
return self._db._adapter.alias(self, alias)
|
||||||
|
|
||||||
def on(self, query):
|
def on(self, query):
|
||||||
return Expression(self._db,self._db._adapter.ON,self,query)
|
return Expression(self._db, self._db._adapter.ON, self, query)
|
||||||
|
|
||||||
def archive_record(qset,fs,archive_table,current_record):
|
|
||||||
|
def archive_record(qset, fs, archive_table, current_record):
|
||||||
tablenames = qset.db._adapter.tables(qset.query)
|
tablenames = qset.db._adapter.tables(qset.query)
|
||||||
if len(tablenames)!=1: raise RuntimeError("cannot update join")
|
if len(tablenames) != 1:
|
||||||
table = qset.db[tablenames[0]]
|
raise RuntimeError("cannot update join")
|
||||||
for row in qset.select():
|
for row in qset.select():
|
||||||
fields = archive_table._filter_fields(row)
|
fields = archive_table._filter_fields(row)
|
||||||
fields[current_record] = row.id
|
fields[current_record] = row.id
|
||||||
@@ -9342,7 +9358,6 @@ def archive_record(qset,fs,archive_table,current_record):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Expression(object):
|
class Expression(object):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -9399,9 +9414,9 @@ class Expression(object):
|
|||||||
db = self.db
|
db = self.db
|
||||||
return Expression(db, db._adapter.UPPER, self, None, self.type)
|
return Expression(db, db._adapter.UPPER, self, None, self.type)
|
||||||
|
|
||||||
def replace(self,a,b):
|
def replace(self, a, b):
|
||||||
db = self.db
|
db = self.db
|
||||||
return Expression(db, db._adapter.REPLACE, self, (a,b), self.type)
|
return Expression(db, db._adapter.REPLACE, self, (a, b), self.type)
|
||||||
|
|
||||||
def year(self):
|
def year(self):
|
||||||
db = self.db
|
db = self.db
|
||||||
@@ -9423,7 +9438,7 @@ class Expression(object):
|
|||||||
db = self.db
|
db = self.db
|
||||||
return Expression(db, db._adapter.EXTRACT, self, 'minute', 'integer')
|
return Expression(db, db._adapter.EXTRACT, self, 'minute', 'integer')
|
||||||
|
|
||||||
def coalesce(self,*others):
|
def coalesce(self, *others):
|
||||||
db = self.db
|
db = self.db
|
||||||
return Expression(db, db._adapter.COALESCE, self, others, self.type)
|
return Expression(db, db._adapter.COALESCE, self, others, self.type)
|
||||||
|
|
||||||
@@ -9452,32 +9467,32 @@ class Expression(object):
|
|||||||
length = self.len()
|
length = self.len()
|
||||||
else:
|
else:
|
||||||
length = '(%s - %s)' % (stop + 1, pos0)
|
length = '(%s - %s)' % (stop + 1, pos0)
|
||||||
return Expression(db,db._adapter.SUBSTRING,
|
return Expression(db, db._adapter.SUBSTRING,
|
||||||
self, (pos0, length), self.type)
|
self, (pos0, length), self.type)
|
||||||
|
|
||||||
def __getitem__(self, i):
|
def __getitem__(self, i):
|
||||||
return self[i:i + 1]
|
return self[i:i + 1]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.db._adapter.expand(self,self.type)
|
return self.db._adapter.expand(self, self.type)
|
||||||
|
|
||||||
def __or__(self, other): # for use in sortby
|
def __or__(self, other): # for use in sortby
|
||||||
db = self.db
|
db = self.db
|
||||||
return Expression(db,db._adapter.COMMA,self,other,self.type)
|
return Expression(db, db._adapter.COMMA, self, other, self.type)
|
||||||
|
|
||||||
def __invert__(self):
|
def __invert__(self):
|
||||||
db = self.db
|
db = self.db
|
||||||
if hasattr(self,'_op') and self.op == db._adapter.INVERT:
|
if hasattr(self,'_op') and self.op == db._adapter.INVERT:
|
||||||
return self.first
|
return self.first
|
||||||
return Expression(db,db._adapter.INVERT,self,type=self.type)
|
return Expression(db, db._adapter.INVERT, self, type=self.type)
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
db = self.db
|
db = self.db
|
||||||
return Expression(db,db._adapter.ADD,self,other,self.type)
|
return Expression(db, db._adapter.ADD, self, other, self.type)
|
||||||
|
|
||||||
def __sub__(self, other):
|
def __sub__(self, other):
|
||||||
db = self.db
|
db = self.db
|
||||||
if self.type in ('integer','bigint'):
|
if self.type in ('integer', 'bigint'):
|
||||||
result_type = 'integer'
|
result_type = 'integer'
|
||||||
elif self.type in ['date','time','datetime','double','float']:
|
elif self.type in ['date','time','datetime','double','float']:
|
||||||
result_type = 'double'
|
result_type = 'double'
|
||||||
@@ -9724,20 +9739,23 @@ class FieldVirtual(object):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s.%s' % (self.tablename, self.name)
|
return '%s.%s' % (self.tablename, self.name)
|
||||||
|
|
||||||
|
|
||||||
class FieldMethod(object):
|
class FieldMethod(object):
|
||||||
def __init__(self, name, f=None, handler=None):
|
def __init__(self, name, f=None, handler=None):
|
||||||
# for backward compatibility
|
# for backward compatibility
|
||||||
(self.name, self.f) = (name, f) if f else ('unknown', name)
|
(self.name, self.f) = (name, f) if f else ('unknown', name)
|
||||||
self.handler = handler
|
self.handler = handler
|
||||||
|
|
||||||
|
|
||||||
def list_represent(x,r=None):
|
def list_represent(x,r=None):
|
||||||
return ', '.join(str(y) for y in x or [])
|
return ', '.join(str(y) for y in x or [])
|
||||||
|
|
||||||
|
|
||||||
class Field(Expression):
|
class Field(Expression):
|
||||||
|
|
||||||
Virtual = FieldVirtual
|
Virtual = FieldVirtual
|
||||||
Method = FieldMethod
|
Method = FieldMethod
|
||||||
Lazy = FieldMethod # for backward compatibility
|
Lazy = FieldMethod # for backward compatibility
|
||||||
|
|
||||||
"""
|
"""
|
||||||
an instance of this class represents a database field
|
an instance of this class represents a database field
|
||||||
@@ -9798,13 +9816,13 @@ class Field(Expression):
|
|||||||
custom_retrieve=None,
|
custom_retrieve=None,
|
||||||
custom_retrieve_file_properties=None,
|
custom_retrieve_file_properties=None,
|
||||||
custom_delete=None,
|
custom_delete=None,
|
||||||
filter_in = None,
|
filter_in=None,
|
||||||
filter_out = None,
|
filter_out=None,
|
||||||
custom_qualifier = None,
|
custom_qualifier=None,
|
||||||
map_none = None,
|
map_none=None,
|
||||||
rname = None
|
rname=None
|
||||||
):
|
):
|
||||||
self._db = self.db = None # both for backward compatibility
|
self._db = self.db = None # both for backward compatibility
|
||||||
self.op = None
|
self.op = None
|
||||||
self.first = None
|
self.first = None
|
||||||
self.second = None
|
self.second = None
|
||||||
@@ -9815,16 +9833,18 @@ class Field(Expression):
|
|||||||
raise SyntaxError('Field: invalid unicode field name')
|
raise SyntaxError('Field: invalid unicode field name')
|
||||||
self.name = fieldname = cleanup(fieldname)
|
self.name = fieldname = cleanup(fieldname)
|
||||||
if not isinstance(fieldname, str) or hasattr(Table, fieldname) or \
|
if not isinstance(fieldname, str) or hasattr(Table, fieldname) or \
|
||||||
fieldname[0] == '_' or REGEX_PYTHON_KEYWORDS.match(fieldname):
|
fieldname[0] == '_' or '.' in fieldname or \
|
||||||
raise SyntaxError('Field: invalid field name: %s' % fieldname)
|
REGEX_PYTHON_KEYWORDS.match(fieldname):
|
||||||
|
raise SyntaxError('Field: invalid field name: %s, '
|
||||||
|
'use rname for "funny" names' % fieldname)
|
||||||
|
|
||||||
if not isinstance(type, (Table,Field)):
|
if not isinstance(type, (Table, Field)):
|
||||||
self.type = type
|
self.type = type
|
||||||
else:
|
else:
|
||||||
self.type = 'reference %s' % type
|
self.type = 'reference %s' % type
|
||||||
|
|
||||||
self.length = length if not length is None else DEFAULTLENGTH.get(self.type,512)
|
self.length = length if not length is None else DEFAULTLENGTH.get(self.type, 512)
|
||||||
self.default = default if default!=DEFAULT else (update or None)
|
self.default = default if default != DEFAULT else (update or None)
|
||||||
self.required = required # is this field required
|
self.required = required # is this field required
|
||||||
self.ondelete = ondelete.upper() # this is for reference fields only
|
self.ondelete = ondelete.upper() # this is for reference fields only
|
||||||
self.notnull = notnull
|
self.notnull = notnull
|
||||||
@@ -9840,8 +9860,8 @@ class Field(Expression):
|
|||||||
self.update = update
|
self.update = update
|
||||||
self.authorize = authorize
|
self.authorize = authorize
|
||||||
self.autodelete = autodelete
|
self.autodelete = autodelete
|
||||||
self.represent = list_represent if \
|
self.represent = (list_represent if represent is None and
|
||||||
represent==None and type in ('list:integer','list:string') else represent
|
type in ('list:integer', 'list:string') else represent)
|
||||||
self.compute = compute
|
self.compute = compute
|
||||||
self.isattachment = True
|
self.isattachment = True
|
||||||
self.custom_store = custom_store
|
self.custom_store = custom_store
|
||||||
@@ -9851,15 +9871,16 @@ class Field(Expression):
|
|||||||
self.filter_in = filter_in
|
self.filter_in = filter_in
|
||||||
self.filter_out = filter_out
|
self.filter_out = filter_out
|
||||||
self.custom_qualifier = custom_qualifier
|
self.custom_qualifier = custom_qualifier
|
||||||
self.label = label if label!=None else fieldname.replace('_',' ').title()
|
self.label = (label if label is not None else
|
||||||
self.requires = requires if requires!=None else []
|
fieldname.replace('_', ' ').title())
|
||||||
|
self.requires = requires if requires is not None else []
|
||||||
self.map_none = map_none
|
self.map_none = map_none
|
||||||
self._rname = rname
|
self._rname = rname
|
||||||
|
|
||||||
def set_attributes(self,*args,**attributes):
|
def set_attributes(self, *args, **attributes):
|
||||||
self.__dict__.update(*args,**attributes)
|
self.__dict__.update(*args, **attributes)
|
||||||
|
|
||||||
def clone(self,point_self_references_to=False,**args):
|
def clone(self, point_self_references_to=False, **args):
|
||||||
field = copy.copy(self)
|
field = copy.copy(self)
|
||||||
if point_self_references_to and \
|
if point_self_references_to and \
|
||||||
field.type == 'reference %s'+field._tablename:
|
field.type == 'reference %s'+field._tablename:
|
||||||
@@ -9869,14 +9890,13 @@ class Field(Expression):
|
|||||||
|
|
||||||
def store(self, file, filename=None, path=None):
|
def store(self, file, filename=None, path=None):
|
||||||
if self.custom_store:
|
if self.custom_store:
|
||||||
return self.custom_store(file,filename,path)
|
return self.custom_store(file, filename, path)
|
||||||
if isinstance(file, cgi.FieldStorage):
|
if isinstance(file, cgi.FieldStorage):
|
||||||
filename = filename or file.filename
|
filename = filename or file.filename
|
||||||
file = file.file
|
file = file.file
|
||||||
elif not filename:
|
elif not filename:
|
||||||
filename = file.name
|
filename = file.name
|
||||||
filename = os.path.basename(filename.replace('/', os.sep)\
|
filename = os.path.basename(filename.replace('/', os.sep).replace('\\', os.sep))
|
||||||
.replace('\\', os.sep))
|
|
||||||
m = REGEX_STORE_PATTERN.search(filename)
|
m = REGEX_STORE_PATTERN.search(filename)
|
||||||
extension = m and m.group('e') or 'txt'
|
extension = m and m.group('e') or 'txt'
|
||||||
uuid_key = web2py_uuid().replace('-', '')[-16:]
|
uuid_key = web2py_uuid().replace('-', '')[-16:]
|
||||||
@@ -9885,12 +9905,12 @@ class Field(Expression):
|
|||||||
(self._tablename, self.name, uuid_key, encoded_filename)
|
(self._tablename, self.name, uuid_key, encoded_filename)
|
||||||
newfilename = newfilename[:(self.length - 1 - len(extension))] + '.' + extension
|
newfilename = newfilename[:(self.length - 1 - len(extension))] + '.' + extension
|
||||||
self_uploadfield = self.uploadfield
|
self_uploadfield = self.uploadfield
|
||||||
if isinstance(self_uploadfield,Field):
|
if isinstance(self_uploadfield, Field):
|
||||||
blob_uploadfield_name = self_uploadfield.uploadfield
|
blob_uploadfield_name = self_uploadfield.uploadfield
|
||||||
keys={self_uploadfield.name: newfilename,
|
keys = {self_uploadfield.name: newfilename,
|
||||||
blob_uploadfield_name: file.read()}
|
blob_uploadfield_name: file.read()}
|
||||||
self_uploadfield.table.insert(**keys)
|
self_uploadfield.table.insert(**keys)
|
||||||
elif self_uploadfield == True:
|
elif self_uploadfield is True:
|
||||||
if path:
|
if path:
|
||||||
pass
|
pass
|
||||||
elif self.uploadfolder:
|
elif self.uploadfolder:
|
||||||
@@ -9903,8 +9923,9 @@ class Field(Expression):
|
|||||||
if self.uploadseparate:
|
if self.uploadseparate:
|
||||||
if self.uploadfs:
|
if self.uploadfs:
|
||||||
raise RuntimeError("not supported")
|
raise RuntimeError("not supported")
|
||||||
path = pjoin(path,"%s.%s" %(self._tablename, self.name),
|
path = pjoin(path, "%s.%s" % (
|
||||||
uuid_key[:2])
|
self._tablename, self.name), uuid_key[:2]
|
||||||
|
)
|
||||||
if not exists(path):
|
if not exists(path):
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
pathfilename = pjoin(path, newfilename)
|
pathfilename = pjoin(path, newfilename)
|
||||||
@@ -9916,7 +9937,8 @@ class Field(Expression):
|
|||||||
shutil.copyfileobj(file, dest_file)
|
shutil.copyfileobj(file, dest_file)
|
||||||
except IOError:
|
except IOError:
|
||||||
raise IOError(
|
raise IOError(
|
||||||
'Unable to store file "%s" because invalid permissions, readonly file system, or filename too long' % pathfilename)
|
'Unable to store file "%s" because invalid permissions, '
|
||||||
|
'readonly file system, or filename too long' % pathfilename)
|
||||||
dest_file.close()
|
dest_file.close()
|
||||||
return newfilename
|
return newfilename
|
||||||
|
|
||||||
@@ -9935,11 +9957,11 @@ class Field(Expression):
|
|||||||
raise http.HTTP(404)
|
raise http.HTTP(404)
|
||||||
if self.authorize and not self.authorize(row):
|
if self.authorize and not self.authorize(row):
|
||||||
raise http.HTTP(403)
|
raise http.HTTP(403)
|
||||||
file_properties = self.retrieve_file_properties(name,path)
|
file_properties = self.retrieve_file_properties(name, path)
|
||||||
filename = file_properties['filename']
|
filename = file_properties['filename']
|
||||||
if isinstance(self_uploadfield, str): # ## if file is in DB
|
if isinstance(self_uploadfield, str): # ## if file is in DB
|
||||||
stream = StringIO.StringIO(row[self_uploadfield] or '')
|
stream = StringIO.StringIO(row[self_uploadfield] or '')
|
||||||
elif isinstance(self_uploadfield,Field):
|
elif isinstance(self_uploadfield, Field):
|
||||||
blob_uploadfield_name = self_uploadfield.uploadfield
|
blob_uploadfield_name = self_uploadfield.uploadfield
|
||||||
query = self_uploadfield == name
|
query = self_uploadfield == name
|
||||||
data = self_uploadfield.table(query)[blob_uploadfield_name]
|
data = self_uploadfield.table(query)[blob_uploadfield_name]
|
||||||
@@ -9951,10 +9973,10 @@ class Field(Expression):
|
|||||||
# ## if file is on regular filesystem
|
# ## if file is on regular filesystem
|
||||||
# this is intentially a sting with filename and not a stream
|
# this is intentially a sting with filename and not a stream
|
||||||
# this propagates and allows stream_file_or_304_or_206 to be called
|
# this propagates and allows stream_file_or_304_or_206 to be called
|
||||||
fullname = pjoin(file_properties['path'],name)
|
fullname = pjoin(file_properties['path'], name)
|
||||||
if nameonly:
|
if nameonly:
|
||||||
return (filename, fullname)
|
return (filename, fullname)
|
||||||
stream = open(fullname,'rb')
|
stream = open(fullname, 'rb')
|
||||||
return (filename, stream)
|
return (filename, stream)
|
||||||
|
|
||||||
def retrieve_file_properties(self, name, path=None):
|
def retrieve_file_properties(self, name, path=None):
|
||||||
@@ -9974,7 +9996,7 @@ class Field(Expression):
|
|||||||
filename = name
|
filename = name
|
||||||
# ## if file is in DB
|
# ## if file is in DB
|
||||||
if isinstance(self_uploadfield, (str, Field)):
|
if isinstance(self_uploadfield, (str, Field)):
|
||||||
return dict(path=None,filename=filename)
|
return dict(path=None, filename=filename)
|
||||||
# ## if file is on filesystem
|
# ## if file is on filesystem
|
||||||
if not path:
|
if not path:
|
||||||
if self.uploadfolder:
|
if self.uploadfolder:
|
||||||
@@ -9985,9 +10007,8 @@ class Field(Expression):
|
|||||||
t = m.group('table')
|
t = m.group('table')
|
||||||
f = m.group('field')
|
f = m.group('field')
|
||||||
u = m.group('uuidkey')
|
u = m.group('uuidkey')
|
||||||
path = pjoin(path,"%s.%s" % (t,f),u[:2])
|
path = pjoin(path, "%s.%s" % (t, f), u[:2])
|
||||||
return dict(path=path,filename=filename)
|
return dict(path=path, filename=filename)
|
||||||
|
|
||||||
|
|
||||||
def formatter(self, value):
|
def formatter(self, value):
|
||||||
requires = self.requires
|
requires = self.requires
|
||||||
@@ -10007,7 +10028,7 @@ class Field(Expression):
|
|||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if not self.requires or self.requires == DEFAULT:
|
if not self.requires or self.requires == DEFAULT:
|
||||||
return ((value if value!=self.map_none else None), None)
|
return ((value if value != self.map_none else None), None)
|
||||||
requires = self.requires
|
requires = self.requires
|
||||||
if not isinstance(requires, (list, tuple)):
|
if not isinstance(requires, (list, tuple)):
|
||||||
requires = [requires]
|
requires = [requires]
|
||||||
@@ -10015,27 +10036,27 @@ class Field(Expression):
|
|||||||
(value, error) = validator(value)
|
(value, error) = validator(value)
|
||||||
if error:
|
if error:
|
||||||
return (value, error)
|
return (value, error)
|
||||||
return ((value if value!=self.map_none else None), None)
|
return ((value if value != self.map_none else None), None)
|
||||||
|
|
||||||
def count(self, distinct=None):
|
def count(self, distinct=None):
|
||||||
return Expression(self.db, self.db._adapter.COUNT, self, distinct, 'integer')
|
return Expression(self.db, self.db._adapter.COUNT, self, distinct, 'integer')
|
||||||
|
|
||||||
def as_dict(self, flat=False, sanitize=True):
|
def as_dict(self, flat=False, sanitize=True):
|
||||||
attrs = ('name', 'authorize', 'represent', 'ondelete',
|
attrs = (
|
||||||
'custom_store', 'autodelete', 'custom_retrieve',
|
'name', 'authorize', 'represent', 'ondelete',
|
||||||
'filter_out', 'uploadseparate', 'widget', 'uploadfs',
|
'custom_store', 'autodelete', 'custom_retrieve',
|
||||||
'update', 'custom_delete', 'uploadfield', 'uploadfolder',
|
'filter_out', 'uploadseparate', 'widget', 'uploadfs',
|
||||||
'custom_qualifier', 'unique', 'writable', 'compute',
|
'update', 'custom_delete', 'uploadfield', 'uploadfolder',
|
||||||
'map_none', 'default', 'type', 'required', 'readable',
|
'custom_qualifier', 'unique', 'writable', 'compute',
|
||||||
'requires', 'comment', 'label', 'length', 'notnull',
|
'map_none', 'default', 'type', 'required', 'readable',
|
||||||
'custom_retrieve_file_properties', 'filter_in')
|
'requires', 'comment', 'label', 'length', 'notnull',
|
||||||
|
'custom_retrieve_file_properties', 'filter_in')
|
||||||
serializable = (int, long, basestring, float, tuple,
|
serializable = (int, long, basestring, float, tuple,
|
||||||
bool, type(None))
|
bool, type(None))
|
||||||
|
|
||||||
def flatten(obj):
|
def flatten(obj):
|
||||||
if isinstance(obj, dict):
|
if isinstance(obj, dict):
|
||||||
return dict((flatten(k), flatten(v)) for k, v in
|
return dict((flatten(k), flatten(v)) for k, v in obj.items())
|
||||||
obj.items())
|
|
||||||
elif isinstance(obj, (tuple, list, set)):
|
elif isinstance(obj, (tuple, list, set)):
|
||||||
return [flatten(v) for v in obj]
|
return [flatten(v) for v in obj]
|
||||||
elif isinstance(obj, serializable):
|
elif isinstance(obj, serializable):
|
||||||
@@ -10049,10 +10070,10 @@ class Field(Expression):
|
|||||||
d = dict()
|
d = dict()
|
||||||
if not (sanitize and not (self.readable or self.writable)):
|
if not (sanitize and not (self.readable or self.writable)):
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
if flat:
|
if flat:
|
||||||
d.update({attr: flatten(getattr(self, attr))})
|
d.update({attr: flatten(getattr(self, attr))})
|
||||||
else:
|
else:
|
||||||
d.update({attr: getattr(self, attr)})
|
d.update({attr: getattr(self, attr)})
|
||||||
d["fieldname"] = d.pop("name")
|
d["fieldname"] = d.pop("name")
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@@ -10120,7 +10141,7 @@ class Query(object):
|
|||||||
op,
|
op,
|
||||||
first=None,
|
first=None,
|
||||||
second=None,
|
second=None,
|
||||||
ignore_common_filters = False,
|
ignore_common_filters=False,
|
||||||
**optional_args
|
**optional_args
|
||||||
):
|
):
|
||||||
self.db = self._db = db
|
self.db = self._db = db
|
||||||
@@ -10177,6 +10198,7 @@ class Query(object):
|
|||||||
|
|
||||||
SERIALIZABLE_TYPES = (tuple, dict, set, list, int, long, float,
|
SERIALIZABLE_TYPES = (tuple, dict, set, list, int, long, float,
|
||||||
basestring, type(None), bool)
|
basestring, type(None), bool)
|
||||||
|
|
||||||
def loop(d):
|
def loop(d):
|
||||||
newd = dict()
|
newd = dict()
|
||||||
for k, v in d.items():
|
for k, v in d.items():
|
||||||
@@ -10199,7 +10221,7 @@ class Query(object):
|
|||||||
newd[k] = v.__name__
|
newd[k] = v.__name__
|
||||||
elif isinstance(v, basestring):
|
elif isinstance(v, basestring):
|
||||||
newd[k] = v
|
newd[k] = v
|
||||||
else: pass # not callable or string
|
else: pass # not callable or string
|
||||||
elif isinstance(v, SERIALIZABLE_TYPES):
|
elif isinstance(v, SERIALIZABLE_TYPES):
|
||||||
if isinstance(v, dict):
|
if isinstance(v, dict):
|
||||||
newd[k] = loop(v)
|
newd[k] = loop(v)
|
||||||
@@ -10210,7 +10232,6 @@ class Query(object):
|
|||||||
return loop(self.__dict__)
|
return loop(self.__dict__)
|
||||||
else: return self.__dict__
|
else: return self.__dict__
|
||||||
|
|
||||||
|
|
||||||
def as_xml(self, sanitize=True):
|
def as_xml(self, sanitize=True):
|
||||||
if have_serializers:
|
if have_serializers:
|
||||||
xml = serializers.xml
|
xml = serializers.xml
|
||||||
@@ -10227,6 +10248,7 @@ class Query(object):
|
|||||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||||
return json(d)
|
return json(d)
|
||||||
|
|
||||||
|
|
||||||
def xorify(orderby):
|
def xorify(orderby):
|
||||||
if not orderby:
|
if not orderby:
|
||||||
return None
|
return None
|
||||||
@@ -10235,10 +10257,12 @@ def xorify(orderby):
|
|||||||
orderby2 = orderby2 | item
|
orderby2 = orderby2 | item
|
||||||
return orderby2
|
return orderby2
|
||||||
|
|
||||||
|
|
||||||
def use_common_filters(query):
|
def use_common_filters(query):
|
||||||
return (query and hasattr(query,'ignore_common_filters') and \
|
return (query and hasattr(query,'ignore_common_filters') and \
|
||||||
not query.ignore_common_filters)
|
not query.ignore_common_filters)
|
||||||
|
|
||||||
|
|
||||||
class Set(object):
|
class Set(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -10258,7 +10282,7 @@ class Set(object):
|
|||||||
|
|
||||||
def __init__(self, db, query, ignore_common_filters = None):
|
def __init__(self, db, query, ignore_common_filters = None):
|
||||||
self.db = db
|
self.db = db
|
||||||
self._db = db # for backward compatibility
|
self._db = db # for backward compatibility
|
||||||
self.dquery = None
|
self.dquery = None
|
||||||
|
|
||||||
# if query is a dict, parse it
|
# if query is a dict, parse it
|
||||||
@@ -10850,7 +10874,6 @@ class Rows(object):
|
|||||||
"represent" attributes will be transformed).
|
"represent" attributes will be transformed).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
if i is None:
|
if i is None:
|
||||||
return (self.render(i, fields=fields) for i in range(len(self)))
|
return (self.render(i, fields=fields) for i in range(len(self)))
|
||||||
import sqlhtml
|
import sqlhtml
|
||||||
@@ -10889,7 +10912,6 @@ class Rows(object):
|
|||||||
self.compact = compact
|
self.compact = compact
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
def as_dict(self,
|
def as_dict(self,
|
||||||
key='id',
|
key='id',
|
||||||
compact=True,
|
compact=True,
|
||||||
@@ -10929,7 +10951,6 @@ class Rows(object):
|
|||||||
else:
|
else:
|
||||||
return dict([(key(r),r) for r in rows])
|
return dict([(key(r),r) for r in rows])
|
||||||
|
|
||||||
|
|
||||||
def as_trees(self, parent_name='parent_id', children_name='children'):
|
def as_trees(self, parent_name='parent_id', children_name='children'):
|
||||||
roots = []
|
roots = []
|
||||||
drows = {}
|
drows = {}
|
||||||
@@ -10964,6 +10985,7 @@ class Rows(object):
|
|||||||
represent = kwargs.get('represent', False)
|
represent = kwargs.get('represent', False)
|
||||||
writer = csv.writer(ofile, delimiter=delimiter,
|
writer = csv.writer(ofile, delimiter=delimiter,
|
||||||
quotechar=quotechar, quoting=quoting)
|
quotechar=quotechar, quoting=quoting)
|
||||||
|
|
||||||
def unquote_colnames(colnames):
|
def unquote_colnames(colnames):
|
||||||
unq_colnames = []
|
unq_colnames = []
|
||||||
for col in colnames:
|
for col in colnames:
|
||||||
|
|||||||
+37
-41
@@ -79,9 +79,6 @@ def tearDownModule():
|
|||||||
class TestFields(unittest.TestCase):
|
class TestFields(unittest.TestCase):
|
||||||
|
|
||||||
def testFieldName(self):
|
def testFieldName(self):
|
||||||
return
|
|
||||||
|
|
||||||
# Any table name is supported as long as underlying db does. The following code is ignored.
|
|
||||||
|
|
||||||
# Check that Fields cannot start with underscores
|
# Check that Fields cannot start with underscores
|
||||||
self.assertRaises(SyntaxError, Field, '_abc', 'string')
|
self.assertRaises(SyntaxError, Field, '_abc', 'string')
|
||||||
@@ -201,6 +198,25 @@ class TestFields(unittest.TestCase):
|
|||||||
db.tt.drop()
|
db.tt.drop()
|
||||||
|
|
||||||
|
|
||||||
|
class TestTables(unittest.TestCase):
|
||||||
|
|
||||||
|
def testTableNames(self):
|
||||||
|
|
||||||
|
# Check that Tables cannot start with underscores
|
||||||
|
self.assertRaises(SyntaxError, Table, None, '_abc')
|
||||||
|
|
||||||
|
# Check that Tables cannot contain punctuation other than underscores
|
||||||
|
self.assertRaises(SyntaxError, Table, None, 'a.bc')
|
||||||
|
|
||||||
|
# Check that Tables cannot be a name of a method or property of DAL
|
||||||
|
for x in ['define_table', 'tables', 'as_dict']:
|
||||||
|
self.assertRaises(SyntaxError, Table, None, x)
|
||||||
|
|
||||||
|
# Check that Table allows underscores in the body of a field name.
|
||||||
|
self.assert_(Table(None, 'a_bc'),
|
||||||
|
"Table isn't allowing underscores in tablename. It should.")
|
||||||
|
|
||||||
|
|
||||||
class TestAll(unittest.TestCase):
|
class TestAll(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -1396,45 +1412,7 @@ class TestRNameFields(unittest.TestCase):
|
|||||||
self.assertEqual(len(db.person._referenced_by),0)
|
self.assertEqual(len(db.person._referenced_by),0)
|
||||||
db.person.drop()
|
db.person.drop()
|
||||||
|
|
||||||
|
|
||||||
class TestQuoting(unittest.TestCase):
|
class TestQuoting(unittest.TestCase):
|
||||||
# tests for complex table names
|
|
||||||
def testRun(self):
|
|
||||||
return
|
|
||||||
db = DAL(DEFAULT_URI, check_reserved=['all'])
|
|
||||||
|
|
||||||
t0 = db.define_table('A.table.with.dots and spaces',
|
|
||||||
Field('f', 'string'))
|
|
||||||
t1 = db.define_table('A.table',
|
|
||||||
Field('f_other', t0),
|
|
||||||
Field('words', 'text'))
|
|
||||||
|
|
||||||
blather = 'blah blah and so'
|
|
||||||
t0[0] = {'f': 'content'}
|
|
||||||
t1[0] = {'f_other': int(t0[1]['id']),
|
|
||||||
'words': blather}
|
|
||||||
|
|
||||||
|
|
||||||
r = db(t1['f_other']==t0.id).select()
|
|
||||||
self.assertEqual(r[0][db['A.table']].words, blather)
|
|
||||||
|
|
||||||
db.define_table('t0', Field('f0'))
|
|
||||||
db.define_table('t1', Field('f1'), Field('t0', db['t0']))
|
|
||||||
db.t0[0]=dict(f0=3)
|
|
||||||
db.t1[0]=dict(f1=3, t0=1)
|
|
||||||
|
|
||||||
rows=db(db.t0.id==db.t1.t0).select()
|
|
||||||
self.assertEqual(rows[0].t1.t0, rows[0].t0.id)
|
|
||||||
if DEFAULT_URI.startswith('mssql'):
|
|
||||||
#there's no drop cascade in mssql
|
|
||||||
t1.drop()
|
|
||||||
t0.drop()
|
|
||||||
else:
|
|
||||||
t0.drop('cascade')
|
|
||||||
t1.drop()
|
|
||||||
|
|
||||||
db.t1.drop()
|
|
||||||
db.t0.drop()
|
|
||||||
|
|
||||||
# tests for case sensitivity
|
# tests for case sensitivity
|
||||||
def testCase(self):
|
def testCase(self):
|
||||||
@@ -1528,6 +1506,24 @@ class TestQuoting(unittest.TestCase):
|
|||||||
t3.drop()
|
t3.drop()
|
||||||
t4.drop()
|
t4.drop()
|
||||||
|
|
||||||
|
|
||||||
|
class TestTableAndFieldCase(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
at the Python level we should not allow db.C and db.c because of .table conflicts on windows
|
||||||
|
but it should be possible to map two different names into distinct tables "c" and "C" at the Python level
|
||||||
|
By default Python models names should be mapped into lower case table names and assume case insensitivity.
|
||||||
|
"""
|
||||||
|
def testme(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class TestQuotesByDefault(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
all default tables names should be quoted unless an explicit mapping has been given for a table.
|
||||||
|
"""
|
||||||
|
def testme(self):
|
||||||
|
return
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
tearDownModule()
|
tearDownModule()
|
||||||
|
|||||||
Reference in New Issue
Block a user