吐槽:为什么Flask在数据库查询上不引入lambda而是用这么一种奇怪的方式

学python后端的时候,先学的Django,但是看到了他的数据库查询方式是这样的

id__gt=1 # id > 1?

这个方式我好久都没缓过来,强迫症好难受,(听说这种写法是matalab风,以后我不知道要不要学这个),你好歹用个dict,像mongodb那样,{'id':{'$gt':1}}


缓过来之后去接触Flask,感觉简单的多,可是又到了数据库查询那块,虽然比Django好看很多,但是总感觉哪里不对

>>> Movie.query.filter_by(title='Mahjong').first()
# 获取 title字段值为 Mahjong 的记录
>>> Movie.query.filter(Movie.title=='Mahjong').first()
# 等同于上面的查询,但使用不同的过滤方法

这种语法看着没问题,但我总感觉这是对的拙劣模拟,既然是filter,为什么不这样

>>> Movie.query.filter(lambda elmt:elmt.title == 'Mahjong').first()

还是硬着头皮学吧,希望语法来个更新


纠错1. 这种方法原来是关键字参数

>>> Movie.query.filter_by(title='Mahjong').first()
# 获取 title字段值为 Mahjong 的记录

但是这个肯定不是

>>> Movie.query.filter(Movie.title=='Mahjong').first()
# 等同于上面的查询,但使用不同的过滤方法

感觉用 lambda 表达式的话就只能把所有数据都取出来再过滤了,用关键字参数应该可以将条件写入查询

看来我没有认识到python有关键字参数这个东西,总感觉是或其他元编程手段

为什么不手写SQL?

使用orm不就是为了方便简单吗,如果所有的语法都像这样

Movie.query.filter(lambda elmt:elmt.title == 'Mahjong').first()
Movie.query.filter({'id':{'$gt':1}}).first()

对比

Movie.query.filter(Movie.title=='Mahjong').first()
Movie.query.filter(Movie.title > 1).first()

哪个更简单方便,orm已经是封装了很多层了,只把最简单最直接pythonic的方法暴露给使用者

class Column(object):
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return {self.name: other}

    def __gt__(self, other):
        return {self.name: other}


class AA:
    a = Column("a")
    b = Column("b")


def test(*kwargs):
    print(kwargs)


test(AA.a == 1)
test(AA.b > 10)

难道不是SQL就那几个残疾查询方法,lambda可以嵌入任意Python表达式的原因吗

看到这里其实我有个疑问,为什么这里的ORM执着于把方法和数据绑定在一个类里,
倘若我这样做跟ORM一样吗

(filter (fn [movie]
                    (= (:name movie) "Mahjong")) movies)

另外我认为用lambda表达式可以避免写这些怪异的语法,不用为and,or查询提供新语法 http://www.cherishlau.site/2018/03/29/flask-sqlalchemy-use-or-and/

如果能用lambda的话,and,or这些操作不用为他们发明新语法,这篇文章里提供的and,or用法让我觉得函数式编程是真的好 http://www.cherishlau.site/2018/03/29/flask-sqlalchemy-use-or-and/

lambda可以随便写(turing complete),SQL查询规则就那几条。不用这种DSL你怎么逐步把lambda翻译过去?

说到底还是SQL语言的设计问题,不是框架的问题。SQL设计真那么棒怎么还要ORM一套一套的搞?

能详细讲解一点吗。如果我用lambda会碰到SQL的哪些问题??

def query_fn(_movie):
    # More, more and more side effect codes here!
    pass

Movie.query(query_fn)

这种情况你怎么处理?Python里一个lambda是不透明的结构,你的代码本身在执行前没法判断这个lambda会执行什么内容。如果query函数本身就不合法呢(比如没有描述query,而是跑去执行副作用代码了)

因为orm实质是转换为sql语句去查,就像 @24RGB 所说的,如果你用lambda,其实是把所有数据查出来,然后再用python的lambda来筛选所需要的数据,而把方法绑定到一个类里,是为了方便转化 Model.column > 12 这种形式,它会转换为一个字典或者元组,而不是正常情况下的a > b布尔值,然后用这个值组合成SQL

关于and和or也是一样的,它会返回特殊格式的值,然后把这个值组合成SQL,而不是直接可以使用

1 个赞

看到这里,我想到一种办法,创建一个返回lambda的函数,并保证这个函数无副作用
不过好像在这种情况下作用不大

那你不是把你这里举的例子重新发明了一次?

但是DSL真的不想用,看起来怪怪的

Just do it, submit PR or fork one for yourself

你只要用了 lambda,就注定翻译不进SQL吧,没看明白你的槽点😅

Laravel

 DB::table('users')
            ->where('name', '=', 'John')
            ->orWhere(function ($query) {
                $query->where('votes', '>', 100)
                      ->where('title', '<>', 'Admin');
            })
            ->get();

Elixir Ecto:

def with_minimum(age, height_ft) do
  from u in "users",
    where: u.age > ^age and u.height > ^(height_ft * 3.28),
    select: u.name
end

Lambda 怎么不行了🤔️没看懂

卧草,还有这种东西

Lambda 怎么不行了🤔️没看懂

我没看懂,为什么我觉得用lambda好点,因为那样在Python就可以不用写丑陋的DSL了。说起DSL还是lisp方言的写法优美一点