Skip to content

Instantly share code, notes, and snippets.

@EdgeCaseBerg
Last active December 21, 2015 02:19
Show Gist options
  • Save EdgeCaseBerg/6234059 to your computer and use it in GitHub Desktop.
Save EdgeCaseBerg/6234059 to your computer and use it in GitHub Desktop.
ORM for mysql and python. Subclass ORM_Base with a camel cased class name of your table name and bam. Create a subclassed object, passing in the primary key as the parameter. Kapow. You now have a copy of the object that was in the database.
MYSQL_HOST="localhost"
MYSQL_USER="username"
MYSQL_PASS="password"
MYSQL_DB ="databasename"
#Query for getting column names from database
TABLE_INFO = "SELECT distinct column_name FROM information_schema.columns WHERE table_name = '%s' order by ordinal_position asc"
class ORM_Base():
def __init__(self,pk_id=None):
if pk_id is not None:
self.create(pk_id)
#Do nothing and you must instantiate your object with
#.create or .createFromMysqlRow
def create(self,pk_id):
db = MySQLdb.connect(host=MYSQL_HOST,user=MYSQL_PASS,
passwd=MYSQL_PASS, db=MYSQL_DB)
#Requires that we have a database connection...
curs = db.cursor()
curs.execute(TABLE_INFO % self.getTableName())
fields = sum(curs.fetchall(),()) #flatten
db.query("SELECT * FROM %s WHERE id=%d" % (self.getTableName(),pk_id))
info_row = db.store_result().fetch_row()
db.close()
self.createFromMysqlRow(fields,info_row[0])
return self
def createFromMysqlRow(self,field_names,row):
i=0
#We will use the value map during the saving process to order the fields
self.value_map = ()
for field in field_names:
#Ignore the primary field that pops up
if field == "pk":
continue
self.value_map += ((field , i),)
setattr(self,field,row[i])
i +=1
#for chaining if desired
return self
def __str__(self):
return str(self.__dict__)
@classmethod
def getTableName(cls):
return ORM_Base.camelToSnake( cls.__name__)
@classmethod
def camelToSnake(cls, s):
#For snake casing
_underscorer1 = re.compile(r'(.)([A-Z][a-z]+)')
_underscorer2 = re.compile('([a-z0-9])([A-Z])')
subbed = _underscorer1.sub(r'\1_\2', s)
return _underscorer2.sub(r'\1_\2', subbed).lower()
@classmethod
def getFields(cls,curs=None):
if curs is None:
raise "Must pass a valid cursor object to getFields"
curs.execute(TABLE_INFO % cls.getTableName())
return sum(curs.fetchall(),()) #flatten the fields tuple out
def save(self):
#Save the object to the target database
db = MySQLdb.connect(host=MYSQL_HOST,user=MYSQL_PASS,
passwd=MYSQL_PASS, db=MYSQL_DB)
#We should now have a connection.
#perform the save by constructing an insertion tuple into the table
#We get the table name from the class itself (snake case it)
table_name = self.getTableName()
#construct the insert statement which will be:
#INSERT INTO table_name VALUES (...field values...)
#Sort the fields we'll be inserting using the value map
sorted_fields = sorted(self.value_map,key=lambda tup:tup[1])
#Use the order of the sorted fields to grab the fields out from
#the class in the proper order
insertion_string = "INSERT INTO %s VALUES ("
for field,value in sorted_fields:
insertion_string += "%s," % str(getattr(self,str(field)))
#replace the last comma with an ending )
temp_arr = list(insertion_string)
temp_arr[-1] = ')'
insertion_string = "".join(temp_arr)
curs = db.cursor()
try:
curs.execute(insertion_string)
except:
print "problem saving object. %s " % this.__str__()
print "Query was: %s" % insertion_string
finally:
curs.close()
db.close()
@EdgeCaseBerg
Copy link
Author

Credit where credit is due: Snake case function: https://gist.github.com/jaytaylor/3660565

@StoicJester
Copy link

Since camelToSnake does exactly what you would expect it to do, I don't think that can be counted as ironic, even if the function is named in camelCase.

@EdgeCaseBerg
Copy link
Author

haha thanks :P Its left in there from it's source (jaytaylor)

@EdgeCaseBerg
Copy link
Author

Example usage:

#Include code above...

class Users(ORM_Base):
    pass

#connect to database...
curs = db.cursor()

u_fields = Users.getFields(curs)
u = User(1) #create an object populated by user row with id of 1

users = []
u_query = "SELECT * FROM users"
curs.execute(u_query)
for user in curs.fetchall():
    users.append(User().createFromMysqlRow(u_fields,u))

#You now have a list of all users in the database! 

This shows how you can grab just a single object with no worry about connecting to the database --since the parent class does that. Or if you need to lazily create the instances then populate their fields you can use createfromMysqlRow directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment