Last active
December 21, 2015 02:19
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
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.
haha thanks :P Its left in there from it's source (jaytaylor)
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
Credit where credit is due: Snake case function: https://gist.github.com/jaytaylor/3660565