Index: django/db/backends/mysql/base.py
===================================================================
--- django/db/backends/mysql/base.py	(revision 5766)
+++ django/db/backends/mysql/base.py	(working copy)
@@ -177,9 +177,16 @@
 def get_datetime_cast_sql():
     return None
 
-def get_limit_offset_sql(limit, offset=None):
+def get_limit_offset_sql(limit=None, offset=None):
+    if not limit and not offset:
+        return ''
     sql = "LIMIT "
-    if offset and offset != 0:
+    if not limit:
+        # Per mySQL manual:
+        # To retrieve all rows from a certain offset up to the end of the result
+        # set, you can use some large number for the second parameter.
+        limit = 18446744073709551615
+    if offset:
         sql += "%s," % offset
     return sql + str(limit)
 
Index: django/db/backends/mysql_old/base.py
===================================================================
--- django/db/backends/mysql_old/base.py	(revision 5766)
+++ django/db/backends/mysql_old/base.py	(working copy)
@@ -192,9 +192,16 @@
 def get_datetime_cast_sql():
     return None
 
-def get_limit_offset_sql(limit, offset=None):
+def get_limit_offset_sql(limit=None, offset=None):
+    if not limit and not offset:
+        return ''
     sql = "LIMIT "
-    if offset and offset != 0:
+    if not limit:
+        # Per mySQL manual:
+        # To retrieve all rows from a certain offset up to the end of the result
+        # set, you can use some large number for the second parameter.
+        limit = 18446744073709551615
+    if offset:
         sql += "%s," % offset
     return sql + str(limit)
 
Index: django/db/backends/postgresql/base.py
===================================================================
--- django/db/backends/postgresql/base.py	(revision 5766)
+++ django/db/backends/postgresql/base.py	(working copy)
@@ -158,9 +158,12 @@
 def get_datetime_cast_sql():
     return None
 
-def get_limit_offset_sql(limit, offset=None):
-    sql = "LIMIT %s" % limit
-    if offset and offset != 0:
+def get_limit_offset_sql(limit=None, offset=None):
+    if not limit and not offset:
+        return ''
+    # From PostgreSQL manual: "LIMIT { count | ALL } OFFSET start"
+    sql = "LIMIT %s" % (limit or 'ALL')
+    if offset:
         sql += " OFFSET %s" % offset
     return sql
 
Index: django/db/backends/postgresql_psycopg2/base.py
===================================================================
--- django/db/backends/postgresql_psycopg2/base.py	(revision 5766)
+++ django/db/backends/postgresql_psycopg2/base.py	(working copy)
@@ -112,9 +112,11 @@
 def get_datetime_cast_sql():
     return None
 
-def get_limit_offset_sql(limit, offset=None):
-    sql = "LIMIT %s" % limit
-    if offset and offset != 0:
+def get_limit_offset_sql(limit=None, offset=None):
+    if not limit and not offset:
+        return ''
+    sql = "LIMIT %s" % (limit or 'ALL')
+    if offset:
         sql += " OFFSET %s" % offset
     return sql
 
Index: django/db/backends/sqlite3/base.py
===================================================================
--- django/db/backends/sqlite3/base.py	(revision 5766)
+++ django/db/backends/sqlite3/base.py	(working copy)
@@ -142,8 +142,11 @@
 def get_datetime_cast_sql():
     return None
 
-def get_limit_offset_sql(limit, offset=None):
-    sql = "LIMIT %s" % limit
+def get_limit_offset_sql(limit=None, offset=None):
+    if not limit and not offset:
+        return ''
+    # From sqlite manual: "A negative LIMIT indicates no upper bound."
+    sql = "LIMIT %s" % (limit or -1)
     if offset and offset != 0:
         sql += " OFFSET %s" % offset
     return sql
Index: django/db/models/query.py
===================================================================
--- django/db/models/query.py	(revision 5766)
+++ django/db/models/query.py	(working copy)
@@ -15,6 +15,11 @@
 except NameError:
     from sets import Set as set   # Python 2.3 fallback
 
+try:
+    reversed
+except NameError:
+    from django.utils.itercompat import reversed   # Python 2.3 fallback 
+
 # The string constant used to separate query parts
 LOOKUP_SEPARATOR = '__'
 
@@ -82,6 +87,26 @@
     else:
         return backend.quote_name(word)
 
+def negative_slice(k):
+    return (k.start and k.start < 0) or (k.stop and k.stop < 0)
+
+def invert_slice(k):
+    """
+    Reverse the slice, inverting the negation and swapping the start and stop
+    slice positions.
+    """
+    new_start = k.stop and -k.stop or None
+    if k.stop == 0:
+        # Edge case of [-x:0] should remain empty.
+        new_stop = 0
+    else:
+        new_stop = k.start and -k.start or None
+    return slice(new_start, new_stop)
+
+def invert_item(k):
+    "Reverse the __getitem__ integer item position."
+    return -k - 1
+
 class _QuerySet(object):
     "Represents a lazy database lookup for a set of objects"
     def __init__(self, model=None):
@@ -97,6 +122,7 @@
         self._tables = []            # List of extra tables to use.
         self._offset = None          # OFFSET clause.
         self._limit = None           # LIMIT clause.
+        self._reversed = None        # Reverse the db results (used for negative slicing)
         self._result_cache = None
 
     ########################
@@ -116,11 +142,28 @@
         "Retrieve an item or slice from the set of results."
         if not isinstance(k, (slice, int)):
             raise TypeError
-        assert (not isinstance(k, slice) and (k >= 0)) \
-            or (isinstance(k, slice) and (k.start is None or k.start >= 0) and (k.stop is None or k.stop >= 0)), \
-            "Negative indexing is not supported."
+        if isinstance(k, slice):
+            if k.start and k.stop:
+                assert not ((k.start < 0) ^ (k.stop < 0)), \
+                    "Slicing with both positive and negative indexes is not supported."
+            if k.start and k.start < 0 or k.stop and k.stop < 0:
+                assert not k.step or k.step == 1, \
+                    "Slicing with a negative index and a step is not supported."
         if self._result_cache is None:
+            extra_clone_args = {}
             if isinstance(k, slice):
+                if self._reversed:
+                    # Because we will be dealing with a reversed QuerySet, the
+                    # slice should be inverted.
+                    if not negative_slice(k):
+                        # Can't negative slice an already reversed QuerySet, so
+                        # just slice it as a list. Note, we haven't done the
+                        # inversion yet, hence reverse logic in the condition.
+                        return self._get_data()[k]
+                    k = invert_slice(k) 
+                elif negative_slice(k):
+                    extra_clone_args['_reversed'] = True
+                    k = invert_slice(k)
                 # Offset:
                 if self._offset is None:
                     offset = k.start
@@ -137,7 +180,7 @@
                 # Limit:
                 if k.stop is not None and k.start is not None:
                     if limit is None:
-                        limit = k.stop - k.start
+                        limit = max(k.stop - k.start, 0)
                     else:
                         limit = min((k.stop - k.start), limit)
                 else:
@@ -147,13 +190,32 @@
                         if k.stop is not None:
                             limit = min(k.stop, limit)
 
-                if k.step is None:
-                    return self._clone(_offset=offset, _limit=limit)
+                if k.step is None or k.step == 1:
+                    return self._clone(_offset=offset, _limit=limit,
+                                       **extra_clone_args)
                 else:
-                    return list(self._clone(_offset=offset, _limit=limit))[::k.step]
+                    return list(self._clone(_offset=offset, _limit=limit,
+                                            **extra_clone_args))[::k.step]
             else:
+                if self._reversed:
+                    # Because we will be dealing with a reversed QuerySet, the
+                    # item position should be inverted.
+                    if k >= 0:
+                        # Can't negate already offset reverse slice via the
+                        # database way, just have to get it as a list. Note, we
+                        # haven't done the inversion yet, hence reverse logic in
+                        # the condition.
+                        return self._get_data()[k]
+                    # Reversed queryset lookups need to take the current offset
+                    # into consideration.
+                    k += self._offset
+                    k = invert_item(k)
+                elif k < 0:
+                    k = invert_item(k)
+                    extra_clone_args['_reversed'] = True
                 try:
-                    return list(self._clone(_offset=k, _limit=1))[0]
+                    return list(self._clone(_offset=k, _limit=1,
+                                            **extra_clone_args))[0]
                 except self.model.DoesNotExist, e:
                     raise IndexError, e.args
         else:
@@ -174,11 +236,19 @@
     ####################################
 
     def iterator(self):
+        if self._reversed:
+            # Being a reverse slice, the order will be reversed from what the
+            # user expects.
+            return reversed(list(self._iterator()))
+        return self._iterator()
+    def _iterator(self):
         "Performs the SELECT database lookup of this QuerySet."
         try:
             select, sql, params = self._get_sql_clause()
         except EmptyResultSet:
             raise StopIteration
+        if self._limit == 0:
+            raise StopIteration
 
         # self._select is a dictionary, and dictionaries' key order is
         # undefined, so we convert it to a list of tuples.
@@ -452,6 +522,7 @@
         c._tables = self._tables[:]
         c._offset = self._offset
         c._limit = self._limit
+        c._reversed = self._reversed
         c.__dict__.update(kwargs)
         return c
 
@@ -525,11 +596,7 @@
 
         # ORDER BY clause
         order_by = []
-        if self._order_by is not None:
-            ordering_to_use = self._order_by
-        else:
-            ordering_to_use = opts.ordering
-        for f in handle_legacy_orderlist(ordering_to_use):
+        for f in handle_legacy_orderlist(self._get_ordering()):
             if f == '?': # Special case.
                 order_by.append(backend.get_random_function_sql())
             else:
@@ -554,13 +621,24 @@
             sql.append("ORDER BY " + ", ".join(order_by))
 
         # LIMIT and OFFSET clauses
-        if self._limit is not None:
+        if self._limit is not None or self._offset is not None:
             sql.append("%s " % backend.get_limit_offset_sql(self._limit, self._offset))
-        else:
-            assert self._offset is None, "'offset' is not allowed without 'limit'"
 
         return select, " ".join(sql), params
 
+    def _get_ordering(self):
+        if self._order_by is not None:
+            ordering = self._order_by
+        else:
+            ordering = self.model._meta.ordering
+        if not self._reversed:
+            return ordering
+        # Negative indexing requires inverting the ordering.
+        assert ordering, 'Negative indexing requires explicit ordering'
+        reverse_ordering = lambda f: (f.startswith('-') and f[1:]
+                                      or '-%s' % f)
+        return [reverse_ordering(f) for f in ordering]
+
 # Use the backend's QuerySet class if it defines one, otherwise use _QuerySet.
 if hasattr(backend, 'get_query_set_class'):
     QuerySet = backend.get_query_set_class(_QuerySet)
@@ -573,7 +651,7 @@
         # select_related isn't supported in values().
         self._select_related = False
 
-    def iterator(self):
+    def _iterator(self):
         try:
             select, sql, params = self._get_sql_clause()
         except EmptyResultSet:
@@ -621,7 +699,7 @@
         return c
 
 class DateQuerySet(QuerySet):
-    def iterator(self):
+    def _iterator(self):
         from django.db.backends.util import typecast_timestamp
         from django.db.models.fields import DateTimeField
         self._order_by = () # Clear this because it'll mess things up otherwise.
Index: docs/db-api.txt
===================================================================
--- docs/db-api.txt	(revision 5766)
+++ docs/db-api.txt	(working copy)
@@ -434,6 +434,17 @@
 Note, however, that the first of these will raise ``IndexError`` while the
 second will raise ``DoesNotExist`` if no objects match the given criteria.
 
+**New in Django development version**
+
+You can also use negative slicing and negative indexes, provided the
+``QuerySet``s has an explicit order (either via ``.order_by()`` or the model's
+meta ``ordering`` option). For example::
+
+    entries_by_date_order = Entry.objects.order_by('pub_date')
+    oldest_entry = entries_by_date_order[0]
+    latest_entry = entries_by_date_order[-1]
+    latest_three_entries = entries_by_date_order[:-3]
+
 QuerySet methods that return new QuerySets
 ------------------------------------------
 
Index: tests/modeltests/basic/models.py
===================================================================
--- tests/modeltests/basic/models.py	(revision 5766)
+++ tests/modeltests/basic/models.py	(working copy)
@@ -246,7 +246,10 @@
 >>> s3 = Article.objects.filter(id__exact=3)
 >>> (s1 | s2 | s3)[::2]
 [<Article: Area woman programs in Python>, <Article: Third article>]
+>>> Article.objects.all()[2:]
+[<Article: Third article>, <Article: Article 6>, <Article: Default headline>, <Article: Fourth article>, <Article: Article 7>, <Article: Updated article 8>]
 
+
 # Slices (without step) are lazy:
 >>> Article.objects.all()[0:5].filter()
 [<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Article 6>, <Article: Default headline>]
@@ -268,14 +271,26 @@
 [<Article: Third article>, <Article: Article 6>]
 >>> Article.objects.all()[2:][2:3]
 [<Article: Default headline>]
+>>> Article.objects.all()[-5:-2]
+[<Article: Article 6>, <Article: Default headline>, <Article: Fourth article>]
+>>> Article.objects.all()[-5:-2][1]
+<Article: Default headline>
+>>> Article.objects.all()[-5:-2][-1]
+<Article: Fourth article>
+>>> Article.objects.all()[-5:-2][1:]
+[<Article: Default headline>, <Article: Fourth article>]
+>>> Article.objects.all()[-5:-2][-2:]
+[<Article: Default headline>, <Article: Fourth article>]
+>>> Article.objects.all()[-5:-2][:2]
+[<Article: Article 6>, <Article: Default headline>]
+>>> Article.objects.all()[-5:-2][:-2]
+[<Article: Article 6>]
+>>> Article.objects.all()[-5:-2][:-10]
+[]
+>>> Article.objects.all()[-5:-2][-10:]
+[<Article: Article 6>, <Article: Default headline>, <Article: Fourth article>]
 
-# Note that you can't use 'offset' without 'limit' (on some dbs), so this doesn't work:
->>> Article.objects.all()[2:]
-Traceback (most recent call last):
-    ...
-AssertionError: 'offset' is not allowed without 'limit'
-
-# Also, once you have sliced you can't filter, re-order or combine
+# Once you have sliced you can't filter, re-order or combine
 >>> Article.objects.all()[0:5].filter(id=1)
 Traceback (most recent call last):
     ...
@@ -291,16 +306,32 @@
     ...
 AssertionError: Cannot combine queries once a slice has been taken.
 
-# Negative slices are not supported, due to database constraints.
-# (hint: inverting your ordering might do what you need).
+# Negative slices are possible, provided if the QuerySet has explicit ordering.
+# (done by internally inverting the ordering).
 >>> Article.objects.all()[-1]
+<Article: Updated article 8>
+>>> Article.objects.all()[0:-5]
+[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>]
+>>> Article.objects.all()[-2:]
+[<Article: Article 7>, <Article: Updated article 8>]
+>>> Article.objects.all()[:-7]
+[<Article: Area woman programs in Python>]
+>>> Article.objects.all()[-2:-1]
+[<Article: Article 7>]
+>>> Article.objects.all()[-2:0]
+[]
+>>> Article.objects.all()[-2:-3]
+[]
+
+# Can't mix negative and positive positions when slicing.
+>>> Article.objects.all()[1:-1]
 Traceback (most recent call last):
     ...
-AssertionError: Negative indexing is not supported.
->>> Article.objects.all()[0:-5]
+AssertionError: Slicing with both positive and negative indexes is not supported.
+>>> Article.objects.all()[-5:2]
 Traceback (most recent call last):
     ...
-AssertionError: Negative indexing is not supported.
+AssertionError: Slicing with both positive and negative indexes is not supported.
 
 # An Article instance doesn't have access to the "objects" attribute.
 # That's only available on the class.
