Skip to content

Commit 9389fd5

Browse files
committed
refs #90 Default dictionary for null values retrieved from database
ColumnInfo now has a mechanism to set your own default dictionary (or individual keys) for Null values retrieved from the database. These support both literal values and callables! One could cet really creative with functools.partial!
1 parent 1c847ec commit 9389fd5

File tree

1 file changed

+76
-20
lines changed

1 file changed

+76
-20
lines changed

pysimplesql/pysimplesql.py

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2682,6 +2682,30 @@ class ColumnInfo(List):
26822682
def __init__(self, driver:SQLDriver, table_name:str):
26832683
self.driver = driver
26842684
self.table_name = table_name
2685+
2686+
# List of required SQL types to check against when user sets custom values
2687+
self._sql_types = [
2688+
'TEXT','VARCHAR', 'CHAR', 'INTEGER', 'REAL', 'DOUBLE', 'FLOAT', 'DECIMAL', 'BOOLEAN', 'TIME', 'DATE',
2689+
'DATETIME', 'TIMESTAMP'
2690+
]
2691+
2692+
# Defaults to use for Null values returned from the database. These can be overwritten by the user and support
2693+
# function calls as well
2694+
self.null_defaults = {
2695+
'TEXT': 'New Record',
2696+
'VARCHAR': 'New Record',
2697+
'CHAR' : 'New Record',
2698+
'INTEGER' : 1,
2699+
'REAL': 0.0,
2700+
'DOUBLE': 0.0,
2701+
'FLOAT': 0.0,
2702+
'DECIMAL': 0.0,
2703+
'BOOLEAN': 0,
2704+
'TIME': self.default_time(),
2705+
'DATE': self.default_date(),
2706+
'TIMESTAMP': self.default_datetime(),
2707+
'DATETIME': self.default_datetime()
2708+
}
26852709
super().__init__()
26862710

26872711
def __contains__(self, item):
@@ -2702,7 +2726,7 @@ def col_name(self,idx):
27022726
"""Get the column name located at the specified index in this collection of columns"""
27032727
return self[idx].name
27042728

2705-
def looks_like_function(self, s:str):
2729+
def looks_like_function(self, s:str): # TODO: check if something looks like a statement for complex defaults? Regex?
27062730
# check if the string is empty
27072731
if not s:
27082732
return False
@@ -2750,25 +2774,20 @@ def default_dict(self, q_obj:Query):
27502774

27512775
# The stored default is a literal value, lets try to use it:
27522776
if default is None:
2753-
if sql_type == 'BOOLEAN':
2754-
default = 0
2755-
elif sql_type in ['TEXT','VARCHAR','CHAR']:
2756-
if c.name == q_obj.description_column:
2757-
default = 'New record' # If no default is specified, we have to do something
2758-
elif sql_type in ['REAL','DOUBLE','FLOAT','DECIMAL']:
2759-
default = 1.0
2760-
elif sql_type == 'DATE':
2761-
default = date.today().strftime("%Y-%m-%d")
2762-
elif sql_type in ['DATETIME','TIMESTAMP']:
2763-
default = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
2764-
elif sql_type == 'TIME':
2765-
default = datetime.now().strftime("%H:%M:%S")
2766-
elif sql_type == 'INTEGER':
2767-
if not c.pk: # we don't want to default our primary key!
2768-
default = 1
2777+
null_default = self.null_defaults[sql_type]
2778+
2779+
# If our default is callable, call it. Otherwise, assign it
2780+
# Make sure to skip primary keys, and onlu consider text that is in the description column
2781+
if (sql_type not in ['TEXT','VARCHAR','CHAR'] and c.name != q_obj.description_column) and c.pk==False:
2782+
print(f'Setting default for {c.name} to {null_default}')
2783+
default = null_default() if callable(null_default) else null_default
2784+
else:
2785+
print(f'Did not set default for {c.name}')
27692786
else:
2770-
# strip quotes from default strings
2771-
default = c.default.strip('"\'') # strip leading and trailing quotes
2787+
# Load the default from the database
2788+
if sql_type in ['TEXT', 'VARCHAR', 'CHAR']:
2789+
# strip quotes from default strings as they seem to get passed with some database-stored defaults
2790+
default = c.default.strip('"\'') # strip leading and trailing quotes
27722791

27732792
d[c.name]= default
27742793
if q_obj.transform is not None: q_obj.transform(d, TFORM_DECODE)
@@ -2781,6 +2800,43 @@ def _contains_key_value_pair(self, key, value): #used by __contains__
27812800
return True
27822801
return False
27832802

2803+
def default_time(self):
2804+
return datetime.now().strftime("%H:%M:%S")
2805+
2806+
def default_date(self):
2807+
return date.today().strftime("%Y-%m-%d")
2808+
2809+
def default_datetime(self):
2810+
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
2811+
2812+
def set_null_default(self, sql_type:str, value:object) -> None:
2813+
"""
2814+
Set a Null default for a single SQL type
2815+
2816+
:param sql_type: The SQL type to set the default for ('INTEGER', 'TEXT', 'BOOLEAN', etc.)
2817+
:param value: The new value to set the SQL type to. This can be a literal or even a callable
2818+
:return: None
2819+
"""
2820+
if sql_type not in self._sql_types:
2821+
RuntimeError(f'Unsupported SQL Type: {sql_type}. Supported types are: {self._sql_types}')
2822+
2823+
self.null_defaults[sql_type] = value
2824+
2825+
def set_null_defaults(self, null_defaults:dict) -> None:
2826+
"""
2827+
Set Null defaults for all SQL types
2828+
2829+
supported types: 'TEXT','VARCHAR', 'CHAR', 'INTEGER', 'REAL', 'DOUBLE', 'FLOAT', 'DECIMAL', 'BOOLEAN', 'TIME',
2830+
'DATE', 'DATETIME', 'TIMESTAMP'
2831+
:param null_defaults: A dict of SQL types and default values. This can be a literal or even a callable
2832+
:return: None
2833+
"""
2834+
# Check if the null_defaults dict has all of the required keys:
2835+
if not all(key in null_defaults for key in self._sql_types):
2836+
RuntimeError(f'The supplied null_defaults dictionary does not havle all required SQL types. Required: {self._sql_types}')
2837+
2838+
self.null_defaults = null_defaults
2839+
27842840
class ResultColumn:
27852841
"""
27862842
The ResultColumn class is a generic column class. It holds a dict containing the column name, type whether the
@@ -2840,7 +2896,7 @@ def pk(self, value):
28402896
self._column['pk'] = value
28412897

28422898
def get_names(self):
2843-
return [v for k,v in self.columns.items() if key == 'name']
2899+
return [v for k,v in self.columns.items() if k == 'name']
28442900

28452901
class ResultRow:
28462902
# The ResulRow class is a generic row class. It holds a dict containing the columns and values of the row, along

0 commit comments

Comments
 (0)