Django

Code

Changeset 2485

Show
Ignore:
Timestamp:
03/03/06 18:19:59 (3 years ago)
Author:
lukeplant
Message:

magic-removal: Implemented 'laziness' for QuerySet? slicing, to restore functionality that was previously possible with generic views via 'limit' and 'extra_lookup_args'

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/magic-removal/django/db/models/query.py

    r2457 r2485  
    9696    def __getitem__(self, k): 
    9797        "Retrieve an item or slice from the set of results." 
    98         # __getitem__ can't return QuerySet instances, because filter() and 
    99         # order_by() on the result would break badly. This means we don't have 
    100         # to worry about arithmetic with self._limit or self._offset -- they'll 
    101         # both be None at this point. 
    10298        if self._result_cache is None: 
    10399            if isinstance(k, slice): 
     100                # Offset: 
     101                if self._offset is None: 
     102                    offset = k.start 
     103                elif k.start is None: 
     104                    offset = self._offset 
     105                else: 
     106                    offset = self._offset + k.start 
     107                # Now adjust offset to the bounds of any existing limit: 
     108                if self._limit is not None and k.start is not None: 
     109                    limit = self._limit - k.start 
     110                else: 
     111                    limit = self._limit 
     112 
     113                # Limit: 
    104114                if k.stop is not None and k.start is not None: 
    105                     limit = k.stop - k.start 
     115                    if limit is None: 
     116                        limit = k.stop - k.start 
     117                    else: 
     118                        limit = min((k.stop - k.start), limit) 
    106119                else: 
    107                     limit = k.stop 
    108                 return list(self._clone(_offset=k.start, _limit=limit))[::k.step] 
     120                    if limit is None: 
     121                        limit = k.stop 
     122                    else: 
     123                        if k.stop is not None: 
     124                            limit = min(k.stop, limit) 
     125 
     126                if k.step is None: 
     127                    return self._clone(_offset=offset, _limit=limit) 
     128                else: 
     129                    return list(self._clone(_offset=offset, _limit=limit))[::k.step] 
    109130            else: 
    110131                return self._clone(_offset=k, _limit=1).get() 
     
    180201        latest_by = field_name or self.model._meta.get_latest_by 
    181202        assert bool(latest_by), "latest() requires either a field_name parameter or 'get_latest_by' in the model" 
     203        assert self._limit is None and self._offset is None, \ 
     204                "Cannot change a query once a slice has been taken." 
    182205        return self._clone(_limit=1, _order_by=('-'+latest_by,)).get() 
    183206 
     
    187210        that ID. 
    188211        """ 
     212        assert self._limit is None and self._offset is None, \ 
     213                "Cannot use 'limit' or 'offset' with in_bulk" 
    189214        assert isinstance(id_list, (tuple,  list)), "in_bulk() must be provided with a list of IDs." 
    190215        id_list = list(id_list) 
     
    199224        Deletes the records in the current QuerySet. 
    200225        """ 
     226        assert self._limit is None and self._offset is None, \ 
     227            "Cannot use 'limit' or 'offset' with delete." 
     228 
    201229        del_query = self._clone() 
    202230 
     
    204232        del_query._select_related = False 
    205233        del_query._order_by = [] 
    206         del_query._offset = None 
    207         del_query._limit = None 
    208234 
    209235        # Collect all the objects to be deleted, and all the objects that are related to 
     
    252278         
    253279    def _filter_or_exclude(self, qtype, *args, **kwargs): 
     280        if len(args) > 0 or len(kwargs) > 0: 
     281            assert self._limit is None and self._offset is None, \ 
     282                "Cannot filter a query once a slice has been taken." 
     283 
    254284        clone = self._clone() 
    255285        if len(kwargs) > 0: 
     
    265295    def order_by(self, *field_names): 
    266296        "Returns a new QuerySet instance with the ordering changed." 
     297        assert self._limit is None and self._offset is None, \ 
     298                "Cannot reorder a query once a slice has been taken." 
    267299        return self._clone(_order_by=field_names) 
    268300 
     
    272304 
    273305    def extra(self, select=None, where=None, params=None, tables=None): 
     306        assert self._limit is None and self._offset is None, \ 
     307                "Cannot change a query once a slice has been taken" 
    274308        clone = self._clone() 
    275309        if select: clone._select.extend(select) 
     
    302336 
    303337    def _combine(self, other): 
    304         if self._distinct != other._distinct: 
    305             raise ValueException, "Can't combine a unique query with a non-unique query" 
     338        assert self._limit is None and self._offset is None \ 
     339            and other._limit is None and other._offset is None, \ 
     340            "Cannot combine queries once a slice has been taken." 
     341        assert self._distinct == other._distinct, \ 
     342            "Cannot combine a unique query with a non-unique query" 
    306343        #  use 'other's order by 
    307344        #  (so that A.filter(args1) & A.filter(args2) does the same as 
  • django/branches/magic-removal/tests/modeltests/basic/models.py

    r2480 r2485  
    240240[Area woman programs in Python, Third article] 
    241241 
     242# Slices (without step) are lazy: 
     243>>> Article.objects.all()[0:5].filter() 
     244[Area woman programs in Python, Second article, Third article, Fourth article, Article 6] 
     245 
     246# Slicing again works: 
     247>>> Article.objects.all()[0:5][0:2] 
     248[Area woman programs in Python, Second article] 
     249>>> Article.objects.all()[0:5][:2] 
     250[Area woman programs in Python, Second article] 
     251>>> Article.objects.all()[0:5][4:] 
     252[Article 6] 
     253>>> Article.objects.all()[0:5][5:] 
     254[] 
     255 
     256# Some more tests! 
     257>>> Article.objects.all()[2:][0:2] 
     258[Third article, Fourth article] 
     259>>> Article.objects.all()[2:][:2] 
     260[Third article, Fourth article] 
     261>>> Article.objects.all()[2:][2:3] 
     262[Article 6] 
     263 
     264# Note that you can't use 'offset' without 'limit' (on some dbs), so this doesn't work: 
     265>>> Article.objects.all()[2:] 
     266Traceback (most recent call last): 
     267    ... 
     268AssertionError: 'offset' is not allowed without 'limit' 
     269 
     270# Also, once you have sliced you can't filter, re-order or combine 
     271>>> Article.objects.all()[0:5].filter(id=1) 
     272Traceback (most recent call last): 
     273    ... 
     274AssertionError: Cannot filter a query once a slice has been taken. 
     275 
     276>>> Article.objects.all()[0:5].order_by('id') 
     277Traceback (most recent call last): 
     278    ... 
     279AssertionError: Cannot reorder a query once a slice has been taken. 
     280 
     281>>> Article.objects.all()[0:1] & Article.objects.all()[4:5] 
     282Traceback (most recent call last): 
     283    ... 
     284AssertionError: Cannot combine queries once a slice has been taken. 
     285 
     286 
    242287# An Article instance doesn't have access to the "objects" attribute. 
    243288# That's only available on the class.