【问题标题】:Strange SQL error when selecting with a filter in SQLAlchemy在 SQLAlchemy 中使用过滤器进行选择时出现奇怪的 SQL 错误
【发布时间】:2012-07-13 20:11:21
【问题描述】:

编辑我找到了答案,它与模型的设置方式无关。我试图将 request.form 从烧瓶传递给实际创建地址的函数。显然将其转换为 kwargs 会导致问题。手动指定每个参数使一切正常。

我是 SQLAlchemy 的新手,到目前为止一切进展顺利,但我遇到了一个问题,我完全被难住了,所以我希望这里有人知道我做错了什么!

我有一个地址模型,我正在尝试从中进行选择。模型如下所示:

class Address(Base):
    __tablename__ = 'address'

    id = Column(Integer, primary_key=True)
    comment = Column(String(255))
    address1 = Column(String(100))
    address2 = Column(String(100))
    city = Column(String(45))
    state = Column(String(2))
    zip = Column(String(5))
    zip4 = Column(String(4))
    lat = Column(Float)
    lon = Column(Float)

我正在尝试匹配通过我的应用程序输入的地址的元素,以查看是否可以在不进行地理编码的情况下找到纬度和经度。我在其他有效的查询中使用了filter_by,所以我在这里尝试同样的事情:(address dict 来自我的应用程序的 post 参数)

matched_address = session.query(Address).filter_by(
    address1=address['address1'],
    city=address['city'],
    state=address['state'],
    zip=address['zip']
).one()

不幸的是,这失败并出现 SQL 错误:

ProgrammingError: (ProgrammingError) (1064, 'You have an error in your SQL syntax; 
check the manual that corresponds to your MySQL server version for the right 
syntax to use near \') AND address.state = ("\'XX\'",) AND address.zip = 
("\'XXXXX\'",) AND address.city \' at line 3')

这看起来非常非常难看!额外的() 来自哪里?我在对create_engine() 的调用中打开了echo=True,它打印了一个看起来正确的查询(我在mysql 客户端中尝试过,没有任何问题):

SELECT address.id AS address_id, address.comment AS address_comment, 
    address.address1 AS address_address1, address.address2 AS address_address2, 
    address.city AS address_city, address.state AS address_state, 
    address.zip AS address_zip, address.zip4 AS address_zip4, 
    address.lat AS address_lat, address.lon AS address_lon 
FROM address 
WHERE address.address1 = %s AND address.state = %s AND address.zip = %s AND 
    address.city = %s 
LIMIT %s

但是它说它输出的参数似乎有点奇怪,虽然我不确定这是否只是 python 对象,或者它是否真的以这种方式进入 MySQL:(注意 X 字符只是我真实地址的替换)

([u'1111 XXXXXX St'], [u'XX'], [u'XXXXX'], [u'XXXXX'], 1)

只是为了好玩,我尝试使用filter() 而不是filter_by(),但我得到了同样的错误:

matched_address = database.session.query(Address).filter(
    and_(Address.address1==address['address1'], Address.city==address['city'],    
    Address.state==address['state'],Address.zip==address['zip'])).one()

抱歉,这是一个基本问题。提前感谢您的帮助!

【问题讨论】:

  • zip 是 Python 中的内置函数。使用它作为变量名将使您难以追踪此类错误。
  • @DavidNehme 我只尝试使用城市或州来查看查询是否有效,但我仍然遇到同样的问题。

标签: python sqlalchemy


【解决方案1】:

我找到了答案,它与模型的设置方式无关。我试图将 request.form 从烧瓶传递给实际创建地址的函数。显然将其转换为 kwargs 会导致问题。手动指定每个参数使一切正常。

【讨论】:

  • 如果有人想知道为什么会这样,我遇到了同样的情况,可以提供一些额外的细节:Flask 使用 Werkzeug 的 ImmutableMultiDict 类来处理它的请求参数。由于当您尝试直接在该类中解压缩请求参数时,IMD 类需要为给定键存储多个值(以处理 HTML 提交),因此每个值都被包装在一个列表中。因此,这些值(所有列表包装)被传递给 SQLAlchemy,它当然需要字符串,并且在传递列表时会惨遭失败。简单修复:在解包前使用 to_dict() 方法。
【解决方案2】:

您的类定义对我有用,以您未提供的大量设置代码为模。为随后的大量代码道歉。一、定义:

from sqlalchemy import create_engine, Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class Address(Base):
    __tablename__ = 'address'

    id = Column(Integer, primary_key=True)
    comment = Column(String(255))
    address1 = Column(String(100))
    address2 = Column(String(100))
    city = Column(String(45))
    state = Column(String(2))
    zip  = Column(String(5))
    zip4 = Column(String(4))
    lat = Column(Float)
    lon = Column(Float)

    #note I am only initializing the querying columns. i am lazy   
    def __init__(self, cm, a1, ct, st):
        self.comment = cm
        self.address1 = a1
        self.city = ct
        self.state = st

将该类定义放入文件 MyAddr.py 并导入它,然后启动 python。请注意,我在 SA 演示中使用 sqlite。

>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///:memory:', echo=False)
>>> from sqlalchemy.orm import sessionmaker
>>> Session = sessionmaker(bind=engine)
>>> from MyAddr import Base, Address
>>> Base.metadata.create_all(engine)
>>> 
>>> a = '1234 Main St.'
>>> c = 'Anywhere'
>>> st= 'MO'
>>> cm= 'Blah blah blah blah blah'
>>> 
>>> addr = Address(cm, a, c, st)
>>> 
>>> s = Session()
>>> s.add(addr)
>>> s.commit()
>>> 
>>> q = s.query(Address).filter_by(address1=a,city=c)
>>> 
>>> q.one()
<op.Address object at 0x10d158a50>
>>> q = s.query(Address).filter_by(address1=a,city=c,state=st)
>>> q.one()
<op.Address object at 0x10d158a50>
>>> abc = q.one()
>>> abc
<op.Address object at 0x10d158a50>
>>> abc.city
u'Anywhere'
>>> abc.state
u'MO'
>>> abc.address1
u'1234 Main St.'

由于您的类定义可以起作用,因此(对我而言)您的问题中没有足够的信息来了解它为什么不起作用。也许这与您的输入数据有关?您的错误消息包含两组引号,一组已转义。您的输入字典是否包含带引号的字符串?

【讨论】:

  • 您的设置与我的类似。我正在使用mysql。主要区别在于我没有为该类创建自定义 init。据我了解,提供了一个可以满足我需要的基础。
【解决方案3】:

您是否尝试过打印出address['address1'] 等的值? repr 可能是最清楚的。

[编辑]

查看文档后,我想知道您是否应该这样做:

from sqlalchemy import and_
filter(and_(address1 == address['address1'], city == address['city'], ...))

【讨论】:

  • 是的,我打印了这些值,它们是正确的。我正在使用烧瓶。地址信息是从表单输入中获取的。
  • 不确定。我认为 filter 必须按照我原来的问题中的方式调用(使用整个类定义并使用 == 而不是单个)
  • 我已经仔细检查过,你是对的,它应该使用==(在上面更正)。
猜你喜欢
  • 2012-11-22
  • 1970-01-01
  • 1970-01-01
  • 2021-11-26
  • 1970-01-01
  • 2012-03-22
  • 2014-06-08
  • 2021-05-10
  • 2020-08-30
相关资源
最近更新 更多