Ticket #1465: regex-field-lookups.patch
File regex-field-lookups.patch, 10.5 KB (added by , 17 years ago) |
---|
-
django/db/backends/mysql/base.py
diff -r 7a0796fe7efd django/db/backends/mysql/base.py
a b OPERATOR_MAPPING = { 247 247 'iexact': 'LIKE %s', 248 248 'contains': 'LIKE BINARY %s', 249 249 'icontains': 'LIKE %s', 250 'regex': 'REGEXP BINARY %s', 251 'iregex': 'REGEXP %s', 250 252 'gt': '> %s', 251 253 'gte': '>= %s', 252 254 'lt': '< %s', -
django/db/backends/mysql_old/base.py
diff -r 7a0796fe7efd django/db/backends/mysql_old/base.py
a b OPERATOR_MAPPING = { 248 248 'iexact': 'LIKE %s', 249 249 'contains': 'LIKE BINARY %s', 250 250 'icontains': 'LIKE %s', 251 'regex': 'REGEXP BINARY %s', 252 'iregex': 'REGEXP %s', 251 253 'gt': '> %s', 252 254 'gte': '>= %s', 253 255 'lt': '< %s', -
django/db/backends/postgresql/base.py
diff -r 7a0796fe7efd django/db/backends/postgresql/base.py
a b OPERATOR_MAPPING = { 280 280 'iexact': 'ILIKE %s', 281 281 'contains': 'LIKE %s', 282 282 'icontains': 'ILIKE %s', 283 'regex': '~ %s', 284 'iregex': '~* %s', 283 285 'gt': '> %s', 284 286 'gte': '>= %s', 285 287 'lt': '< %s', -
django/db/backends/postgresql_psycopg2/base.py
diff -r 7a0796fe7efd django/db/backends/postgresql_psycopg2/base.py
a b OPERATOR_MAPPING = { 225 225 'iexact': 'ILIKE %s', 226 226 'contains': 'LIKE %s', 227 227 'icontains': 'ILIKE %s', 228 'regex': '~ %s', 229 'iregex': '~* %s', 228 230 'gt': '> %s', 229 231 'gte': '>= %s', 230 232 'lt': '< %s', -
django/db/backends/sqlite3/base.py
diff -r 7a0796fe7efd django/db/backends/sqlite3/base.py
a b class DatabaseWrapper(local): 64 64 } 65 65 kwargs.update(self.options) 66 66 self.connection = Database.connect(**kwargs) 67 # Register extract and date_truncfunctions.67 # Register extract, date_trunc, and regexp functions. 68 68 self.connection.create_function("django_extract", 2, _sqlite_extract) 69 69 self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc) 70 self.connection.create_function("regexp", 2, _sqlite_regexp) 70 71 cursor = self.connection.cursor(factory=SQLiteCursorWrapper) 71 72 cursor.row_factory = utf8rowFactory 72 73 if settings.DEBUG: … … def _sqlite_date_trunc(lookup_type, dt): 214 215 elif lookup_type == 'day': 215 216 return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day) 216 217 218 def _sqlite_regexp(re_pattern, re_string): 219 import re 220 try: 221 return bool(re.search(re_pattern, re_string)) 222 except: 223 return False 224 217 225 # SQLite requires LIKE statements to include an ESCAPE clause if the value 218 226 # being escaped has a percent or underscore in it. 219 227 # See http://www.sqlite.org/lang_expr.html for an explanation. … … OPERATOR_MAPPING = { 222 230 'iexact': "LIKE %s ESCAPE '\\'", 223 231 'contains': "LIKE %s ESCAPE '\\'", 224 232 'icontains': "LIKE %s ESCAPE '\\'", 233 'regex': 'REGEXP %s', 234 'iregex': "REGEXP '(?i)' || %s", 225 235 'gt': '> %s', 226 236 'gte': '>= %s', 227 237 'lt': '< %s', -
django/db/models/fields/__init__.py
diff -r 7a0796fe7efd django/db/models/fields/__init__.py
a b class Field(object): 174 174 175 175 def get_db_prep_lookup(self, lookup_type, value): 176 176 "Returns field's value prepared for database lookup." 177 if lookup_type in ('exact', ' gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'):177 if lookup_type in ('exact', 'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'): 178 178 return [value] 179 179 elif lookup_type in ('range', 'in'): 180 180 return value -
django/db/models/query.py
diff -r 7a0796fe7efd django/db/models/query.py
a b QUERY_TERMS = ( 22 22 'gt', 'gte', 'lt', 'lte', 'in', 23 23 'startswith', 'istartswith', 'endswith', 'iendswith', 24 24 'range', 'year', 'month', 'day', 'isnull', 'search', 25 'regex', 'iregex', 25 26 ) 26 27 27 28 # Size of each "chunk" for get_iterator calls. … … def get_where_clause(lookup_type, table_ 797 798 return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or '')) 798 799 elif lookup_type == 'search': 799 800 return backend.get_fulltext_search_sql(table_prefix + field_name) 801 elif lookup_type in ('regex', 'iregex'): 802 if settings.DATABASE_ENGINE == 'oracle': 803 if lookup_type == 'regex': 804 match_option = 'c' 805 else: 806 match_option = 'i' 807 return "REGEXP_LIKE(%s%s, %s, '%s')" % (table_prefix, field_name, cast_sql, match_option) 808 else: 809 raise NotImplementedError 800 810 raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type) 801 811 802 812 def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0): -
docs/db-api.txt
diff -r 7a0796fe7efd docs/db-api.txt
a b Note this is only available in MySQL and 1173 1173 Note this is only available in MySQL and requires direct manipulation of the 1174 1174 database to add the full-text index. 1175 1175 1176 regex 1177 ~~~~~ 1178 1179 Case-sensitive regular expression match. 1180 1181 The regular expression syntax is that of the database backend in use; for the 1182 ``sqlite`` backend, the syntax is that of Python's ``re`` module. 1183 1184 Example:: 1185 1186 Entry.objects.get(title__regex=r'^(An?|The) +') 1187 1188 SQL equivalents:: 1189 1190 SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL 1191 1192 SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL 1193 1194 SELECT ... WHERE title REGEXP '^(An?|The) +'; -- sqlite 1195 1196 Using raw strings for passing in the regular expression syntax is recommended. 1197 1198 Regular expression matching is not supported on the ``ado_mssql`` and 1199 ``oracle`` backends; these will raise a ``NotImplementedError``. 1200 1201 iregex 1202 ~~~~~~ 1203 1204 Case-insensitive regular expression match. 1205 1206 Example:: 1207 1208 Entry.objects.get(title__iregex=r'^(an?|the) +') 1209 1210 SQL equivalents:: 1211 1212 SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL 1213 1214 SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL 1215 1216 SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- sqlite 1217 1176 1218 Default lookups are exact 1177 1219 ------------------------- 1178 1220 -
tests/modeltests/lookup/models.py
diff -r 7a0796fe7efd tests/modeltests/lookup/models.py
a b Traceback (most recent call last): 251 251 ... 252 252 TypeError: Cannot resolve keyword 'headline__starts' into field. Choices are: id, headline, pub_date 253 253 254 # Create some articles with a bit more interesting headlines for testing field lookups: 255 >>> now = datetime.now() 256 >>> for a in Article.objects.all(): 257 ... a.delete() 258 >>> a1 = Article(pub_date=now, headline='f') 259 >>> a1.save() 260 >>> a2 = Article(pub_date=now, headline='fo') 261 >>> a2.save() 262 >>> a3 = Article(pub_date=now, headline='foo') 263 >>> a3.save() 264 >>> a4 = Article(pub_date=now, headline='fooo') 265 >>> a4.save() 266 >>> a5 = Article(pub_date=now, headline='Foo') 267 >>> a5.save() 268 269 # zero-or-more 270 >>> Article.objects.filter(headline__regex=r'fo*') 271 [<Article: f>, <Article: fo>, <Article: foo>, <Article: fooo>] 272 >>> Article.objects.filter(headline__iregex=r'fo*') 273 [<Article: Foo>, <Article: f>, <Article: fo>, <Article: foo>, <Article: fooo>] 274 275 # one-or-more 276 >>> Article.objects.filter(headline__regex=r'fo+') 277 [<Article: fo>, <Article: foo>, <Article: fooo>] 278 279 # wildcard 280 >>> Article.objects.filter(headline__regex=r'fooo?') 281 [<Article: foo>, <Article: fooo>] 282 283 # and some more: 284 >>> a6 = Article(pub_date=now, headline='bar') 285 >>> a6.save() 286 >>> a7 = Article(pub_date=now, headline='Bar') 287 >>> a7.save() 288 >>> a8 = Article(pub_date=now, headline='baz') 289 >>> a8.save() 290 >>> a9 = Article(pub_date=now, headline='baZ') 291 >>> a9.save() 292 293 # leading anchor 294 >>> Article.objects.filter(headline__regex=r'^b') 295 [<Article: baZ>, <Article: bar>, <Article: baz>] 296 >>> Article.objects.filter(headline__iregex=r'^b') 297 [<Article: Bar>, <Article: baZ>, <Article: bar>, <Article: baz>] 298 299 # trailing anchor 300 >>> Article.objects.filter(headline__regex=r'z$') 301 [<Article: baz>] 302 >>> Article.objects.filter(headline__iregex=r'z$') 303 [<Article: baZ>, <Article: baz>] 304 305 # character sets 306 >>> Article.objects.filter(headline__regex=r'ba[rz]') 307 [<Article: bar>, <Article: baz>] 308 >>> Article.objects.filter(headline__regex=r'ba[RZ]') 309 [<Article: baZ>] 310 >>> Article.objects.filter(headline__iregex=r'ba[RZ]') 311 [<Article: Bar>, <Article: baZ>, <Article: bar>, <Article: baz>] 312 313 # and yet more: 314 >>> a10 = Article(pub_date=now, headline='foobar') 315 >>> a10.save() 316 >>> a11 = Article(pub_date=now, headline='foobaz') 317 >>> a11.save() 318 >>> a12 = Article(pub_date=now, headline='FooBarBaz') 319 >>> a12.save() 320 >>> a13 = Article(pub_date=now, headline='foobarbaz') 321 >>> a13.save() 322 >>> a14 = Article(pub_date=now, headline='zoocarfaz') 323 >>> a14.save() 324 >>> a15 = Article(pub_date=now, headline='barfoobaz') 325 >>> a15.save() 326 >>> a16 = Article(pub_date=now, headline='BAZBARFOO') 327 >>> a16.save() 328 329 # alternation 330 >>> Article.objects.filter(headline__regex=r'foo(bar|baz)') 331 [<Article: barfoobaz>, <Article: foobar>, <Article: foobarbaz>, <Article: foobaz>] 332 >>> Article.objects.filter(headline__iregex=r'foo(bar|baz)') 333 [<Article: FooBarBaz>, <Article: barfoobaz>, <Article: foobar>, <Article: foobarbaz>, <Article: foobaz>] 334 >>> Article.objects.filter(headline__regex=r'^foo(bar|baz)') 335 [<Article: foobar>, <Article: foobarbaz>, <Article: foobaz>] 336 337 # greedy matching 338 >>> Article.objects.filter(headline__regex=r'f.*z') 339 [<Article: barfoobaz>, <Article: foobarbaz>, <Article: foobaz>, <Article: zoocarfaz>] 340 >>> Article.objects.filter(headline__iregex=r'f.*z') 341 [<Article: FooBarBaz>, <Article: barfoobaz>, <Article: foobarbaz>, <Article: foobaz>, <Article: zoocarfaz>] 342 343 # grouping and backreferences 344 >>> Article.objects.filter(headline__regex=r'b(.).*b\1') 345 [<Article: barfoobaz>, <Article: foobarbaz>] 346 >>> Article.objects.filter(headline__iregex=r'b(.).*b\1') 347 [<Article: BAZBARFOO>, <Article: FooBarBaz>, <Article: barfoobaz>, <Article: foobarbaz>] 254 348 """}