Ticket #251: and_or_clauses.patch

File and_or_clauses.patch, 5.0 KB (added by rmunn@…, 14 years ago)

Patch against r390 to add meta.AND and meta.OR functionality; tests included

  • django/core/meta/__init__.py

     
    11061106            select.extend(['%s.%s' % (db_table, f2.name) for f2 in f.rel.to.fields])
    11071107            _fill_table_cache(f.rel.to, select, tables, where, db_table, cache_tables_seen)
    11081108
     1109def AND(**kwargs):
     1110    # Helper function for _parse_lookup below
     1111    return ' AND ', kwargs.items()
     1112
     1113def OR(**kwargs):
     1114    # Helper function for _parse_lookup below
     1115    return ' OR ', kwargs.items()
     1116
    11091117def _throw_bad_kwarg_error(kwarg):
    11101118    # Helper function to remove redundancy.
    11111119    raise TypeError, "got unexpected keyword argument '%s'" % kwarg
     
    11251133            continue
    11261134        if kwarg_value is None:
    11271135            continue
    1128         if kwarg == '_or':
    1129             for val in kwarg_value:
    1130                 tables2, join_where2, where2, params2, table_count = _parse_lookup(val, opts, table_count)
    1131                 tables.extend(tables2)
    1132                 join_where.extend(join_where2)
    1133                 where.append('(%s)' % ' OR '.join(where2))
    1134                 params.extend(params2)
     1136        if kwarg.startswith('clause') and (len(kwarg) == 6 or kwarg[6:].isdigit()):
     1137            clause_type, clause_kwarg_items = kwarg_value
     1138            tables2, join_where2, where2, params2, table_count = _parse_lookup(clause_kwarg_items, opts, table_count)
     1139            tables.extend(tables2)
     1140            join_where.extend(join_where2)
     1141            where.append('(%s)' % clause_type.join(where2))  # clause_type is either ' AND ' or ' OR '
     1142            params.extend(params2)
    11351143            continue
    11361144        lookup_list = kwarg.split(LOOKUP_SEPARATOR)
    11371145        # pk="value" is shorthand for (primary key)__exact="value"
  • tests/testapp/models/__init__.py

     
    11__all__ = ['basic', 'repr', 'custom_methods', 'many_to_one', 'many_to_many',
    22           'ordering', 'lookup', 'get_latest', 'm2m_intermediary', 'one_to_one',
    3            'm2o_recursive', 'm2o_recursive2']
     3           'm2o_recursive', 'm2o_recursive2', 'complex']
  • tests/testapp/models/complex.py

     
     1"""
     213. Building complex queries.
     3
     4To build complex queries involving AND and OR clauses, use the meta.AND
     5and meta.OR functions.
     6"""
     7
     8from django.core import meta
     9
     10class Person(meta.Model):
     11    fields = (
     12        meta.CharField('fname', maxlength=25),
     13        meta.CharField('lname', maxlength=25),
     14        meta.IntegerField('age'),
     15    )
     16    def __repr__(self):
     17        return '%s %s (%s)' % (self.fname, self.lname, self.age)
     18
     19API_TESTS = """
     20# Make sure meta gets imported; we'll need it later
     21>>> from django.core import meta
     22
     23# Add some people to the system
     24>>> p = persons.Person(fname='John', lname='Smith', age=26)
     25>>> p.save()
     26>>> p = persons.Person(fname='Mary', lname='Smith', age=31)
     27>>> p.save()
     28>>> p = persons.Person(fname='Betty', lname='Jones', age=30)
     29>>> p.save()
     30>>> p = persons.Person(fname='Bob', lname='Jones', age=42)
     31>>> p.save()
     32>>> p = persons.Person(fname='Susan', lname='White', age=70)
     33>>> p.save()
     34>>> p = persons.Person(fname='James', lname='White', age=65)
     35>>> p.save()
     36>>> p = persons.Person(fname='Karen', lname='White', age=33)
     37>>> p.save()
     38
     39# Verify that the test data got loaded correctly.
     40>>> persons.get_list(order_by=['age'])
     41[John Smith (26), Betty Jones (30), Mary Smith (31), Karen White (33), \
     42Bob Jones (42), James White (65), Susan White (70)]
     43
     44# Search clauses are joined with an AND by default
     45>>> persons.get_list(order_by=['age'], lname__exact='Smith')
     46[John Smith (26), Mary Smith (31)]
     47>>> persons.get_list(order_by=['age'], lname__exact='Smith', age__gte=30)
     48[Mary Smith (31)]
     49
     50# Use "clause=meta.OR" to join with an OR instead
     51>>> persons.get_list(order_by=['age'], clause=meta.OR(lname__exact='Smith', age__gte=50))
     52[John Smith (26), Mary Smith (31), James White (65), Susan White (70)]
     53
     54# You can nest AND clauses inside OR clauses
     55>>> persons.get_list(order_by=['age'], clause=meta.OR(lname__exact='Smith',
     56...     clause=meta.AND(lname__exact='White', age__lte=65)))
     57[John Smith (26), Mary Smith (31), Karen White (33), James White (65)]
     58
     59# Since Python doesn't let you repeat the same keyword parameter twice in a
     60# function call, use clause# (where # is a number) if you want to have
     61# multiple AND or OR clauses in a single get_list
     62# (Remember that the outer level of clauses is joined by AND)
     63>>> persons.get_list(order_by=['age'], clause1=meta.OR(age__lte=30, age__gte=60),
     64...     clause2=meta.OR(fname__startswith='B', lname__startswith='W'))
     65[Betty Jones (30), James White (65), Susan White (70)]
     66"""
Back to Top