Finally updating this...
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
# Aether API Python FastAPI
|
# Aether API Python FastAPI
|
||||||
The Aether API was created and is being developed by Scott Idem.
|
The Aether API was created and is being developed by Scott Idem using the Python FastAPI framework.
|
||||||
|
|||||||
@@ -5,3 +5,6 @@ SQLAlchemy
|
|||||||
mysqlclient
|
mysqlclient
|
||||||
redis
|
redis
|
||||||
aioredis
|
aioredis
|
||||||
|
html2text
|
||||||
|
pytz
|
||||||
|
#mypy
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ class Settings(BaseSettings):
|
|||||||
AETHER_DB_PASSWORD = 'xxx'
|
AETHER_DB_PASSWORD = 'xxx'
|
||||||
SQLALCHEMY_DATABASE_URI = 'mysql://'+AETHER_DB_USERNAME+':'+AETHER_DB_PASSWORD+'@'+AETHER_DB_SERVER+'/'+AETHER_DB_NAME
|
SQLALCHEMY_DATABASE_URI = 'mysql://'+AETHER_DB_USERNAME+':'+AETHER_DB_PASSWORD+'@'+AETHER_DB_SERVER+'/'+AETHER_DB_NAME
|
||||||
|
|
||||||
|
DB_CFG_FASTAPI_ID = 0
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|||||||
499
app/db_sql.py
Normal file
499
app/db_sql.py
Normal file
@@ -0,0 +1,499 @@
|
|||||||
|
from app.config import settings
|
||||||
|
from .log import *
|
||||||
|
|
||||||
|
from sqlalchemy import create_engine, text
|
||||||
|
from sqlalchemy.exc import IntegrityError, OperationalError
|
||||||
|
|
||||||
|
db_uri = settings.SQLALCHEMY_DATABASE_URI
|
||||||
|
|
||||||
|
connection_string = db_uri
|
||||||
|
engine = create_engine(name_or_url=connection_string, pool_size=25, pool_recycle=60, pool_pre_ping=True, echo=True, echo_pool=True, isolation_level='READ COMMITTED')
|
||||||
|
# NOTE: The default isolation_level is 'REPEATABLE READ'. This can sometimes not show updated data.
|
||||||
|
|
||||||
|
db = engine.connect()
|
||||||
|
|
||||||
|
|
||||||
|
# #### ### ## # BEGIN SQL # ## ### ####
|
||||||
|
# Create, Read/Get, Update, Delete
|
||||||
|
# CRUD or CGUD
|
||||||
|
|
||||||
|
|
||||||
|
# ### BEGIN ### Core Help CRUD ### sql_insert() ###
|
||||||
|
def sql_insert(sql=None, data=None, table_name=None, rm_id_random=None, id_random_length=None):
|
||||||
|
log.setLevel(logging.ERROR) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
return False
|
||||||
|
# ### END ### Core Help CRUD ### sql_insert() ###
|
||||||
|
|
||||||
|
|
||||||
|
# ### BEGIN ### Core Help CRUD ### sql_update() ###
|
||||||
|
def sql_update(sql=None, data=None, table_name=None, rm_id_random=None, id_random_length=None):
|
||||||
|
log.setLevel(logging.ERROR) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
return False
|
||||||
|
# ### END ### Core Help CRUD ### sql_update() ###
|
||||||
|
|
||||||
|
|
||||||
|
# ### BEGIN ### Core Help CRUD ### sql_insert_or_update() ###
|
||||||
|
# The catch all SQL INSERT or UPDATE function - STI 2021-02-17
|
||||||
|
# This one does it all for SQL INSERT and UPDATE queries
|
||||||
|
def sql_insert_or_update(sql:str=None, data:dict=None, table_name:str=None, rm_id_random:bool=None, id_random_length:int=None):
|
||||||
|
log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
#if sql: pass
|
||||||
|
#else:
|
||||||
|
#log.error('SQL text is missing')
|
||||||
|
#return False
|
||||||
|
|
||||||
|
if sql:
|
||||||
|
sql_insert_or_update = text(sql)
|
||||||
|
elif table_name and data:
|
||||||
|
if rm_id_random:
|
||||||
|
data = lookup_id_random_pop(obj_data=data)
|
||||||
|
if not data.get('id_random', None) and id_random_length:
|
||||||
|
data['id_random'] = secrets.token_urlsafe(id_random_length)
|
||||||
|
|
||||||
|
fields = []
|
||||||
|
values = []
|
||||||
|
for key, value in data.items():
|
||||||
|
if key != 'id': # A special exception for the id auto increment field.
|
||||||
|
fields.append('`'+str(key)+'`')
|
||||||
|
values.append(':'+str(key))
|
||||||
|
fields_string = ', '.join(fields)
|
||||||
|
values_string = ', '.join(values)
|
||||||
|
|
||||||
|
field_list = []
|
||||||
|
for key, value in data.items():
|
||||||
|
if key != 'id': # Creating a special exception for the id field.
|
||||||
|
field_list.append('`'+str(key) + '` = :' + str(key))
|
||||||
|
set_values_string = ', '.join(field_list)
|
||||||
|
|
||||||
|
sql_insert_or_update = text(
|
||||||
|
f"""
|
||||||
|
INSERT INTO `{table_name}` ({fields_string}) VALUES ({values_string})
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
{set_values_string}
|
||||||
|
;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
log.debug(f"""
|
||||||
|
INSERT INTO `{table_name}` ({fields_string}) VALUES ({values_string})
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
{set_values_string}
|
||||||
|
;
|
||||||
|
""")
|
||||||
|
|
||||||
|
trans = db.begin()
|
||||||
|
try:
|
||||||
|
result_insert = db.execute(sql_insert_or_update, data)
|
||||||
|
trans.commit()
|
||||||
|
except Exception as e:
|
||||||
|
trans.rollback()
|
||||||
|
log.exception('*** An exception happened. ***')
|
||||||
|
log.exception(repr(e))
|
||||||
|
log.exception('***')
|
||||||
|
log.exception(str(e))
|
||||||
|
log.exception('^^^ exception ^^^')
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if result_insert.rowcount == 1 and result_insert.lastrowid > 0: # insert
|
||||||
|
log.info('Insert record')
|
||||||
|
record_id = result_insert.lastrowid
|
||||||
|
return record_id
|
||||||
|
elif result_insert.rowcount == 1 and result_insert.lastrowid == 0: # update with no change
|
||||||
|
log.info('Update record with no change')
|
||||||
|
return True
|
||||||
|
elif result_insert.rowcount == 2 and result_insert.lastrowid > 0: # update with change
|
||||||
|
log.info('Update record with changes')
|
||||||
|
record_id = result_insert.lastrowid
|
||||||
|
return record_id
|
||||||
|
else:
|
||||||
|
log.debug(result_insert)
|
||||||
|
log.debug(vars(result_insert))
|
||||||
|
log.debug(dir(result_insert))
|
||||||
|
log.debug(result_insert.rowcount) # returns 1 on insert and 2 on update with change
|
||||||
|
log.debug(result_insert.lastrowid) # returns last row ID on insert and update with a change and returns 0 if nothing changed
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
# ### END ### Core Help CRUD ### sql_insert_or_update() ###
|
||||||
|
|
||||||
|
|
||||||
|
# ### BEGIN ### Core Help CRUD ### sql_select() ###
|
||||||
|
# The catch all SQL SELECT function - STI 2021-02-17
|
||||||
|
# This one does it all for SQL SELECT queries
|
||||||
|
def sql_select(table_name=None, record_id=None, record_id_random=None, field_name=None, field_value=None, sql=None, data=None, rm_id_random=None, as_dict=True, as_list=None):
|
||||||
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if table_name and not (record_id or record_id_random or field_name or field_value or sql or data):
|
||||||
|
# Select all records from a table
|
||||||
|
log.info('Select all records from a table')
|
||||||
|
sql = text(
|
||||||
|
f"""
|
||||||
|
SELECT *
|
||||||
|
FROM `{table_name}`
|
||||||
|
;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
elif table_name and (record_id or record_id_random) and not (field_name or field_value or sql or data):
|
||||||
|
# Select all records from a table with an ID (auto or random)
|
||||||
|
log.info('Select all records from a table with an ID (auto or random)')
|
||||||
|
data = {}
|
||||||
|
if record_id:
|
||||||
|
data['record_id'] = record_id
|
||||||
|
|
||||||
|
sql = text(
|
||||||
|
f"""
|
||||||
|
SELECT *
|
||||||
|
FROM `{table_name}`
|
||||||
|
WHERE `{table_name}`.id = :record_id
|
||||||
|
;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
elif record_id_random:
|
||||||
|
data['record_id_random'] = record_id_random
|
||||||
|
|
||||||
|
sql = text(
|
||||||
|
f"""
|
||||||
|
SELECT *
|
||||||
|
FROM `{table_name}`
|
||||||
|
WHERE `{table_name}`.id_random = :record_id_random
|
||||||
|
;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
elif table_name and field_name and field_value and not (record_id or record_id_random or sql or data):
|
||||||
|
# Select all records from a table with a specific field and field value
|
||||||
|
log.info('Select all records from a table with a specific field and field value')
|
||||||
|
data = {}
|
||||||
|
data[field_name] = field_value
|
||||||
|
|
||||||
|
sql = text(
|
||||||
|
f"""
|
||||||
|
SELECT *
|
||||||
|
FROM `{table_name}`
|
||||||
|
WHERE `{table_name}`.{field_name} = :{field_name}
|
||||||
|
;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
elif table_name and data and not (record_id or record_id_random or field_name or field_value or sql):
|
||||||
|
# Select all records from a table with a specific list of fields and field values (list of dicts)
|
||||||
|
log.info('Select all records from a table with a specific list of fields and field values (list of dicts)')
|
||||||
|
|
||||||
|
if rm_id_random:
|
||||||
|
data = lookup_id_random_pop(obj_data=data)
|
||||||
|
|
||||||
|
sql_where = []
|
||||||
|
for field_name in data:
|
||||||
|
sql_where_line = f"""`{table_name}`.{field_name} = :{field_name}"""
|
||||||
|
sql_where.append(sql_where_line)
|
||||||
|
|
||||||
|
sql_where_string = ' AND '.join(sql_where)
|
||||||
|
log.debug(sql_where_string)
|
||||||
|
|
||||||
|
sql = text(
|
||||||
|
f"""
|
||||||
|
SELECT *
|
||||||
|
FROM `{table_name}`
|
||||||
|
WHERE {sql_where_string}
|
||||||
|
;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
elif sql and not (table_name or record_id or record_id_random or field_name or field_value or data):
|
||||||
|
# Select records based on the SQL statement given
|
||||||
|
log.info('Select records based on the SQL statement given')
|
||||||
|
sql = text(sql)
|
||||||
|
elif sql and data and not (table_name or record_id or record_id_random or field_name or field_value):
|
||||||
|
# Select records based on the SQL statement given and with the matching data dict fields and values
|
||||||
|
|
||||||
|
if rm_id_random:
|
||||||
|
data = lookup_id_random_pop(obj_data=data)
|
||||||
|
|
||||||
|
log.info('Select records based on the SQL statement given and with the matching data dict fields and values')
|
||||||
|
sql = text(sql)
|
||||||
|
else:
|
||||||
|
# Nothing matched the expected combination of parameters passed to this function
|
||||||
|
log.warning('Nothing matched the expected combination of parameters passed to this function')
|
||||||
|
return False # Not successful
|
||||||
|
|
||||||
|
#log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(sql)
|
||||||
|
log.debug(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if data:
|
||||||
|
log.info('Executing with SQL statement and data...')
|
||||||
|
result = db.execute(sql, data)
|
||||||
|
else:
|
||||||
|
log.info('Executing with SQL statement only...')
|
||||||
|
result = db.execute(sql)
|
||||||
|
except OperationalError as e:
|
||||||
|
log.warning('*** An exception happened: OperationalError ***')
|
||||||
|
log.warning('* This is likely a "MySQL server has gone away" error. Going to try again... *')
|
||||||
|
log.warning(repr(e))
|
||||||
|
log.warning('***')
|
||||||
|
log.warning(str(e))
|
||||||
|
log.warning('^^^ exception ^^^')
|
||||||
|
|
||||||
|
log.warning('Trying to recreate the pool...')
|
||||||
|
log.debug('############## ############')
|
||||||
|
log.debug(dir(db))
|
||||||
|
log.debug(vars(db))
|
||||||
|
log.debug('############## ############')
|
||||||
|
log.debug(dir(db.engine))
|
||||||
|
log.debug(vars(db.engine))
|
||||||
|
log.debug('############## ############')
|
||||||
|
log.debug(dir(db.engine.pool))
|
||||||
|
log.debug(vars(db.engine.pool))
|
||||||
|
log.debug('############## ############')
|
||||||
|
db.engine.dispose()
|
||||||
|
log.warning('Now trying the query again...')
|
||||||
|
try:
|
||||||
|
if data:
|
||||||
|
log.warning('2x Executing with SQL statement and data...')
|
||||||
|
result = db.execute(sql, data)
|
||||||
|
else:
|
||||||
|
log.warning('2x Executing with SQL statement only...')
|
||||||
|
result = db.execute(sql)
|
||||||
|
except Exception as e:
|
||||||
|
log.warning('2x A *second* exception happened. Returning False.')
|
||||||
|
log.exception(repr(e))
|
||||||
|
log.exception('***')
|
||||||
|
log.exception(str(e))
|
||||||
|
log.exception('^^^ exception ^^^')
|
||||||
|
return False # Not successful
|
||||||
|
else:
|
||||||
|
log.info('Successfully executed the SQL on the second try.')
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
log.info('An exception happened. Returning False.')
|
||||||
|
log.exception(repr(e))
|
||||||
|
log.exception('***')
|
||||||
|
log.exception(str(e))
|
||||||
|
log.exception('^^^ exception ^^^')
|
||||||
|
return False # Not successful
|
||||||
|
else:
|
||||||
|
log.info('Successfully executed the SQL on the first try.')
|
||||||
|
pass
|
||||||
|
|
||||||
|
#log.debug(result.fetchall()) # Uncommenting this breaks things?
|
||||||
|
# BEGIN NOTE: Check this out later! ###
|
||||||
|
#header = result.keys()
|
||||||
|
#for row in result:
|
||||||
|
# yield dict(zip(header, row))
|
||||||
|
# END NOTE: Check this out later! ###
|
||||||
|
|
||||||
|
#log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(result.rowcount)
|
||||||
|
log.debug(vars(result))
|
||||||
|
log.debug(dir(result))
|
||||||
|
if result.rowcount == 1:
|
||||||
|
log.info(f'Found one record. as_dict={as_dict}, as_list={as_list}')
|
||||||
|
if as_dict:
|
||||||
|
record = sql_result_proxy_to_dict_simple(result_proxy=result.first())
|
||||||
|
else:
|
||||||
|
record = result.first()
|
||||||
|
if as_list:
|
||||||
|
record_li = []
|
||||||
|
record_li.append(record)
|
||||||
|
#log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(record_li)
|
||||||
|
return record_li # Successful
|
||||||
|
else:
|
||||||
|
#log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(record)
|
||||||
|
return record # Successful
|
||||||
|
elif result.rowcount > 1:
|
||||||
|
log.info(f'Found {result.rowcount} records. as_dict={as_dict}, as_list={as_list}')
|
||||||
|
#log.info('Found more than one record. Returning as a list of dicts.')
|
||||||
|
if as_dict:
|
||||||
|
record_li = sql_result_proxy_to_dict_simple(result_proxy=result.fetchall())
|
||||||
|
else:
|
||||||
|
record_li = result.fetchall()
|
||||||
|
log.debug(record_li)
|
||||||
|
return record_li # Successful
|
||||||
|
else:
|
||||||
|
log.info('No records found. Returning None.')
|
||||||
|
log.debug(result)
|
||||||
|
return None # Successful
|
||||||
|
# ### END ### Core Help CRUD ### sql_select() ###
|
||||||
|
|
||||||
|
|
||||||
|
# ### BEGIN ### Core Help CRUD ### sql_delete() ###
|
||||||
|
# The catch all SQL DELETE function - STI 2021-02-17
|
||||||
|
# This one does it all for SQL DELETE queries
|
||||||
|
def sql_delete(table_name:str=None, record_id:int=None, record_id_random:str=None, field_name:str=None, field_value=None, sql:str=None, data:dict=None):
|
||||||
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if table_name and (record_id or record_id_random) and not (field_name or field_value or sql or data):
|
||||||
|
# Delete all records from a table with an ID (auto or random)
|
||||||
|
log.info('Delete all records from a table with an ID (auto or random)')
|
||||||
|
data = {}
|
||||||
|
if record_id:
|
||||||
|
data['record_id'] = record_id
|
||||||
|
|
||||||
|
sql = text(
|
||||||
|
f"""
|
||||||
|
DELETE FROM `{table_name}`
|
||||||
|
WHERE `{table_name}`.id = :record_id
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
elif record_id_random:
|
||||||
|
data['record_id_random'] = record_id_random
|
||||||
|
|
||||||
|
sql = text(
|
||||||
|
f"""
|
||||||
|
DELETE FROM `{table_name}`
|
||||||
|
WHERE `{table_name}`.id_random = :record_id_random
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
elif table_name and field_name and field_value and not (record_id or record_id_random or sql or data):
|
||||||
|
# Delete all records from a table with a specific field and field value
|
||||||
|
log.info('Delete all records from a table with a specific field and field value')
|
||||||
|
data = {}
|
||||||
|
data[field_name] = field_value
|
||||||
|
|
||||||
|
sql = text(
|
||||||
|
f"""
|
||||||
|
DELETE FROM `{table_name}`
|
||||||
|
WHERE `{table_name}`.{field_name} = :{field_name}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
elif table_name and data and not (record_id or record_id_random or field_name or field_value or sql):
|
||||||
|
# Delete all records from a table with a specific list of fields and field values (list of dicts)
|
||||||
|
log.info('Delete all records from a table with a specific list of fields and field values (list of dicts)')
|
||||||
|
sql_where = []
|
||||||
|
for field_name in data:
|
||||||
|
sql_where_line = f"""`{table_name}`.{field_name} = :{field_name}"""
|
||||||
|
sql_where.append(sql_where_line)
|
||||||
|
|
||||||
|
sql_where_string = ' AND '.join(sql_where)
|
||||||
|
log.debug(sql_where_string)
|
||||||
|
|
||||||
|
sql = text(
|
||||||
|
f"""
|
||||||
|
DELETE FROM `{table_name}`
|
||||||
|
WHERE {sql_where_string}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
log.debug(sql)
|
||||||
|
elif sql and not (table_name or record_id or record_id_random or field_name or field_value or data):
|
||||||
|
# Delete records based on the SQL statement given
|
||||||
|
log.info('Delete records based on the SQL statement given')
|
||||||
|
sql = text(sql)
|
||||||
|
elif sql and data and not (table_name or record_id or record_id_random or field_name or field_value):
|
||||||
|
# Delete records based on the SQL statement given and with the matching data dict fields and values
|
||||||
|
log.info('Delete records based on the SQL statement given and with the matching data dict fields and values')
|
||||||
|
sql = text(sql)
|
||||||
|
else:
|
||||||
|
# Nothing matched the expected combination of parameters passed to this function
|
||||||
|
log.warning('Nothing matched the expected combination of parameters passed to this function')
|
||||||
|
return False # Not successful
|
||||||
|
|
||||||
|
log.debug(sql)
|
||||||
|
|
||||||
|
try:
|
||||||
|
log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
if data:
|
||||||
|
log.info('Executing with SQL (DELETE) statement and data...')
|
||||||
|
result = db.execute(sql, data)
|
||||||
|
else:
|
||||||
|
log.info('Executing with SQL (DELETE?) statement only...')
|
||||||
|
result = db.execute(sql)
|
||||||
|
log.debug(result)
|
||||||
|
log.debug(dir(result))
|
||||||
|
log.debug(vars(result))
|
||||||
|
except OperationalError as e:
|
||||||
|
log.warning('*** An exception happened: OperationalError ***')
|
||||||
|
log.warning('* This is likely a "MySQL server has gone away" error. Going to try again... *')
|
||||||
|
log.warning(repr(e))
|
||||||
|
log.warning('***')
|
||||||
|
log.warning(str(e))
|
||||||
|
log.warning('^^^ exception ^^^')
|
||||||
|
|
||||||
|
log.warning('Trying to recreate the pool...')
|
||||||
|
log.debug('############## ############')
|
||||||
|
log.debug(dir(db))
|
||||||
|
log.debug(vars(db))
|
||||||
|
log.debug('############## ############')
|
||||||
|
log.debug(dir(db.engine))
|
||||||
|
log.debug(vars(db.engine))
|
||||||
|
log.debug('############## ############')
|
||||||
|
log.debug(dir(db.engine.pool))
|
||||||
|
log.debug(vars(db.engine.pool))
|
||||||
|
log.debug('############## ############')
|
||||||
|
db.engine.dispose()
|
||||||
|
log.warning('Now trying the query again...')
|
||||||
|
try:
|
||||||
|
log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
if data:
|
||||||
|
log.warning('2x Executing with SQL statement and data...')
|
||||||
|
result = db.execute(sql, data)
|
||||||
|
else:
|
||||||
|
log.warning('2x Executing with SQL statement only...')
|
||||||
|
result = db.execute(sql)
|
||||||
|
log.debug(result)
|
||||||
|
except Exception as e:
|
||||||
|
log.warning('2x A *second* exception happened. Returning False.')
|
||||||
|
log.exception(repr(e))
|
||||||
|
log.exception('***')
|
||||||
|
log.exception(str(e))
|
||||||
|
log.exception('^^^ exception ^^^')
|
||||||
|
return False # Not successful
|
||||||
|
else:
|
||||||
|
log.info('Successfully executed the SQL on the second try.')
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
log.info('An exception happened. Returning False.')
|
||||||
|
log.exception(repr(e))
|
||||||
|
log.exception('***')
|
||||||
|
log.exception(str(e))
|
||||||
|
log.exception('^^^ exception ^^^')
|
||||||
|
return False # Not successful
|
||||||
|
else:
|
||||||
|
log.info('Successfully executed the SQL on the first try.')
|
||||||
|
pass
|
||||||
|
|
||||||
|
# NOTE: Need to deal with 0 rows affected when the WHERE clause was not satisfied and there was no error.
|
||||||
|
return True # Successful
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE WARNING: This is a near duplicate of what is under lib_rest (was lib_general). WARNING
|
||||||
|
# Change SQL SELECT result RowProxy record to a dict (named key/value)
|
||||||
|
# Change SQL SELECT list result RowProxy records to a list of dicts (named key/value)
|
||||||
|
def sql_result_proxy_to_dict_simple(result_proxy=None):
|
||||||
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
log.debug(type(result_proxy))
|
||||||
|
|
||||||
|
if isinstance(result_proxy, list):
|
||||||
|
log.info('Processing a SQL list...')
|
||||||
|
|
||||||
|
record_li = []
|
||||||
|
for row_proxy in result_proxy:
|
||||||
|
log.debug(row_proxy)
|
||||||
|
|
||||||
|
record = {}
|
||||||
|
for key, value in row_proxy.items():
|
||||||
|
record[key] = value
|
||||||
|
record_li.append(record)
|
||||||
|
return record_li
|
||||||
|
|
||||||
|
# Must import sqlalchemy to check the type correctly.
|
||||||
|
# Or convert it to a string and compare.
|
||||||
|
if str(type(result_proxy)) == '<class \'sqlalchemy.engine.result.RowProxy\'>':
|
||||||
|
#if isinstance(result_proxy, sqlalchemy.engine.result.RowProxy):
|
||||||
|
log.info('Processing a SQL record (sqlalchemy.engine.result.RowProxy)')
|
||||||
|
|
||||||
|
row_proxy = result_proxy
|
||||||
|
|
||||||
|
record = {}
|
||||||
|
for key, value in row_proxy.items():
|
||||||
|
record[key] = value
|
||||||
|
return record
|
||||||
|
return False
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import redis
|
import datetime, redis
|
||||||
|
|
||||||
from datetime import datetime, time, timedelta
|
#from datetime import datetime, time, timedelta
|
||||||
from fastapi import APIRouter, Depends, Header, HTTPException, status
|
from fastapi import APIRouter, Depends, Header, HTTPException, status
|
||||||
from pydantic import BaseModel, EmailStr, Field
|
from pydantic import BaseModel, EmailStr, Field
|
||||||
from typing import Dict, List, Optional, Set, Union
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
|
||||||
from .log import *
|
from .log import *
|
||||||
from .db import *
|
from .db_sql import *
|
||||||
|
|
||||||
|
|
||||||
async def get_token_header(x_token: str = Header(...)):
|
async def get_token_header(x_token: str = Header(...)):
|
||||||
@@ -38,78 +38,82 @@ async def get_account_header(x_account_id: str = Header(...)):
|
|||||||
return account
|
return account
|
||||||
|
|
||||||
|
|
||||||
#Add the processing time to the response header.
|
# Just return the value if it is an integer
|
||||||
#@app.middleware('http')
|
# Check if the id_random value is a string and the correct length
|
||||||
#async def add_process_time_header(request: Request, call_next):
|
# Attempt to look up id_random key in Redis
|
||||||
#import time
|
# If success then return the ID number
|
||||||
#start_time = time.time()
|
|
||||||
#response = await call_next(request)
|
|
||||||
#process_time = time.time() - start_time
|
|
||||||
#response.headers['X-Process-Time'] = str(process_time)
|
|
||||||
#return response
|
|
||||||
|
|
||||||
|
|
||||||
#async def get_token_header(x_token: str = Header(...)):
|
|
||||||
#if x_token != 'fake-super-secret-token':
|
|
||||||
#raise HTTPException(status_code=400, detail='X-Token header invalid')
|
|
||||||
|
|
||||||
|
|
||||||
#async def get_account_header(x_account_id: str = Header(...)):
|
|
||||||
#@app.middleware("http")
|
|
||||||
#async def get_account_header(x_account_id: str = Header(...)):
|
|
||||||
#return x_account_id
|
|
||||||
#x_account_id: str = Header(...)
|
|
||||||
#x_account_id = 'static random ID...'
|
|
||||||
#response = await call_next(request)
|
|
||||||
|
|
||||||
#print(x_account_id)
|
|
||||||
|
|
||||||
#return x_account_id
|
|
||||||
|
|
||||||
|
|
||||||
#async def get_account_header(x_account_id: str = Header(...)):
|
|
||||||
#print('get_account_header(): '+x_account_id+'z9999z')
|
|
||||||
#return x_account_id+'z9999z'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Attempt to look up id_random key
|
|
||||||
# If success then return the id number
|
|
||||||
# If not success and there is a table_name then check the database table passed
|
# If not success and there is a table_name then check the database table passed
|
||||||
# If found in database table then store in Redis
|
# If found in database table then store in Redis and return the ID number
|
||||||
def redis_lookup_id_random(record_id_random=None, table_name=None):
|
def redis_lookup_id_random(record_id_random=None, table_name=None):
|
||||||
log.setLevel(logging.DEBUG) # DEBUG, INFO, WARN, WARNING, ERROR, EXCEPTION, CRITICAL
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
log.debug(locals())
|
log.debug(locals())
|
||||||
|
|
||||||
|
if record_id_random is None: return False
|
||||||
|
if isinstance(record_id_random, bool): return False
|
||||||
|
if isinstance(record_id_random, int):
|
||||||
|
return record_id_random
|
||||||
|
elif isinstance(record_id_random, str):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
log.warning(f'Unexpected data type: {str(type(record_id_random))} Expected type is a string 11 or 22 characters long.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if record_id_random and table_name:
|
||||||
|
# WARNING: The record_id_random string length should be checked just in case?
|
||||||
|
if len(record_id_random) < 11:
|
||||||
|
log.warning(f'The length of id_random is too short: {str(record_id_random)} ({len(record_id_random)} chars)')
|
||||||
|
return False
|
||||||
|
elif len(record_id_random) > 22:
|
||||||
|
log.warning(f'The length of id_random is too long {str(record_id_random)} ({len(record_id_random)} chars)')
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
log.warning('Missing table_name to select from for id_random')
|
||||||
|
return False
|
||||||
|
|
||||||
r = redis.Redis(host='localhost', port=6379, db=7, password=None, decode_responses=True)
|
r = redis.Redis(host='localhost', port=6379, db=7, password=None, decode_responses=True)
|
||||||
|
|
||||||
key_name = 'record_id:'+record_id_random
|
key_name = 'record_id:'+record_id_random
|
||||||
|
|
||||||
record_id = r.get(key_name)
|
record_id = r.get(key_name)
|
||||||
#print('Record ID? '+str(record_id))
|
log.debug(f'Record ID? {str(record_id)}')
|
||||||
|
|
||||||
if record_id:
|
if record_id:
|
||||||
print('TTL for: '+key_name+' : '+str(record_id)+' is '+str(r.ttl(key_name))+' seconds')
|
log.info('The record ID was found using the record_id_random value.')
|
||||||
return record_id
|
log.info(f'TTL for: {key_name} : {str(record_id)} is {str(r.ttl(key_name))} seconds')
|
||||||
|
return int(record_id)
|
||||||
elif table_name:
|
elif table_name:
|
||||||
data = { 'id_random': record_id_random }
|
data = { 'id_random': record_id_random }
|
||||||
sql = """
|
sql = f"""
|
||||||
SELECT id
|
SELECT id
|
||||||
FROM `"""+table_name+"""` AS `table`
|
FROM `{table_name}` AS `table`
|
||||||
WHERE table.id_random = :id_random
|
WHERE `table`.id_random = :id_random;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if select_results := sql_select(table_name=table_name, record_id_random=record_id_random): # sql_select(sql=sql, data=data)
|
if select_results := sql_select(sql=sql, data=data):
|
||||||
#print('Record ID random found: '+str(select_results['id']))
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
record_id = select_results['id']
|
log.debug(select_results)
|
||||||
r.setex(key_name, timedelta(minutes=2), value=record_id)
|
log.debug(type(select_results))
|
||||||
return record_id
|
if isinstance(select_results, dict):
|
||||||
|
log.info(f"""Record ID random found: {str(select_results['id'])}""")
|
||||||
|
if record_id := select_results.get('id'):
|
||||||
|
r.setex(key_name, datetime.timedelta(minutes=90), value=record_id)
|
||||||
|
return int(record_id)
|
||||||
|
else:
|
||||||
|
log.setLevel(logging.ERROR) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.error('The SQL result was not what was expected.')
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
log.setLevel(logging.ERROR) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.error('More than one record may have been found. There may be a duplicate id_random.')
|
||||||
|
log.error(select_results)
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
#print('Record ID random was not found')
|
#log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.info('Record ID random was not found')
|
||||||
return None
|
return None
|
||||||
else:
|
|
||||||
print('Missing table_name to select from for id_random')
|
log.setLevel(logging.ERROR) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
return False
|
log.error('We should not be here. Something unexpected happened.')
|
||||||
#return False
|
return False # Just in case
|
||||||
|
|||||||
19
app/main.py
19
app/main.py
@@ -1,7 +1,7 @@
|
|||||||
import logging, random # , uvicorn
|
import logging, random # , uvicorn
|
||||||
|
|
||||||
from datetime import datetime, time, timedelta
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
#from datetime import datetime, time, timedelta
|
||||||
from fastapi import Body, Cookie, Depends, FastAPI, File, Form, Header, HTTPException, Path, Query, Request, status, UploadFile
|
from fastapi import Body, Cookie, Depends, FastAPI, File, Form, Header, HTTPException, Path, Query, Request, status, UploadFile
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, PlainTextResponse
|
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, PlainTextResponse
|
||||||
@@ -18,14 +18,14 @@ from .lib_general import *
|
|||||||
from .log import *
|
from .log import *
|
||||||
|
|
||||||
# Import the routers here first:
|
# Import the routers here first:
|
||||||
from .routers import items, journals, users, websockets
|
from .routers import api_crud, items, journals, users, websockets
|
||||||
|
|
||||||
|
|
||||||
# TEST TEST TEST
|
# TEST TEST TEST
|
||||||
print('**** Calling db.py ... ****')
|
print('**** Calling db_sql.py ... ****')
|
||||||
#from .db import engine, SessionLocal, Base
|
#from .db_sql import engine, SessionLocal, Base
|
||||||
from .db import db
|
from .db_sql import db
|
||||||
print('**** Called db.py ****')
|
print('**** Called db_sql.py ****')
|
||||||
# TEST TEST TEST
|
# TEST TEST TEST
|
||||||
|
|
||||||
|
|
||||||
@@ -48,6 +48,13 @@ app.mount('/static', StaticFiles(directory='static'), name='static')
|
|||||||
|
|
||||||
|
|
||||||
# Set up each route once the router has been imported
|
# Set up each route once the router has been imported
|
||||||
|
app.include_router(
|
||||||
|
api_crud.router,
|
||||||
|
prefix='/crud',
|
||||||
|
tags=['CRUD'],
|
||||||
|
#dependencies=[Depends(get_token_header)],
|
||||||
|
#responses={404: {'description': 'Not found'}},
|
||||||
|
)
|
||||||
app.include_router(
|
app.include_router(
|
||||||
items.router,
|
items.router,
|
||||||
prefix='/item',
|
prefix='/item',
|
||||||
|
|||||||
41
app/redis.py
41
app/redis.py
@@ -1,41 +0,0 @@
|
|||||||
from app.db import *
|
|
||||||
import redis
|
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
# Attempt to look up id_random key
|
|
||||||
# If success then return the id number
|
|
||||||
# If not success and there is a table_name then check the database table passed
|
|
||||||
# If found in database table then store in Redis
|
|
||||||
def redis_lookup_id_random(record_id_random=None, table_name=None):
|
|
||||||
print('*** redis_lookup_id_random() ***')
|
|
||||||
|
|
||||||
r = redis.Redis(host='localhost', port=6379, db=7, password=None, decode_responses=True)
|
|
||||||
|
|
||||||
key_name = 'record_id:'+record_id_random
|
|
||||||
|
|
||||||
record_id = r.get(key_name)
|
|
||||||
#print('Record ID? '+str(record_id))
|
|
||||||
|
|
||||||
if record_id:
|
|
||||||
print('TTL for: '+key_name+' : '+str(record_id)+' is '+str(r.ttl(key_name))+' seconds')
|
|
||||||
return record_id
|
|
||||||
elif table_name:
|
|
||||||
data = { 'id_random': record_id_random }
|
|
||||||
sql = """
|
|
||||||
SELECT id
|
|
||||||
FROM `"""+table_name+"""` AS `table`
|
|
||||||
WHERE table.id_random = :id_random
|
|
||||||
"""
|
|
||||||
|
|
||||||
if select_results := sql_select(table_name=table_name, record_id_random=record_id_random): # sql_select(sql=sql, data=data)
|
|
||||||
#print('Record ID random found: '+str(select_results['id']))
|
|
||||||
record_id = select_results['id']
|
|
||||||
r.setex(key_name, timedelta(minutes=2), value=record_id)
|
|
||||||
return record_id
|
|
||||||
else:
|
|
||||||
#print('Record ID random was not found')
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
print('Missing table_name to select from for id_random')
|
|
||||||
return False
|
|
||||||
#return False
|
|
||||||
119
app/routers/address_model.py
Normal file
119
app/routers/address_model.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
import datetime, hashlib, logging, os, pytz, redis, secrets
|
||||||
|
|
||||||
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, validator
|
||||||
|
|
||||||
|
from ..lib_general import *
|
||||||
|
from ..log import *
|
||||||
|
from .common_field_schema import base_fields, default_num_bytes
|
||||||
|
#from .account_model import Account_Base
|
||||||
|
|
||||||
|
|
||||||
|
class Address_Base(BaseModel):
|
||||||
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
#from .account_model import Account_Base
|
||||||
|
|
||||||
|
id_random: Optional[str] = Field(
|
||||||
|
**base_fields['address_id_random'],
|
||||||
|
alias='address_id_random',
|
||||||
|
default_factory=lambda:secrets.token_urlsafe(default_num_bytes),
|
||||||
|
)
|
||||||
|
id: Optional[int] = Field(
|
||||||
|
#alias='address_id'
|
||||||
|
)
|
||||||
|
account_id_random: Optional[str]
|
||||||
|
account_id: Optional[int]
|
||||||
|
|
||||||
|
for_type: Optional[str]
|
||||||
|
for_id_random: Optional[str]
|
||||||
|
for_id: Optional[int] #organization: Optional[Organization_Base] = Organization_Base()
|
||||||
|
|
||||||
|
name: Optional[str]
|
||||||
|
attention_to: Optional[str]
|
||||||
|
organization_name: Optional[str]
|
||||||
|
|
||||||
|
line_1: Optional[str]
|
||||||
|
line_2: Optional[str]
|
||||||
|
line_3: Optional[str]
|
||||||
|
city: Optional[str]
|
||||||
|
country_subdivision_code: Optional[str]
|
||||||
|
state_province: Optional[str]
|
||||||
|
postal_code: Optional[str]
|
||||||
|
country_alpha_2_code: Optional[str]
|
||||||
|
country: Optional[str]
|
||||||
|
|
||||||
|
lu_time_zone_id: Optional[str]
|
||||||
|
timezone: Optional[str]
|
||||||
|
|
||||||
|
latitude: Optional[str]
|
||||||
|
longitude: Optional[str]
|
||||||
|
|
||||||
|
map_url: Optional[str]
|
||||||
|
|
||||||
|
congressional_district: Optional[str]
|
||||||
|
|
||||||
|
#priority: Optional[int]
|
||||||
|
#sort: Optional[int]
|
||||||
|
#group: Optional[str]
|
||||||
|
|
||||||
|
created_on: Optional[datetime.datetime] = None
|
||||||
|
updated_on: Optional[datetime.datetime] = None
|
||||||
|
|
||||||
|
#account: Optional[Account_Base] = Account_Base()
|
||||||
|
|
||||||
|
_processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now)
|
||||||
|
|
||||||
|
#@validator('address_id_random', always=True)
|
||||||
|
def address_id_random_copy(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['id_random']:
|
||||||
|
return values['id_random']
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('id', always=True)
|
||||||
|
def address_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['id_random']:
|
||||||
|
log.debug(values['id_random'])
|
||||||
|
return redis_lookup_id_random(record_id_random=values['id_random'], table_name='address')
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('account_id', always=True)
|
||||||
|
def account_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['account_id_random']:
|
||||||
|
return redis_lookup_id_random(record_id_random=values['account_id_random'], table_name='account')
|
||||||
|
return None
|
||||||
|
|
||||||
|
#@validator('organization_id', always=True)
|
||||||
|
#def organization_id_lookup(cls, v, values, **kwargs):
|
||||||
|
#log.setLevel(logging.WARNING)
|
||||||
|
#log.debug(locals())
|
||||||
|
|
||||||
|
#if values['organization_id']:
|
||||||
|
#return redis_lookup_id_random(record_id_random=values['organization_id'], table_name='organization')
|
||||||
|
#return None
|
||||||
|
|
||||||
|
@validator('for_id', always=True)
|
||||||
|
def for_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['for_id_random'] and values['for_type']:
|
||||||
|
return redis_lookup_id_random(record_id_random=values['for_id_random'], table_name=values['for_type'])
|
||||||
|
return None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
underscore_attrs_are_private = True
|
||||||
|
fields = base_fields
|
||||||
|
|
||||||
|
Address_Base.update_forward_refs()
|
||||||
80
app/routers/api_crud.py
Normal file
80
app/routers/api_crud.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import datetime
|
||||||
|
#from datetime import datetime, time, timedelta
|
||||||
|
from fastapi import APIRouter, Depends, Header, HTTPException, Query, status
|
||||||
|
from pydantic import BaseModel, EmailStr, Field
|
||||||
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
|
||||||
|
from ..lib_general import *
|
||||||
|
from ..log import *
|
||||||
|
from app.config import settings
|
||||||
|
from app.db import *
|
||||||
|
#from .journal_models import *
|
||||||
|
#from .user_models import *
|
||||||
|
from .user_model import *
|
||||||
|
from .response_model import *
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
# Working on the basic API CRUD - STI 2021-03-05
|
||||||
|
|
||||||
|
#@router.get('/{object_l1}/list')
|
||||||
|
@router.get('/{object_l1}/{object_id}/list')
|
||||||
|
@router.get('/{object_l1}/{object_l2}/{object_id}/list')
|
||||||
|
@router.get('/{object_l1}/{object_l2}/{object_id}/{object_l3}/list')
|
||||||
|
async def get_obj_li(object_l1: str=None, object_l2: str=None, object_l3: str=None, object_id: str=None, x_account_id: str = Header(...)):
|
||||||
|
response_data = {}
|
||||||
|
response_data['object_l1'] = object_l1
|
||||||
|
response_data['object_l2'] = object_l2
|
||||||
|
response_data['object_l3'] = object_l3
|
||||||
|
response_data['object_id'] = object_id
|
||||||
|
response_data['list'] = 'li'
|
||||||
|
|
||||||
|
sql_result = sql_select(table_name='user', record_id=1)
|
||||||
|
|
||||||
|
response_data['sql_result'] = sql_result
|
||||||
|
|
||||||
|
return response_data
|
||||||
|
|
||||||
|
|
||||||
|
@router.get('/{object_l1}/{object_id}')
|
||||||
|
@router.get('/{object_l1}/{object_l2}/{object_id}')
|
||||||
|
@router.get('/{object_l1}/{object_l2}/{object_l3}/{object_id}')
|
||||||
|
async def get_obj(object_l1: str=None, object_l2: str=None, object_l3: str=None, object_id: str=None, x_account_id: str = Header(...),
|
||||||
|
qry_str: Optional[str] = Query(None, max_length=50),
|
||||||
|
qry_int: Optional[int] = None,
|
||||||
|
by_alias: Optional[bool] = True,
|
||||||
|
exclude_unset: Optional[bool] = True,
|
||||||
|
):
|
||||||
|
log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
log.debug(by_alias)
|
||||||
|
log.debug(exclude_unset)
|
||||||
|
log.debug(qry_str)
|
||||||
|
log.debug(qry_int)
|
||||||
|
|
||||||
|
response_data = {}
|
||||||
|
response_data['object_l1'] = object_l1
|
||||||
|
response_data['object_l2'] = object_l2
|
||||||
|
response_data['object_l3'] = object_l3
|
||||||
|
response_data['object_id'] = object_id
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
data['id_random'] = 1
|
||||||
|
|
||||||
|
sql_select_str = f"""
|
||||||
|
SELECT `user`.id AS 'user_id', `user`.id_random AS 'user_id_random', username, name, email, super
|
||||||
|
FROM `user` AS `user`
|
||||||
|
WHERE `user`.id = :id_random;
|
||||||
|
"""
|
||||||
|
|
||||||
|
#sql_result = sql_select(table_name='user', record_id=1)
|
||||||
|
sql_result = sql_select(sql=sql_select_str, data=data)
|
||||||
|
resp_data = User_Base(**sql_result).dict(by_alias=by_alias, exclude_unset=exclude_unset)
|
||||||
|
|
||||||
|
#response_data['sql_result'] = sql_result
|
||||||
|
|
||||||
|
resp = mk_resp(data=resp_data)
|
||||||
|
|
||||||
|
return resp
|
||||||
69
app/routers/common_field_schema.py
Normal file
69
app/routers/common_field_schema.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import copy, datetime, hashlib, logging, os, pytz, redis, secrets
|
||||||
|
|
||||||
|
default_num_bytes = 8 # URL safe 8 bytes is 11 characters long and 16 bytes is 22 characters long
|
||||||
|
|
||||||
|
xxx_id_random_field_schema: dict = {
|
||||||
|
'title': 'XXX ID Random',
|
||||||
|
'description': 'This is an id_random field for this object.',
|
||||||
|
'min_length': 11,
|
||||||
|
'max_length': 22,
|
||||||
|
'example': secrets.token_urlsafe(8) # random each reloading of app with: secrets.token_urlsafe(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
xxx_id_random_field_schema_default: dict = copy.copy(xxx_id_random_field_schema)
|
||||||
|
xxx_id_random_field_schema_default['default_factory'] = lambda:secrets.token_urlsafe(8)
|
||||||
|
|
||||||
|
created_updated_on_field_schema: dict = {
|
||||||
|
'title': 'Created or Updated On',
|
||||||
|
'description': 'This is the created or updated on timestamp field for this object. It is filled in by the SQL DB.',
|
||||||
|
'example': '2021-12-31T21:10:10'
|
||||||
|
}
|
||||||
|
|
||||||
|
base_fields = {}
|
||||||
|
#base_fields['id_random'] = xxx_id_random_field_schema_default
|
||||||
|
base_fields['obj_id_random'] = xxx_id_random_field_schema # General or generic object_id_random
|
||||||
|
base_fields['account_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['address_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['archive_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['contact_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['event_exhibit_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['event_file_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['event_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['event_presentation_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['event_registration_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['fundraising_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['hosted_file_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['membership_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['membership_profile_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['order_cart_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['order_cart_line_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['order_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['order_line_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['order_transaction_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['organization_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['page_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['person_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['post_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['post_comment_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['product_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['site_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['site_domain_id_random'] = xxx_id_random_field_schema
|
||||||
|
base_fields['user_id_random'] = xxx_id_random_field_schema
|
||||||
|
|
||||||
|
base_fields['created_on'] = created_updated_on_field_schema
|
||||||
|
base_fields['updated_on'] = created_updated_on_field_schema
|
||||||
|
|
||||||
|
base_fields['obj_type'] = {}
|
||||||
|
base_fields['obj_id_random'] = xxx_id_random_field_schema_default
|
||||||
|
base_fields['obj_id_rand'] = xxx_id_random_field_schema_default
|
||||||
|
base_fields['obj_id'] = {}
|
||||||
|
base_fields['obj_name'] = {}
|
||||||
|
base_fields['obj_notes'] = {}
|
||||||
|
|
||||||
|
base_fields['for_id_random'] = xxx_id_random_field_schema_default
|
||||||
|
|
||||||
|
#xxx_id_random_field_schema['alias'] = 'order_id_random'
|
||||||
|
#base_fields['id_random'] = xxx_id_random_field_schema_default
|
||||||
|
#xxx_id_random_field_schema['alias'] = 'user_id_random_x'
|
||||||
|
#c = {'alias': 'user_id_random_x'}
|
||||||
|
#base_fields['user_id_random'] = combine_dict(xxx_id_random_field_schema, c)
|
||||||
129
app/routers/contact_model.py
Normal file
129
app/routers/contact_model.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
import datetime, hashlib, logging, os, pytz, redis, secrets
|
||||||
|
|
||||||
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, validator
|
||||||
|
|
||||||
|
from ..lib_general import *
|
||||||
|
from ..log import *
|
||||||
|
from .common_field_schema import base_fields, default_num_bytes
|
||||||
|
#from .account_model import Account_Base
|
||||||
|
from .address_model import Address_Base
|
||||||
|
|
||||||
|
|
||||||
|
class Contact_Base(BaseModel):
|
||||||
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
#from .account_model import Account_Base
|
||||||
|
#from .address_model import Address_Base
|
||||||
|
|
||||||
|
id_random: Optional[str] = Field(
|
||||||
|
**base_fields['contact_id_random'],
|
||||||
|
alias='contact_id_random',
|
||||||
|
default_factory=lambda:secrets.token_urlsafe(default_num_bytes),
|
||||||
|
)
|
||||||
|
id: Optional[int] = Field(
|
||||||
|
#alias='contact_id'
|
||||||
|
)
|
||||||
|
account_id_random: Optional[str]
|
||||||
|
account_id: Optional[int]
|
||||||
|
address_id_random: Optional[str]
|
||||||
|
address_id: Optional[int]
|
||||||
|
|
||||||
|
for_type: Optional[str]
|
||||||
|
for_id_random: Optional[str]
|
||||||
|
for_id: Optional[int]
|
||||||
|
|
||||||
|
name: Optional[str]
|
||||||
|
title: Optional[str]
|
||||||
|
tagline: Optional[str]
|
||||||
|
|
||||||
|
description: Optional[str]
|
||||||
|
lu_time_zone_id: Optional[str]
|
||||||
|
timezone: Optional[str]
|
||||||
|
|
||||||
|
email: Optional[str]
|
||||||
|
website: Optional[str]
|
||||||
|
website_name: Optional[str]
|
||||||
|
|
||||||
|
phone_mobile: Optional[str]
|
||||||
|
phone_home: Optional[str]
|
||||||
|
phone_office: Optional[str]
|
||||||
|
phone_land: Optional[str]
|
||||||
|
phone_fax: Optional[str]
|
||||||
|
|
||||||
|
facebook: Optional[str]
|
||||||
|
instagram: Optional[str]
|
||||||
|
twitter: Optional[str]
|
||||||
|
linkedin: Optional[str]
|
||||||
|
|
||||||
|
other_site_url: Optional[str]
|
||||||
|
other_site_name: Optional[str]
|
||||||
|
|
||||||
|
other_text: Optional[str]
|
||||||
|
other_json: Optional[Json]
|
||||||
|
|
||||||
|
priority: Optional[int]
|
||||||
|
sort: Optional[int]
|
||||||
|
group: Optional[str]
|
||||||
|
|
||||||
|
#account: Optional[Account_Base] = Account_Base()
|
||||||
|
address: Optional[Address_Base] = Address_Base()
|
||||||
|
|
||||||
|
created_on: Optional[datetime.datetime] = None
|
||||||
|
updated_on: Optional[datetime.datetime] = None
|
||||||
|
|
||||||
|
_processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now)
|
||||||
|
|
||||||
|
#@validator('contact_id_random', always=True)
|
||||||
|
def contact_id_random_copy(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['id_random']:
|
||||||
|
return values['id_random']
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('id', always=True)
|
||||||
|
def contact_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['id_random']:
|
||||||
|
log.debug(values['id_random'])
|
||||||
|
return redis_lookup_id_random(record_id_random=values['id_random'], table_name='contact')
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('account_id', always=True)
|
||||||
|
def account_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['account_id_random']:
|
||||||
|
return redis_lookup_id_random(record_id_random=values['account_id_random'], table_name='account')
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('address_id', always=True)
|
||||||
|
def address_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values.get('address_id_random', None):
|
||||||
|
return redis_lookup_id_random(record_id_random=values['address_id_random'], table_name='address')
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('for_id', always=True)
|
||||||
|
def for_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['for_id_random'] and values['for_type']:
|
||||||
|
return redis_lookup_id_random(record_id_random=values['for_id_random'], table_name=values['for_type'])
|
||||||
|
return None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
underscore_attrs_are_private = True
|
||||||
|
fields = base_fields
|
||||||
|
|
||||||
|
Contact_Base.update_forward_refs()
|
||||||
55
app/routers/core_object_model.py
Normal file
55
app/routers/core_object_model.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
import datetime, hashlib, logging, os, pytz, redis, secrets
|
||||||
|
|
||||||
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
from pydantic import BaseModel, EmailStr, Field, PrivateAttr, ValidationError, validator
|
||||||
|
|
||||||
|
from .common_field_schema import base_fields
|
||||||
|
|
||||||
|
|
||||||
|
class Core_Object_Base(BaseModel):
|
||||||
|
app.logger.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
app.logger.debug(locals())
|
||||||
|
|
||||||
|
obj_type: str
|
||||||
|
obj_id_random: str # alias this one based on obj_type?
|
||||||
|
obj_id_rand: str # alias this one based on obj_type?
|
||||||
|
obj_id: int # alias this one?
|
||||||
|
obj_name: Optional[str]
|
||||||
|
id_random: str # alias this one?
|
||||||
|
id: int # alias this one?
|
||||||
|
|
||||||
|
account_id_random: Optional[str]
|
||||||
|
account_id: Optional[str]
|
||||||
|
|
||||||
|
notes: Optional[str]
|
||||||
|
created_on: Optional[datetime.datetime] = None
|
||||||
|
updated_on: Optional[datetime.datetime] = None
|
||||||
|
|
||||||
|
_processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now)
|
||||||
|
|
||||||
|
|
||||||
|
class Example_Object_Base(Core_Object_Base): # Based on Core_Object_Base
|
||||||
|
title: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
password_set_on: Optional[datetime.datetime] = None
|
||||||
|
archive_on: Optional[datetime.datetime] = None
|
||||||
|
logged_in_on: Optional[datetime.datetime] = None
|
||||||
|
last_activity_on: Optional[datetime.datetime] = None
|
||||||
|
other_random_fields: dict
|
||||||
|
list_of_: Optional[dict] = {}
|
||||||
|
|
||||||
|
# Create, Read/Get, Update, Delete
|
||||||
|
# CRUD or CGUD
|
||||||
|
|
||||||
|
# def create_object(object_data):
|
||||||
|
# return False # True, False, or None or object_data
|
||||||
|
|
||||||
|
# def get_object(object_id):
|
||||||
|
# return object_data # False or None
|
||||||
|
|
||||||
|
# def update_object(object_id, object_data):
|
||||||
|
# return False # True, False, or None or object_data
|
||||||
|
|
||||||
|
# def delete_object(object_id):
|
||||||
|
# return False # True, False, or None or object_data
|
||||||
62
app/routers/response_model.py
Normal file
62
app/routers/response_model.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
import datetime, hashlib, logging, os, pytz, redis, secrets
|
||||||
|
|
||||||
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
from pydantic import BaseModel, EmailStr, Field, PrivateAttr, ValidationError, validator
|
||||||
|
|
||||||
|
from ..lib_general import *
|
||||||
|
from ..log import *
|
||||||
|
from .common_field_schema import base_fields
|
||||||
|
from app.config import settings
|
||||||
|
|
||||||
|
|
||||||
|
# The pydantic BaseModel to help make consistent REST responses - STI 2021-03-05
|
||||||
|
class Resp_Body_Base(BaseModel):
|
||||||
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
data: Union[dict, list]
|
||||||
|
meta: Optional[dict]
|
||||||
|
|
||||||
|
|
||||||
|
# The make response function for REST - STI 2021-03-05
|
||||||
|
def mk_resp(data={}, dict_to_json=None, status_code=200, status_message=None, status_name=None, success=True, details=None, by_alias=True, exclude_unset=True):
|
||||||
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if data is None: data = { 'result': None }
|
||||||
|
elif data == False: data = { 'result': False }
|
||||||
|
elif data == True: data = { 'result': True }
|
||||||
|
|
||||||
|
resp_body = {}
|
||||||
|
resp_body['data'] = data
|
||||||
|
resp_body['meta'] = {}
|
||||||
|
resp_body['meta']['details'] = details
|
||||||
|
resp_body['meta']['status_code'] = status_code
|
||||||
|
resp_body['meta']['status_message'] = settings.HTTP_STATUS_LI[status_code]['message']
|
||||||
|
resp_body['meta']['status_name'] = settings.HTTP_STATUS_LI[status_code]['name']
|
||||||
|
resp_body['meta']['success'] = success
|
||||||
|
|
||||||
|
if isinstance(data, bool):
|
||||||
|
resp_body['meta']['data_type'] = 'bool'
|
||||||
|
elif isinstance(data, int):
|
||||||
|
resp_body['meta']['data_type'] = 'int'
|
||||||
|
elif isinstance(data, str):
|
||||||
|
resp_body['meta']['data_type'] = 'str'
|
||||||
|
elif isinstance(data, dict):
|
||||||
|
resp_body['meta']['data_type'] = 'dict'
|
||||||
|
elif isinstance(data, list):
|
||||||
|
resp_body['meta']['data_type'] = 'list'
|
||||||
|
resp_body['meta']['data_list_count'] = len(data)
|
||||||
|
|
||||||
|
log.debug(type(resp_body['data']))
|
||||||
|
|
||||||
|
resp_body = Resp_Body_Base(**resp_body).dict(by_alias=by_alias, exclude_unset=exclude_unset)
|
||||||
|
#resp_body_json = resp_body.json(by_alias=True, exclude_unset=False)
|
||||||
|
|
||||||
|
#response = app.response_class(
|
||||||
|
#response=resp_body_json,
|
||||||
|
#status=status_code,
|
||||||
|
#mimetype='application/json'
|
||||||
|
#)
|
||||||
|
return resp_body
|
||||||
140
app/routers/user_model.py
Normal file
140
app/routers/user_model.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
import datetime, hashlib, logging, os, pytz, redis, secrets
|
||||||
|
|
||||||
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, validator
|
||||||
|
|
||||||
|
from ..lib_general import *
|
||||||
|
from ..log import *
|
||||||
|
from .common_field_schema import base_fields, default_num_bytes
|
||||||
|
#from .account_model import Account_Base
|
||||||
|
from .contact_model import Contact_Base
|
||||||
|
#from .organization_model import Organization_Base
|
||||||
|
#from .person_model import Person_Base
|
||||||
|
|
||||||
|
|
||||||
|
class User_Base(BaseModel):
|
||||||
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
#from .account_model import Account_Base
|
||||||
|
#from .contact_model import Contact_Base
|
||||||
|
#from .organization_model import Organization_Base
|
||||||
|
#from .person_model import Person_Base
|
||||||
|
|
||||||
|
#if TYPE_CHECKING:
|
||||||
|
#from .person_model import Person_Base
|
||||||
|
|
||||||
|
id_random: Optional[str] = Field(
|
||||||
|
**base_fields['user_id_random'],
|
||||||
|
alias='user_id_random',
|
||||||
|
default_factory=lambda:secrets.token_urlsafe(default_num_bytes),
|
||||||
|
)
|
||||||
|
id: Optional[int] = Field(
|
||||||
|
#alias='user_id'
|
||||||
|
)
|
||||||
|
account_id_random: Optional[str]
|
||||||
|
account_id: Optional[int]
|
||||||
|
contact_id_random: Optional[str]
|
||||||
|
contact_id: Optional[int]
|
||||||
|
organization_id_random: Optional[str]
|
||||||
|
organization_id: Optional[int]
|
||||||
|
person_id_random: Optional[str]
|
||||||
|
person_id: Optional[int]
|
||||||
|
|
||||||
|
username: Optional[str]
|
||||||
|
name: Optional[str]
|
||||||
|
email: Optional[str]
|
||||||
|
email_verified: Optional[bool]
|
||||||
|
password: Optional[str]
|
||||||
|
auth_key: Optional[str]
|
||||||
|
|
||||||
|
enable: Optional[bool]
|
||||||
|
enable_from: Optional[datetime.datetime] = None
|
||||||
|
enable_to: Optional[datetime.datetime] = None
|
||||||
|
|
||||||
|
super: Optional[bool]
|
||||||
|
manager: Optional[bool]
|
||||||
|
administrator: Optional[bool]
|
||||||
|
public: Optional[bool]
|
||||||
|
verified: Optional[bool]
|
||||||
|
status_id: Optional[int]
|
||||||
|
status_name: Optional[str]
|
||||||
|
|
||||||
|
password_set_on: Optional[datetime.datetime] = None
|
||||||
|
password_reset_token: Optional[str] = None
|
||||||
|
password_reset_expire_on: Optional[datetime.datetime] = None
|
||||||
|
logged_in_on: Optional[datetime.datetime] = None
|
||||||
|
last_activity_on: Optional[datetime.datetime] = None
|
||||||
|
|
||||||
|
#account: Optional[Account_Base]# = Account_Base()
|
||||||
|
contact: Optional[Contact_Base]# = Contact_Base()
|
||||||
|
#organization: Optional[Organization_Base]# = Organization_Base()
|
||||||
|
#person: Optional[Person_Base]# = Person_Base()
|
||||||
|
|
||||||
|
notes: Optional[str]
|
||||||
|
created_on: Optional[datetime.datetime] = None
|
||||||
|
updated_on: Optional[datetime.datetime] = None
|
||||||
|
|
||||||
|
_processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now)
|
||||||
|
|
||||||
|
#@validator('user_id_random', always=True)
|
||||||
|
def user_id_random_copy(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['id_random']:
|
||||||
|
return values['id_random']
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('id', always=True)
|
||||||
|
def user_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['id_random']:
|
||||||
|
log.debug(values['id_random'])
|
||||||
|
return redis_lookup_id_random(record_id_random=values['id_random'], table_name='user')
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('account_id', always=True)
|
||||||
|
def account_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['account_id_random']:
|
||||||
|
return redis_lookup_id_random(record_id_random=values['account_id_random'], table_name='account')
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('contact_id', always=True)
|
||||||
|
def contact_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['contact_id_random']:
|
||||||
|
return redis_lookup_id_random(record_id_random=values['contact_id_random'], table_name='contact')
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('organization_id', always=True)
|
||||||
|
def organization_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['organization_id_random']:
|
||||||
|
return redis_lookup_id_random(record_id_random=values['organization_id_random'], table_name='organization')
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('person_id', always=True)
|
||||||
|
def person_id_lookup(cls, v, values, **kwargs):
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
if values['person_id_random']:
|
||||||
|
return redis_lookup_id_random(record_id_random=values['person_id_random'], table_name='person')
|
||||||
|
return None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
underscore_attrs_are_private = True
|
||||||
|
fields = base_fields
|
||||||
|
|
||||||
|
User_Base.update_forward_refs()
|
||||||
Reference in New Issue
Block a user