Python:Generic / Templated getters

时间:2016-08-31 08:54:57

标签: python python-3.x sqlalchemy metaprogramming

我有一个代码只是从数据库中提取用户

class users:
    def __init__(self):
        self.engine = create_engine("mysql+pymysql://root:password@127.0.0.1/my_database")
        self.connection = self.engine.connect()
        self.meta = MetaData(bind=self.connection)
        self.users = Table('users', self.meta, autoload = true)

    def get_user_by_user_id(self, user_id):
        stmt = self.users.select().where(self.users.c.user_id == user_id)
        return self.connection.execute(stmt)

    def get_user_by_username(self, username):
        stmt = self.users.select().where(self.users.c.username == username)
        return self.connection.execute(stmt)

    def get_users_by_role_and_company(self, role, company)
        stmt = self.users.select().where(self.users.c.role == role).where(self.users.c.company == company)
        return self.connection.execute(stmt)

现在,我想做的是让getters变得通用:

class users:
    def __init__(self):
        self.engine = create_engine("mysql+pymysql://root:password@127.0.0.1/my_database")
        self.connection = self.engine.connect()
        self.meta = MetaData(bind=self.connection)
        self.users = Table('users', self.meta, autoload = true)

    def get_user(self, **kwargs):
        '''How do I make this generic function?'''

所以,不要打这样的话:

u = users()
u.get_user_by_user_id(1)
u.get_user_by_username('foo')
u.get_users_by_role_and_company('Admin', 'bar')

我会像这样调用泛型函数:

u = users()
u.get_user(user_id=1)
u.get_user(username='foo')
u.get_user(role='Admin', company='bar')

到目前为止,这是我能想到的:

def get_user(**kwargs):
    where_clause = ''
    for key, value in kwargs.items():
        where_clause += '{} == {} AND '.format(key, value)
    where_clause = where_clause[:-5] # remove final AND
    stmt = "SELECT * FROM {tablename} WHERE {where_clause};".format(tablename='users', where_clause=where_clause)
    return self.connection.execute(stmt)

有什么方法可以使用ORM样式来创建语句吗?

3 个答案:

答案 0 :(得分:1)

完全一般化,因此只要表中存在该名称的字段,就可以接受任何合法字段名称的组合。神奇的是getattr,它允许我们动态查找我们感兴趣的字段(如果使用不存在的字段名称调用,则引发AttributeError):

def get_user(self, **kwargs):
    # Basic query
    stmt = self.users.select()

    # Query objects can be refined piecemeal, so we just loop and
    # add new clauses, assigning back to stmt to build up the query
    for field, value in kwargs.items():
        stmt = stmt.where(getattr(self.users.c, field) == value)

    return self.connection.execute(stmt)

答案 1 :(得分:0)

但是你做了所有艰苦的工作。它只是组合你创建的函数,初始化输入变量并在混合中抛出一些if语句。

def get_user(self, user_id=0, username='', role='', company=''):
    if user_id:
        stmt = self.users.select().where(self.users.c.user_id == user_id)
        return self.connection.execute(stmt)
    elif username:
        stmt = self.users.select().where(self.users.c.username == username)
        return self.connection.execute(stmt)
    elif role and company:
        stmt = self.users.select().where(self.users.c.role == role).where(self.users.c.company == company)
        return self.connection.execute(stmt)
    else:
        print('Not adequate information given. Please enter "ID" or "USERNAME", or "ROLE"&"COMPANY"')
        return

请注意,user_id已初始化为0,因此它的布尔值为False。如果可以使用0 id,请将其直接设置为False。因此,由于输入不能随机输入,您是否有理由使用**kwargs进行输入?

或者,如果组合的数量太多而无法编码,我将采用不同的路径(SQL-injection-valnurable脚本传入!),这是以下内容:

def get_user(self, query):
    form_query = 'SELECT user FROM {} WHERE {}'.format(table_name, query)
    # now execute it and return whatever it is you want returned

您不再将变量传递给函数,而是将一个字符串附加到查询并执行。 毋庸置疑,你必须非常小心。

答案 2 :(得分:0)

尝试类似:

def get_user(self, **kwargs):
    if 'user_id' in kwargs:
        (...)
    elif  'username' in kwargs:
        (...)
    elif all(a in ['role','company'] for a in kwargs):
        (...)
    else:
        (...)