Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconstructing query from dict not working #531

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions pydal/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@
'id': '[1-9]\d*',
'decimal': '\d{1,10}\.\d{2}',
'integer': '[+-]?\d*',
'float': '[+-]?\d*(\.\d*)?',
'float': '[+-]?\d*(\.\d*)?',
'double': '[+-]?\d*(\.\d*)?',
'date': '\d{4}\-\d{2}\-\d{2}',
'time': '\d{2}\:\d{2}(\:\d{2}(\.\d*)?)?',
'datetime':'\d{4}\-\d{2}\-\d{2} \d{2}\:\d{2}(\:\d{2}(\.\d*)?)?',
}
}

class Row(BasicStorage):

Expand Down Expand Up @@ -380,9 +380,9 @@ def fields(self):

def _structure(self):
keys = ['name','type','writable','listable','searchable','regex','options',
'default','label','unique','notnull','required']
'default','label','unique','notnull','required']
def noncallable(obj): return obj if not callable(obj) else None
return [{key: noncallable(getattr(field, key)) for key in keys}
return [{key: noncallable(getattr(field, key)) for key in keys}
for field in self if field.readable and not field.type=='password']

@cachedprop
Expand Down Expand Up @@ -782,7 +782,7 @@ def validate_and_insert(self, **fields):
response.id = self.insert(**new_fields)
return response

def validate_and_update(self, _key=DEFAULT, **fields):
def validate_and_update(self, _key=DEFAULT, **fields):
response, new_fields = self._validate_fields(fields, 'update')
#: select record(s) for update
if _key is DEFAULT:
Expand Down Expand Up @@ -878,9 +878,9 @@ def import_from_csv_file(self,
null = '<NULL>',
unique = 'uuid',
id_offset = None, # id_offset used only when id_map is None
transform = None,
transform = None,
validate=False,
**kwargs
**kwargs
):
"""
Import records from csv file.
Expand Down Expand Up @@ -1274,10 +1274,10 @@ def abs(self):
return Expression(
self.db, self._dialect.aggregate, self, 'ABS', self.type)

def cast(self, cast_as, **kwargs):
def cast(self, cast_as, **kwargs):
return Expression(
self.db, self._dialect.cast, self, self._dialect.types[cast_as] % kwargs, cast_as)

def lower(self):
return Expression(
self.db, self._dialect.lower, self, None, self.type)
Expand Down Expand Up @@ -1601,7 +1601,7 @@ class Field(Expression, Serializable):
def __init__(self, fieldname, type='string', length=None, default=DEFAULT,
required=False, requires=DEFAULT, ondelete='CASCADE',
notnull=False, unique=False, uploadfield=True, widget=None,
label=None, comment=None,
label=None, comment=None,
writable=True, readable=True,
searchable=True, listable=True,
regex=None, options=None,
Expand Down Expand Up @@ -1796,7 +1796,7 @@ def retrieve(self, name, path=None, nameonly=False):
stream = BytesIO(to_bytes(data))
elif self.uploadfs:
# ## if file is on pyfilesystem
stream = self.uploadfs.open(name, 'rb')
stream = self.uploadfs.open(unicode(name), 'rb')
else:
# ## if file is on regular filesystem
# this is intentially a sting with filename and not a stream
Expand Down Expand Up @@ -2172,14 +2172,15 @@ def build(self, d):
d.get("second", None))
left = right = built = None

if op in ("AND", "OR"):
op = op.upper()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand this fix. But why change AND -> _AND?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the query is pickle to the session the operators and, or and not are prefixed with _, so: _AND, _OR and _NOT

In [1]: q = db(db.auth_user.id > 0) & (db.auth_user.id < 10)

In [2]: q
Out[2]: <Query (("auth_user"."id" < 10) AND <Set ("auth_user"."id" > 0)>)>

In [3]: q.as_dict()
Out[3]: 
{'_db': <DAL uri="sqlite://storage.sqlite">,
 'db': <DAL uri="sqlite://storage.sqlite">,
 'first': <Query ("auth_user"."id" < 10)>,
 'ignore_common_filters': False,
 'op': <bound method SQLiteDialect._and of <pydal.dialects.sqlite.SQLiteDialect object at 0x7f897554ff90>>,
 'optional_args': {},
 'second': <Set ("auth_user"."id" > 0)>}

In [4]: q.as_dict(flat=True)
Out[4]: 
{'first': {'first': {'fieldname': 'id', 'tablename': 'auth_user'},
  'ignore_common_filters': False,
  'op': 'lt',
  'optional_args': {},
  'second': 10},
 'ignore_common_filters': False,
 'op': '_and',
 'optional_args': {}}

Note to that the < is not prefixed with _. Actually all the operatos are lowercase, i just make then upper there for the sake of not making to many changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I am slow I still do not understand where do the '_' comes from?

if op in ("_AND", "_OR"):
if not (type(first), type(second)) == (dict, dict):
raise SyntaxError("Invalid AND/OR query")
if op == "AND":
if op == "_AND":
built = self.build(first) & self.build(second)
else:
built = self.build(first) | self.build(second)
elif op == "NOT":
elif op == "_NOT":
if first is None:
raise SyntaxError("Invalid NOT query")
built = ~self.build(first)
Expand All @@ -2195,8 +2196,8 @@ def build(self, d):
else:
right = v

if hasattr(self.db._adapter, op):
opm = getattr(self.db._adapter, op)
if hasattr(self.db._adapter.dialect, op.lower()):
opm = getattr(self.db._adapter.dialect, op.lower())

if op == "EQ":
built = left == right
Expand Down