如何在Python和sqlite3中实现非ASCII字符的不区分大小写查询?
关于这个问题,有一些明显的建议和资源,但我找不到直接的例子。
http://www.sqlite.org/faq.html#q18 上说可以重写 NOCASE 排序规则,还有 lower 和 upper 函数。我在一个 Django 测试案例中这样做了,但在我的测试中,它们实际上都没有被调用。
from django.test import TestCase
来自 django.contrib.sites.models 的 Site
来自 basic.blog.models 的 Post 和 Settings
class CaseInsenstiveTest(TestCase):
def setUp(self):
self.site = Site.objects.create()
self.settings = Settings.objects.create(site=self.site)
Settings.get_current = classmethod(lambda cls: self.settings)
def testLike(self):
from django.db import connection
Post.objects.all() # Sets up the connection
def like(x, y, z=None):
"""Y LIKE X [ESCAPE Z]"""
assert x.startswith('%') and x.endswith('%')
like.called = True
print "like(%r, %r, %r)" % (x, y, z)
return x[1:-1].lower() in y.lower()
like.called = False
def lower(s):
print "lower(%r)" % (s,)
return s.lower()
def upper(s):
print "upper(%r)" % (s,)
return s.upper()
connection.connection.create_function('lower', 1, lower)
connection.connection.create_function('upper', 1, upper)
connection.connection.create_function('like', 3, like)
def NOCASE(a, b):
print "NOCASE(%r, %r)" % (a, b)
return cmp(a.lower(), b.lower())
connection.connection.create_collation('NOCASE', NOCASE)
Post.objects.create(slug='foo', title='Foo')
Post.objects.filter(title__icontains='foo')
似乎没有一个注册的函数或者排序规则被实际调用。有人能指出哪里出了问题吗?
注意:我知道 like 函数还不正确。我只是想弄清楚什么时候调用了什么,这样我才能知道需要重写什么以及如何重写。
1 个回答
2
看起来在django之外一切正常:
所以可能是你的django出了问题?你确定表的字段是用NOCASE排序规则创建的吗?
import sqlite3
def NOCASE(a, b):
print 'comparing %r with %r...' % (a, b)
return cmp(a.lower(), b.lower())
con = sqlite3.connect('')
cur = con.cursor()
cur.execute('CREATE TABLE foo (id INTEGER, text VARCHAR collate NOCASE)')
cur.executemany('INSERT INTO foo (id, text) VALUES (?, ?)', [
(1, u'test'), (2, u'TEST'), (3, u'uest'), (4, u'UEST')])
con.commit()
con.create_collation('NOCASE', NOCASE)
cur = con.cursor()
cur.execute('SELECT * FROM foo ORDER BY text ASC')
print cur.fetchall()
输出结果:
comparing 'test' with 'TEST'...
comparing 'test' with 'uest'...
comparing 'TEST' with 'uest'...
comparing 'TEST' with 'UEST'...
comparing 'uest' with 'UEST'...
[(1, u'test'), (2, u'TEST'), (3, u'uest'), (4, u'UEST')]
同样,使用定义好的函数也能正常工作(数据集是一样的)
def my_lower(text):
print "I'm lowering %r myself" % (text,)
return text.lower()
con.create_function('lower', 1, my_lower)
cur.execute('SELECT lower(text) FROM foo')
输出结果:
I'm lowering u'test' myself
I'm lowering u'TEST' myself
I'm lowering u'uest' myself
I'm lowering u'UEST' myself
[(u'test',), (u'test',), (u'uest',), (u'uest',)]
同样,对于LIKE
操作,如果你想支持两种形式,你需要在两个参数的形式(X LIKE Y
)和三个参数的形式(X LIKE Y ESCAPE Z
)中注册这个函数:
def my_like(a, b, escape=None):
print 'checking if %r matches %r' % (a, b)
return b.lower().startswith(a[0].lower())
con.create_function('like', 2, my_like) # X LIKE Y
con.create_function('like', 3, my_like) # X LIKE Y ESCAPE Z
cur.execute('SELECT * FROM foo WHERE text LIKE ?', (u't%',))
得到的输出结果:
checking if u't%' matches u'test'
checking if u't%' matches u'TEST'
checking if u't%' matches u'uest'
checking if u't%' matches u'UEST'
[(1, u'test'), (2, u'TEST')]