diff --git a/tests/basic/tests.py b/tests/basic/tests.py index a8e4df32d6d..ae1e799bc49 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -6,7 +6,7 @@ import threading from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.db import connections, DEFAULT_DB_ALIAS from django.db import DatabaseError -from django.db.models.fields import Field, FieldDoesNotExist +from django.db.models.fields import Field from django.db.models.manager import BaseManager from django.db.models.query import QuerySet, EmptyQuerySet, ValuesListQuerySet, MAX_GET_RESULTS from django.test import TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature @@ -16,7 +16,167 @@ from django.utils.translation import ugettext_lazy from .models import Article, SelfRef, ArticleSelectOnSave +class ModelInstanceCreationTests(TestCase): + + def test_object_is_not_written_to_database_until_save_was_called(self): + a = Article( + id=None, + headline='Area man programs in Python', + pub_date=datetime(2005, 7, 28), + ) + self.assertIsNone(a.id) + self.assertEquals(Article.objects.all().count(), 0) + + # Save it into the database. You have to call save() explicitly. + a.save() + self.assertIsNotNone(a.id) + self.assertEquals(Article.objects.all().count(), 1) + + def test_can_initialize_model_instance_using_positional_arguments(self): + """ + You can initialize a model instance using positional arguments, + which should match the field order as defined in the model. + """ + a = Article(None, 'Second article', datetime(2005, 7, 29)) + a.save() + + self.assertEqual(a.headline, 'Second article') + self.assertEqual(a.pub_date, datetime(2005, 7, 29, 0, 0)) + + def test_can_create_instance_using_kwargs(self): + a = Article( + id=None, + headline='Third article', + pub_date=datetime(2005, 7, 30), + ) + a.save() + self.assertEqual(a.headline, 'Third article') + self.assertEqual(a.pub_date, datetime(2005, 7, 30, 0, 0)) + + def test_autofields_generate_different_values_for_each_instance(self): + a1 = Article.objects.create(headline='First', pub_date=datetime(2005, 7, 30, 0, 0)) + a2 = Article.objects.create(headline='First', pub_date=datetime(2005, 7, 30, 0, 0)) + a3 = Article.objects.create(headline='First', pub_date=datetime(2005, 7, 30, 0, 0)) + self.assertNotEqual(a3.id, a1.id) + self.assertNotEqual(a3.id, a2.id) + + def test_can_mix_and_match_position_and_kwargs(self): + # You can also mix and match position and keyword arguments, but + # be sure not to duplicate field information. + a = Article(None, 'Fourth article', pub_date=datetime(2005, 7, 31)) + a.save() + self.assertEqual(a.headline, 'Fourth article') + + def test_cannot_create_instance_with_invalid_kwargs(self): + six.assertRaisesRegex( + self, + TypeError, + "'foo' is an invalid keyword argument for this function", + Article, + id=None, + headline='Some headline', + pub_date=datetime(2005, 7, 31), + foo='bar', + ) + + def test_can_leave_off_value_for_autofield_and_it_gets_value_on_save(self): + """ + You can leave off the value for an AutoField when creating an + object, because it'll get filled in automatically when you save(). + """ + a = Article(headline='Article 5', pub_date=datetime(2005, 7, 31)) + a.save() + self.assertEqual(a.headline, 'Article 5') + self.assertNotEqual(a.id, None) + + def test_leaving_off_a_field_with_default_set_the_default_will_be_saved(self): + a = Article(pub_date=datetime(2005, 7, 31)) + a.save() + self.assertEqual(a.headline, 'Default headline') + + def test_for_datetimefields_saves_as_much_precision_as_was_given(self): + """as much precision in *seconds*""" + a1 = Article( + headline='Article 7', + pub_date=datetime(2005, 7, 31, 12, 30), + ) + a1.save() + self.assertEqual(Article.objects.get(id__exact=a1.id).pub_date, + datetime(2005, 7, 31, 12, 30)) + + a2 = Article( + headline='Article 8', + pub_date=datetime(2005, 7, 31, 12, 30, 45), + ) + a2.save() + self.assertEqual(Article.objects.get(id__exact=a2.id).pub_date, + datetime(2005, 7, 31, 12, 30, 45)) + + def test_saving_an_object_again_does_not_create_a_new_object(self): + a = Article(headline='original', pub_date=datetime(2014, 5, 16)) + a.save() + current_id = a.id + + a.save() + self.assertEqual(a.id, current_id) + + a.headline = 'Updated headline' + a.save() + self.assertEqual(a.id, current_id) + + def test_querysets_checking_for_membership(self): + headlines = [ + 'Area man programs in Python', 'Second article', 'Third article'] + some_pub_date = datetime(2014, 5, 16, 12, 1) + for headline in headlines: + Article(headline=headline, pub_date=some_pub_date).save() + a = Article(headline='Some headline', pub_date=some_pub_date) + a.save() + + # You can use 'in' to test for membership... + self.assertTrue(a in Article.objects.all()) + # ... but there will often be more efficient ways if that is all you need: + self.assertTrue(Article.objects.filter(id=a.id).exists()) + + class ModelTest(TestCase): + def test_objects_attribute_is_only_available_on_the_class_itself(self): + six.assertRaisesRegex( + self, + AttributeError, + "Manager isn't accessible via Article instances", + getattr, + Article(), + "objects", + ) + self.assertFalse(hasattr(Article(), 'objects')) + self.assertTrue(hasattr(Article, 'objects')) + + def test_queryset_delete_removes_all_items_in_that_queryset(self): + headlines = [ + 'An article', 'Article One', 'Amazing article', 'Boring article'] + some_pub_date = datetime(2014, 5, 16, 12, 1) + for headline in headlines: + Article(headline=headline, pub_date=some_pub_date).save() + self.assertQuerysetEqual(Article.objects.all().order_by('headline'), + ["", + "", + "", + ""]) + Article.objects.filter(headline__startswith='A').delete() + self.assertQuerysetEqual(Article.objects.all().order_by('headline'), + [""]) + + def test_not_equal_and_equal_operators_behave_as_expected_on_instances(self): + some_pub_date = datetime(2014, 5, 16, 12, 1) + a1 = Article.objects.create(headline='First', pub_date=some_pub_date) + a2 = Article.objects.create(headline='Second', pub_date=some_pub_date) + self.assertTrue(a1 != a2) + self.assertFalse(a1 == a2) + self.assertTrue(a1 == Article.objects.get(id__exact=a1.id)) + + self.assertTrue(Article.objects.get(id__exact=a1.id) != Article.objects.get(id__exact=a2.id)) + self.assertFalse(Article.objects.get(id__exact=a2.id) == Article.objects.get(id__exact=a1.id)) def test_lookup(self): # No articles are in the system yet. @@ -186,320 +346,6 @@ class ModelTest(TestCase): headline__startswith='Area', ) - def test_object_creation(self): - # Create an Article. - a = Article( - id=None, - headline='Area man programs in Python', - pub_date=datetime(2005, 7, 28), - ) - - # Save it into the database. You have to call save() explicitly. - a.save() - - # You can initialize a model instance using positional arguments, - # which should match the field order as defined in the model. - a2 = Article(None, 'Second article', datetime(2005, 7, 29)) - a2.save() - - self.assertNotEqual(a2.id, a.id) - self.assertEqual(a2.headline, 'Second article') - self.assertEqual(a2.pub_date, datetime(2005, 7, 29, 0, 0)) - - # ...or, you can use keyword arguments. - a3 = Article( - id=None, - headline='Third article', - pub_date=datetime(2005, 7, 30), - ) - a3.save() - - self.assertNotEqual(a3.id, a.id) - self.assertNotEqual(a3.id, a2.id) - self.assertEqual(a3.headline, 'Third article') - self.assertEqual(a3.pub_date, datetime(2005, 7, 30, 0, 0)) - - # You can also mix and match position and keyword arguments, but - # be sure not to duplicate field information. - a4 = Article(None, 'Fourth article', pub_date=datetime(2005, 7, 31)) - a4.save() - self.assertEqual(a4.headline, 'Fourth article') - - # Don't use invalid keyword arguments. - six.assertRaisesRegex( - self, - TypeError, - "'foo' is an invalid keyword argument for this function", - Article, - id=None, - headline='Invalid', - pub_date=datetime(2005, 7, 31), - foo='bar', - ) - - # You can leave off the value for an AutoField when creating an - # object, because it'll get filled in automatically when you save(). - a5 = Article(headline='Article 6', pub_date=datetime(2005, 7, 31)) - a5.save() - self.assertEqual(a5.headline, 'Article 6') - - # If you leave off a field with "default" set, Django will use - # the default. - a6 = Article(pub_date=datetime(2005, 7, 31)) - a6.save() - self.assertEqual(a6.headline, 'Default headline') - - # For DateTimeFields, Django saves as much precision (in seconds) - # as you give it. - a7 = Article( - headline='Article 7', - pub_date=datetime(2005, 7, 31, 12, 30), - ) - a7.save() - self.assertEqual(Article.objects.get(id__exact=a7.id).pub_date, - datetime(2005, 7, 31, 12, 30)) - - a8 = Article( - headline='Article 8', - pub_date=datetime(2005, 7, 31, 12, 30, 45), - ) - a8.save() - self.assertEqual(Article.objects.get(id__exact=a8.id).pub_date, - datetime(2005, 7, 31, 12, 30, 45)) - - # Saving an object again doesn't create a new object -- it just saves - # the old one. - current_id = a8.id - a8.save() - self.assertEqual(a8.id, current_id) - a8.headline = 'Updated article 8' - a8.save() - self.assertEqual(a8.id, current_id) - - # Check that != and == operators behave as expecte on instances - self.assertTrue(a7 != a8) - self.assertFalse(a7 == a8) - self.assertEqual(a8, Article.objects.get(id__exact=a8.id)) - - self.assertTrue(Article.objects.get(id__exact=a8.id) != Article.objects.get(id__exact=a7.id)) - self.assertFalse(Article.objects.get(id__exact=a8.id) == Article.objects.get(id__exact=a7.id)) - - # You can use 'in' to test for membership... - self.assertTrue(a8 in Article.objects.all()) - - # ... but there will often be more efficient ways if that is all you need: - self.assertTrue(Article.objects.filter(id=a8.id).exists()) - - # datetimes() returns a list of available dates of the given scope for - # the given field. - self.assertQuerysetEqual( - Article.objects.datetimes('pub_date', 'year'), - ["datetime.datetime(2005, 1, 1, 0, 0)"]) - self.assertQuerysetEqual( - Article.objects.datetimes('pub_date', 'month'), - ["datetime.datetime(2005, 7, 1, 0, 0)"]) - self.assertQuerysetEqual( - Article.objects.datetimes('pub_date', 'day'), - ["datetime.datetime(2005, 7, 28, 0, 0)", - "datetime.datetime(2005, 7, 29, 0, 0)", - "datetime.datetime(2005, 7, 30, 0, 0)", - "datetime.datetime(2005, 7, 31, 0, 0)"]) - self.assertQuerysetEqual( - Article.objects.datetimes('pub_date', 'day', order='ASC'), - ["datetime.datetime(2005, 7, 28, 0, 0)", - "datetime.datetime(2005, 7, 29, 0, 0)", - "datetime.datetime(2005, 7, 30, 0, 0)", - "datetime.datetime(2005, 7, 31, 0, 0)"]) - self.assertQuerysetEqual( - Article.objects.datetimes('pub_date', 'day', order='DESC'), - ["datetime.datetime(2005, 7, 31, 0, 0)", - "datetime.datetime(2005, 7, 30, 0, 0)", - "datetime.datetime(2005, 7, 29, 0, 0)", - "datetime.datetime(2005, 7, 28, 0, 0)"]) - - # datetimes() requires valid arguments. - self.assertRaises( - TypeError, - Article.objects.dates, - ) - - six.assertRaisesRegex( - self, - FieldDoesNotExist, - "Article has no field named 'invalid_field'", - Article.objects.dates, - "invalid_field", - "year", - ) - - six.assertRaisesRegex( - self, - AssertionError, - "'kind' must be one of 'year', 'month' or 'day'.", - Article.objects.dates, - "pub_date", - "bad_kind", - ) - - six.assertRaisesRegex( - self, - AssertionError, - "'order' must be either 'ASC' or 'DESC'.", - Article.objects.dates, - "pub_date", - "year", - order="bad order", - ) - - # Use iterator() with datetimes() to return a generator that lazily - # requests each result one at a time, to save memory. - dates = [] - for article in Article.objects.datetimes('pub_date', 'day', order='DESC').iterator(): - dates.append(article) - self.assertEqual(dates, [ - datetime(2005, 7, 31, 0, 0), - datetime(2005, 7, 30, 0, 0), - datetime(2005, 7, 29, 0, 0), - datetime(2005, 7, 28, 0, 0)]) - - # You can combine queries with & and |. - s1 = Article.objects.filter(id__exact=a.id) - s2 = Article.objects.filter(id__exact=a2.id) - self.assertQuerysetEqual(s1 | s2, - ["", - ""]) - self.assertQuerysetEqual(s1 & s2, []) - - # You can get the number of objects like this: - self.assertEqual(len(Article.objects.filter(id__exact=a.id)), 1) - - # You can get items using index and slice notation. - self.assertEqual(Article.objects.all()[0], a) - self.assertQuerysetEqual(Article.objects.all()[1:3], - ["", ""]) - - s3 = Article.objects.filter(id__exact=a3.id) - self.assertQuerysetEqual((s1 | s2 | s3)[::2], - ["", - ""]) - - # Slicing works with longs (Python 2 only -- Python 3 doesn't have longs). - if six.PY2: - self.assertEqual(Article.objects.all()[long(0)], a) - self.assertQuerysetEqual(Article.objects.all()[long(1):long(3)], - ["", ""]) - self.assertQuerysetEqual((s1 | s2 | s3)[::long(2)], - ["", - ""]) - - # And can be mixed with ints. - self.assertQuerysetEqual(Article.objects.all()[1:long(3)], - ["", ""]) - - # Slices (without step) are lazy: - self.assertQuerysetEqual(Article.objects.all()[0:5].filter(), - ["", - "", - "", - "", - ""]) - - # Slicing again works: - self.assertQuerysetEqual(Article.objects.all()[0:5][0:2], - ["", - ""]) - self.assertQuerysetEqual(Article.objects.all()[0:5][:2], - ["", - ""]) - self.assertQuerysetEqual(Article.objects.all()[0:5][4:], - [""]) - self.assertQuerysetEqual(Article.objects.all()[0:5][5:], []) - - # Some more tests! - self.assertQuerysetEqual(Article.objects.all()[2:][0:2], - ["", ""]) - self.assertQuerysetEqual(Article.objects.all()[2:][:2], - ["", ""]) - self.assertQuerysetEqual(Article.objects.all()[2:][2:3], - [""]) - - # Using an offset without a limit is also possible. - self.assertQuerysetEqual(Article.objects.all()[5:], - ["", - "", - ""]) - - # Also, once you have sliced you can't filter, re-order or combine - six.assertRaisesRegex( - self, - AssertionError, - "Cannot filter a query once a slice has been taken.", - Article.objects.all()[0:5].filter, - id=a.id, - ) - - six.assertRaisesRegex( - self, - AssertionError, - "Cannot reorder a query once a slice has been taken.", - Article.objects.all()[0:5].order_by, - 'id', - ) - - try: - Article.objects.all()[0:1] & Article.objects.all()[4:5] - self.fail('Should raise an AssertionError') - except AssertionError as e: - self.assertEqual(str(e), "Cannot combine queries once a slice has been taken.") - except Exception as e: - self.fail('Should raise an AssertionError, not %s' % e) - - # Negative slices are not supported, due to database constraints. - # (hint: inverting your ordering might do what you need). - try: - Article.objects.all()[-1] - self.fail('Should raise an AssertionError') - except AssertionError as e: - self.assertEqual(str(e), "Negative indexing is not supported.") - except Exception as e: - self.fail('Should raise an AssertionError, not %s' % e) - - error = None - try: - Article.objects.all()[0:-5] - except Exception as e: - error = e - self.assertIsInstance(error, AssertionError) - self.assertEqual(str(error), "Negative indexing is not supported.") - - # An Article instance doesn't have access to the "objects" attribute. - # That's only available on the class. - six.assertRaisesRegex( - self, - AttributeError, - "Manager isn't accessible via Article instances", - getattr, - a7, - "objects", - ) - - # Bulk delete test: How many objects before and after the delete? - self.assertQuerysetEqual(Article.objects.all(), - ["", - "", - "", - "", - "", - "", - "", - ""]) - Article.objects.filter(id__lte=a4.id).delete() - self.assertQuerysetEqual(Article.objects.all(), - ["", - "", - "", - ""]) - @skipUnlessDBFeature('supports_microsecond_precision') def test_microsecond_precision(self): # In PostgreSQL, microsecond-level precision is available. diff --git a/tests/dates/tests.py b/tests/dates/tests.py index c2a8d82e37f..2177ad046e9 100644 --- a/tests/dates/tests.py +++ b/tests/dates/tests.py @@ -2,7 +2,9 @@ from __future__ import unicode_literals import datetime +from django.db.models.fields import FieldDoesNotExist from django.test import TestCase +from django.utils import six from .models import Article, Comment, Category @@ -81,3 +83,40 @@ class DatesTests(TestCase): ], lambda d: d, ) + + def test_dates_fails_when_no_arguments_are_provided(self): + self.assertRaises( + TypeError, + Article.objects.dates, + ) + + def test_dates_fails_when_given_invalid_field_argument(self): + six.assertRaisesRegex( + self, + FieldDoesNotExist, + "Article has no field named 'invalid_field'", + Article.objects.dates, + "invalid_field", + "year", + ) + + def test_dates_fails_when_given_invalid_kind_argument(self): + six.assertRaisesRegex( + self, + AssertionError, + "'kind' must be one of 'year', 'month' or 'day'.", + Article.objects.dates, + "pub_date", + "bad_kind", + ) + + def test_dates_fails_when_given_invalid_order_argument(self): + six.assertRaisesRegex( + self, + AssertionError, + "'order' must be either 'ASC' or 'DESC'.", + Article.objects.dates, + "pub_date", + "year", + order="bad order", + ) diff --git a/tests/datetimes/tests.py b/tests/datetimes/tests.py index ee0b6766645..776f6ecc336 100644 --- a/tests/datetimes/tests.py +++ b/tests/datetimes/tests.py @@ -97,3 +97,60 @@ class DateTimesTests(TestCase): Article.objects.create(title="First one", pub_date=now) qs = Article.objects.datetimes('pub_date', 'second') self.assertEqual(qs[0], now) + + def test_datetimes_returns_available_dates_for_given_scope_and_given_field(self): + pub_dates = [ + datetime.datetime(2005, 7, 28, 12, 15), + datetime.datetime(2005, 7, 29, 2, 15), + datetime.datetime(2005, 7, 30, 5, 15), + datetime.datetime(2005, 7, 31, 19, 15)] + for i, pub_date in enumerate(pub_dates): + Article(pub_date=pub_date, title='title #{}'.format(i)).save() + + self.assertQuerysetEqual( + Article.objects.datetimes('pub_date', 'year'), + ["datetime.datetime(2005, 1, 1, 0, 0)"]) + self.assertQuerysetEqual( + Article.objects.datetimes('pub_date', 'month'), + ["datetime.datetime(2005, 7, 1, 0, 0)"]) + self.assertQuerysetEqual( + Article.objects.datetimes('pub_date', 'day'), + ["datetime.datetime(2005, 7, 28, 0, 0)", + "datetime.datetime(2005, 7, 29, 0, 0)", + "datetime.datetime(2005, 7, 30, 0, 0)", + "datetime.datetime(2005, 7, 31, 0, 0)"]) + self.assertQuerysetEqual( + Article.objects.datetimes('pub_date', 'day', order='ASC'), + ["datetime.datetime(2005, 7, 28, 0, 0)", + "datetime.datetime(2005, 7, 29, 0, 0)", + "datetime.datetime(2005, 7, 30, 0, 0)", + "datetime.datetime(2005, 7, 31, 0, 0)"]) + self.assertQuerysetEqual( + Article.objects.datetimes('pub_date', 'day', order='DESC'), + ["datetime.datetime(2005, 7, 31, 0, 0)", + "datetime.datetime(2005, 7, 30, 0, 0)", + "datetime.datetime(2005, 7, 29, 0, 0)", + "datetime.datetime(2005, 7, 28, 0, 0)"]) + + def test_datetimes_has_lazy_iterator(self): + pub_dates = [ + datetime.datetime(2005, 7, 28, 12, 15), + datetime.datetime(2005, 7, 29, 2, 15), + datetime.datetime(2005, 7, 30, 5, 15), + datetime.datetime(2005, 7, 31, 19, 15)] + for i, pub_date in enumerate(pub_dates): + Article(pub_date=pub_date, title='title #{}'.format(i)).save() + # Use iterator() with datetimes() to return a generator that lazily + # requests each result one at a time, to save memory. + dates = [] + with self.assertNumQueries(0): + article_datetimes_iterator = Article.objects.datetimes('pub_date', 'day', order='DESC').iterator() + + with self.assertNumQueries(1): + for article in article_datetimes_iterator: + dates.append(article) + self.assertEqual(dates, [ + datetime.datetime(2005, 7, 31, 0, 0), + datetime.datetime(2005, 7, 30, 0, 0), + datetime.datetime(2005, 7, 29, 0, 0), + datetime.datetime(2005, 7, 28, 0, 0)]) diff --git a/tests/queries/models.py b/tests/queries/models.py index 604eed5f07c..0dd4833b536 100644 --- a/tests/queries/models.py +++ b/tests/queries/models.py @@ -364,10 +364,14 @@ class Plaything(models.Model): return self.name +@python_2_unicode_compatible class Article(models.Model): name = models.CharField(max_length=20) created = models.DateTimeField() + def __str__(self): + return self.name + @python_2_unicode_compatible class Food(models.Model): diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 751f9792b1f..f6e8d79309e 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -2145,6 +2145,129 @@ class ValuesQuerysetTests(BaseQuerysetTest): self.assertQuerysetEqual(qs, [72], self.identity) +class QuerySetSupportsPythonIdioms(TestCase): + + def setUp(self): + some_date = datetime.datetime(2014, 5, 16, 12, 1) + for i in range(1, 8): + Article.objects.create( + name="Article {}".format(i), created=some_date) + + def get_ordered_articles(self): + return Article.objects.all().order_by('name') + + def test_can_get_items_using_index_and_slice_notation(self): + self.assertEqual(self.get_ordered_articles()[0].name, 'Article 1') + self.assertQuerysetEqual(self.get_ordered_articles()[1:3], + ["", ""]) + + def test_slicing_with_steps_can_be_used(self): + self.assertQuerysetEqual(self.get_ordered_articles()[::2], + ["", + "", + "", + ""]) + + @unittest.skipUnless(six.PY2, "Python 2 only -- Python 3 doesn't have longs.") + def test_slicing_works_with_longs(self): + self.assertEqual(self.get_ordered_articles()[long(0)].name, 'Article 1') + self.assertQuerysetEqual(self.get_ordered_articles()[long(1):long(3)], + ["", ""]) + self.assertQuerysetEqual(self.get_ordered_articles()[::long(2)], + ["", + "", + "", + ""]) + + # And can be mixed with ints. + self.assertQuerysetEqual(self.get_ordered_articles()[1:long(3)], + ["", ""]) + + def test_slicing_without_step_is_lazy(self): + with self.assertNumQueries(0): + self.get_ordered_articles()[0:5] + + def test_slicing_with_tests_is_not_lazy(self): + with self.assertNumQueries(1): + self.get_ordered_articles()[0:5:3] + + def test_slicing_can_slice_again_after_slicing(self): + self.assertQuerysetEqual(self.get_ordered_articles()[0:5][0:2], + ["", + ""]) + self.assertQuerysetEqual(self.get_ordered_articles()[0:5][4:], + [""]) + self.assertQuerysetEqual(self.get_ordered_articles()[0:5][5:], []) + + # Some more tests! + self.assertQuerysetEqual(self.get_ordered_articles()[2:][0:2], + ["", ""]) + self.assertQuerysetEqual(self.get_ordered_articles()[2:][:2], + ["", ""]) + self.assertQuerysetEqual(self.get_ordered_articles()[2:][2:3], + [""]) + + # Using an offset without a limit is also possible. + self.assertQuerysetEqual(self.get_ordered_articles()[5:], + ["", + ""]) + + def test_slicing_cannot_filter_queryset_once_sliced(self): + six.assertRaisesRegex( + self, + AssertionError, + "Cannot filter a query once a slice has been taken.", + Article.objects.all()[0:5].filter, + id=1, + ) + + def test_slicing_cannot_reorder_queryset_once_sliced(self): + six.assertRaisesRegex( + self, + AssertionError, + "Cannot reorder a query once a slice has been taken.", + Article.objects.all()[0:5].order_by, + 'id', + ) + + def test_slicing_cannot_combine_queries_once_sliced(self): + six.assertRaisesRegex( + self, + AssertionError, + "Cannot combine queries once a slice has been taken.", + lambda: Article.objects.all()[0:1] & Article.objects.all()[4:5] + ) + + def test_slicing_negative_indexing_not_supported_for_single_element(self): + """hint: inverting your ordering might do what you need""" + six.assertRaisesRegex( + self, + AssertionError, + "Negative indexing is not supported.", + lambda: Article.objects.all()[-1] + ) + + def test_slicing_negative_indexing_not_supported_for_range(self): + """hint: inverting your ordering might do what you need""" + six.assertRaisesRegex( + self, + AssertionError, + "Negative indexing is not supported.", + lambda: Article.objects.all()[0:-5] + ) + + def test_can_get_number_of_items_in_queryset_using_standard_len(self): + self.assertEqual(len(Article.objects.filter(name__exact='Article 1')), 1) + + def test_can_combine_queries_using_and_and_or_operators(self): + s1 = Article.objects.filter(name__exact='Article 1') + s2 = Article.objects.filter(name__exact='Article 2') + self.assertQuerysetEqual((s1 | s2).order_by('name'), + ["", + ""]) + self.assertQuerysetEqual(s1 & s2, []) + + class WeirdQuerysetSlicingTests(BaseQuerysetTest): def setUp(self): Number.objects.create(num=1)