I see the value in the SmartMap but the issue I am running into is I want to define my database tables as a class with direct attributes but they should also be like keys of a dictionary (for SQL generation purposes otherwise I need to run through my classes non dunder properties and make one). I intially tried it SmartMap, got confused, started from scratch, and now feel like I do need smart map again but am failing to get how to implement it.
Let me show the code I currently have and perhaps someone here can help me get over this hump. The end goal here is that eventually all the INSERT/UPDATE etc sql will be done in the gateway via message handling. The additional complicating factor here that is not required yet is that occasionally I will need to also use transactions.
Anyways my current code:
#db.model
from db.columnTypes import abstractColumn
class Model(object):
"""
Each table that exists should subclass this. Cna use dev.dbToScript to help aid in writing the code.
"""
def __init__(self):
# check initial properties. for some reason abstract property was not doing the job here
try:
self.__tableName__
except AttributeError:
raise NotImplementedError("Model needs a tablename")
def __setattr__(self, attr, val):
col = getattr(self, attr)
if attr.startswith("__"):
raise ValueError("Cannot dynamically modify table properties. You must change %s in the class definition"%(attr))
elif issubclass(type(col), abstractColumn):
print 'trying to set column value'
col.setValue(val)
def loadDict(self, d):
# loads a dictionary of values to a model
for k, v in d.items():
setattr(self, k, v)
# after this the subclassed models should just have a bunch of properties that are column types a la Django's ORM
Abstract Column and types
#db.columnTypes
from abc import ABCMeta, abstractmethod, abstractproperty
import java.lang.String
import java.lang.Boolean
import java.lang.Float
# base abstraction of class
class abstractColumn(object):
__metaclass__ = ABCMeta
value = None
def __init__(self):
# check initial properties. for some reason abstract property was not doing the job here
try:
self.__typeCheck__
except AttributeError:
raise NotImplementedError("Column needs a typeCheck list")
def __init__(self, nullable=True, default=None, validators=None):
self.nullable = nullable
self.default = None
# validators should be a list of functions and hardcoded args I think - think on this
self.validators = validators
def typeCheck(self, val):
for t in self.__typeCheck__:
if issubclass(type(val), t):
# some acceptable class was found
return
# Value did not match any acceptable class
raise ValueError("Cannot assign value %s of type %s to column with one of these types %s"%(str(val),str(type(val)),str(self.__typeCheck__)))
def validatorCheck(self, val):
if self.validators is None:
return
for validator in validators:
validator(val)
def setValue(self, val):
self.typeCheck(val)
self.validatorCheck(val)
self.value = val
def __repr__(self):
return str(self.value)
def __str__(self):
return "%s with current value %s"%(self.__class__.__name__, str(self.value))
class IntegerField(abstractColumn):
__typeCheck__=[int]
class CharField(abstractColumn):
__typeCheck__=[str, java.lang.String]
def __init__(self, maxLength=None, nullable=True, default=None):
abstractColumn.__init__(self, nullable=nullable, default=default)
if maxLength is None or maxLength < 0:
raise ValueError("maxLength of a CharField cannot be None or Negative")
class BooleanField(abstractColumn):
__typeCheck__=[bool, java.lang.Boolean]
class TextField(abstractColumn):
__typeCheck__=[str, java.lang.String]
class DecimalField(abstractColumn):
__typeCheck__=[float, java.lang.Float, int]
And some testing code -
class Customer(db.model.Model):
__tableName__='listcustomers2'
idx = db.columnTypes.IntegerField(nullable=False)
name = db.columnTypes.CharField(nullable=False, maxLength=255)
description = db.columnTypes.CharField(maxLength=255)
active = db.columnTypes.BooleanField(nullable=False, default=True)
companyTypeId = db.columnTypes.IntegerField(nullable=False)
viewable = db.columnTypes.BooleanField(nullable=False, default=True)
discountId = db.columnTypes.IntegerField()
defaultProjectManager = db.columnTypes.IntegerField(default=-1)
customerPath = db.columnTypes.TextField()
creditHold = db.columnTypes.BooleanField(default=False)
creditLimit = db.columnTypes.DecimalField(default=0.00)
calc_unpaidInvoiceTotal = db.columnTypes.DecimalField()
calc_activeInvoiceFlags = db.columnTypes.IntegerField()
calc_paidOnUnpaidInvoice = db.columnTypes.DecimalField()
# this represents how I would get the data off of a vision window or perspective view
values = {'name':'TestCustomer123', "description":"Test Cust Desc", "active":True, "companyTypeId":1,
"viewable":True,"discountId":5,"defaultProjectManager":1112, "customerPath":"blah//blah", "creditHold":False, "creditLimit":5000.0,
"calc_unpaidInvoiceTotal":0, "calc_activeInvoiceFlags":0, "calc_paidOnUnpaidInvoice":0}
c = Customer()
c.loadDict(values)
This all runs and I am able to examine things like c.name
and see the value I put in which is great.
The hurdle now is what do I do with c.save()
to save this as a new entry and also populate my id column with it. Is there a way to utilize smartMap here or am I going to have to write something that goes through my sublcasses db.model.Model, finds the non-id columns, writes the query and what not. I already have a function that does that with a dictionary it's just how do I do it now in this format.
Or maybe I'm completely wrong headed in my approach.