Django

Code

Changeset 5555

Show
Ignore:
Timestamp:
06/27/07 13:58:10 (1 year ago)
Author:
jacob
Message:

Fixed #1465: added support for regex lookups. Thanks, Tom Tobin.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/db/backends/mysql/base.py

    r5519 r5555  
    248248    'contains': 'LIKE BINARY %s', 
    249249    'icontains': 'LIKE %s', 
     250    'regex': 'REGEXP BINARY %s', 
     251    'iregex': 'REGEXP %s', 
    250252    'gt': '> %s', 
    251253    'gte': '>= %s', 
  • django/trunk/django/db/backends/mysql_old/base.py

    r5519 r5555  
    249249    'contains': 'LIKE BINARY %s', 
    250250    'icontains': 'LIKE %s', 
     251    'regex': 'REGEXP BINARY %s', 
     252    'iregex': 'REGEXP %s', 
    251253    'gt': '> %s', 
    252254    'gte': '>= %s', 
  • django/trunk/django/db/backends/postgresql/base.py

    r5519 r5555  
    281281    'contains': 'LIKE %s', 
    282282    'icontains': 'ILIKE %s', 
     283    'regex': '~ %s', 
     284    'iregex': '~* %s', 
    283285    'gt': '> %s', 
    284286    'gte': '>= %s', 
  • django/trunk/django/db/backends/postgresql_psycopg2/base.py

    r5528 r5555  
    226226    'contains': 'LIKE %s', 
    227227    'icontains': 'ILIKE %s', 
     228    'regex': '~ %s', 
     229    'iregex': '~* %s', 
    228230    'gt': '> %s', 
    229231    'gte': '>= %s', 
  • django/trunk/django/db/backends/sqlite3/base.py

    r5519 r5555  
    6565            kwargs.update(self.options) 
    6666            self.connection = Database.connect(**kwargs) 
    67             # Register extract and date_trunc functions. 
     67            # Register extract, date_trunc, and regexp functions. 
    6868            self.connection.create_function("django_extract", 2, _sqlite_extract) 
    6969            self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc) 
     70            self.connection.create_function("regexp", 2, _sqlite_regexp) 
    7071        cursor = self.connection.cursor(factory=SQLiteCursorWrapper) 
    7172        cursor.row_factory = utf8rowFactory 
     
    215216        return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day) 
    216217 
     218def _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 
    217225# SQLite requires LIKE statements to include an ESCAPE clause if the value 
    218226# being escaped has a percent or underscore in it. 
     
    223231    'contains': "LIKE %s ESCAPE '\\'", 
    224232    'icontains': "LIKE %s ESCAPE '\\'", 
     233    'regex': 'REGEXP %s', 
     234    'iregex': "REGEXP '(?i)' || %s", 
    225235    'gt': '> %s', 
    226236    'gte': '>= %s', 
  • django/trunk/django/db/models/fields/__init__.py

    r5544 r5555  
    175175    def get_db_prep_lookup(self, lookup_type, value): 
    176176        "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'): 
    178178            return [value] 
    179179        elif lookup_type in ('range', 'in'): 
  • django/trunk/django/db/models/query.py

    r5519 r5555  
     1from django.conf import settings 
    12from django.db import backend, connection, transaction 
    23from django.db.models.fields import DateField, FieldDoesNotExist 
     
    2324    'startswith', 'istartswith', 'endswith', 'iendswith', 
    2425    'range', 'year', 'month', 'day', 'isnull', 'search', 
     26    'regex', 'iregex', 
    2527) 
    2628 
     
    798800    elif lookup_type == 'search': 
    799801        return backend.get_fulltext_search_sql(table_prefix + field_name) 
     802    elif lookup_type in ('regex', 'iregex'): 
     803        if settings.DATABASE_ENGINE == 'oracle': 
     804            if lookup_type == 'regex': 
     805                match_option = 'c' 
     806            else: 
     807                match_option = 'i' 
     808            return "REGEXP_LIKE(%s%s, %s, '%s')" % (table_prefix, field_name, cast_sql, match_option) 
     809        else: 
     810            raise NotImplementedError 
    800811    raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type) 
    801812 
  • django/trunk/docs/db-api.txt

    r5466 r5555  
    11741174database to add the full-text index. 
    11751175 
     1176regex 
     1177~~~~~ 
     1178 
     1179Case-sensitive regular expression match. 
     1180 
     1181The 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 
     1184Example:: 
     1185 
     1186    Entry.objects.get(title__regex=r'^(An?|The) +') 
     1187 
     1188SQL 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 
     1196Using raw strings for passing in the regular expression syntax is recommended. 
     1197 
     1198Regular expression matching is not supported on the ``ado_mssql`` and 
     1199``oracle`` backends; these will raise a ``NotImplementedError``. 
     1200 
     1201iregex 
     1202~~~~~~ 
     1203 
     1204Case-insensitive regular expression match. 
     1205 
     1206Example:: 
     1207 
     1208    Entry.objects.get(title__iregex=r'^(an?|the) +') 
     1209 
     1210SQL 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 
    11761218Default lookups are exact 
    11771219------------------------- 
  • django/trunk/tests/modeltests/lookup/models.py

    r5385 r5555  
    252252TypeError: Cannot resolve keyword 'headline__starts' into field. Choices are: id, headline, pub_date 
    253253 
     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>] 
    254348"""} 
  • django/trunk/tests/regressiontests/templates/tests.py

    r5530 r5555  
    219219            # value will be converted to a bytestring. 
    220220            'filter-syntax18': (r'{{ var }}', {'var': UnicodeInStrClass()}, '\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91'), 
     221 
     222            # Numbers as filter arguments should work 
     223            'filter-syntax19': ('{{ var|truncatewords:1 }}', {"var": "hello world"}, "hello ..."), 
    221224 
    222225            ### COMMENT SYNTAX ########################################################