mirror of https://github.com/django/django.git
Split tests.basic.ModelTests in several tests; refs #18586.
This commit is contained in:
parent
77c0a904cb
commit
7e2c804c94
|
@ -6,7 +6,7 @@ import threading
|
||||||
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
||||||
from django.db import connections, DEFAULT_DB_ALIAS
|
from django.db import connections, DEFAULT_DB_ALIAS
|
||||||
from django.db import DatabaseError
|
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.manager import BaseManager
|
||||||
from django.db.models.query import QuerySet, EmptyQuerySet, ValuesListQuerySet, MAX_GET_RESULTS
|
from django.db.models.query import QuerySet, EmptyQuerySet, ValuesListQuerySet, MAX_GET_RESULTS
|
||||||
from django.test import TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature
|
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
|
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):
|
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: Amazing article>",
|
||||||
|
"<Article: An article>",
|
||||||
|
"<Article: Article One>",
|
||||||
|
"<Article: Boring article>"])
|
||||||
|
Article.objects.filter(headline__startswith='A').delete()
|
||||||
|
self.assertQuerysetEqual(Article.objects.all().order_by('headline'),
|
||||||
|
["<Article: Boring article>"])
|
||||||
|
|
||||||
|
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):
|
def test_lookup(self):
|
||||||
# No articles are in the system yet.
|
# No articles are in the system yet.
|
||||||
|
@ -186,320 +346,6 @@ class ModelTest(TestCase):
|
||||||
headline__startswith='Area',
|
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,
|
|
||||||
["<Article: Area man programs in Python>",
|
|
||||||
"<Article: Second article>"])
|
|
||||||
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],
|
|
||||||
["<Article: Second article>", "<Article: Third article>"])
|
|
||||||
|
|
||||||
s3 = Article.objects.filter(id__exact=a3.id)
|
|
||||||
self.assertQuerysetEqual((s1 | s2 | s3)[::2],
|
|
||||||
["<Article: Area man programs in Python>",
|
|
||||||
"<Article: Third article>"])
|
|
||||||
|
|
||||||
# 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)],
|
|
||||||
["<Article: Second article>", "<Article: Third article>"])
|
|
||||||
self.assertQuerysetEqual((s1 | s2 | s3)[::long(2)],
|
|
||||||
["<Article: Area man programs in Python>",
|
|
||||||
"<Article: Third article>"])
|
|
||||||
|
|
||||||
# And can be mixed with ints.
|
|
||||||
self.assertQuerysetEqual(Article.objects.all()[1:long(3)],
|
|
||||||
["<Article: Second article>", "<Article: Third article>"])
|
|
||||||
|
|
||||||
# Slices (without step) are lazy:
|
|
||||||
self.assertQuerysetEqual(Article.objects.all()[0:5].filter(),
|
|
||||||
["<Article: Area man programs in Python>",
|
|
||||||
"<Article: Second article>",
|
|
||||||
"<Article: Third article>",
|
|
||||||
"<Article: Article 6>",
|
|
||||||
"<Article: Default headline>"])
|
|
||||||
|
|
||||||
# Slicing again works:
|
|
||||||
self.assertQuerysetEqual(Article.objects.all()[0:5][0:2],
|
|
||||||
["<Article: Area man programs in Python>",
|
|
||||||
"<Article: Second article>"])
|
|
||||||
self.assertQuerysetEqual(Article.objects.all()[0:5][:2],
|
|
||||||
["<Article: Area man programs in Python>",
|
|
||||||
"<Article: Second article>"])
|
|
||||||
self.assertQuerysetEqual(Article.objects.all()[0:5][4:],
|
|
||||||
["<Article: Default headline>"])
|
|
||||||
self.assertQuerysetEqual(Article.objects.all()[0:5][5:], [])
|
|
||||||
|
|
||||||
# Some more tests!
|
|
||||||
self.assertQuerysetEqual(Article.objects.all()[2:][0:2],
|
|
||||||
["<Article: Third article>", "<Article: Article 6>"])
|
|
||||||
self.assertQuerysetEqual(Article.objects.all()[2:][:2],
|
|
||||||
["<Article: Third article>", "<Article: Article 6>"])
|
|
||||||
self.assertQuerysetEqual(Article.objects.all()[2:][2:3],
|
|
||||||
["<Article: Default headline>"])
|
|
||||||
|
|
||||||
# Using an offset without a limit is also possible.
|
|
||||||
self.assertQuerysetEqual(Article.objects.all()[5:],
|
|
||||||
["<Article: Fourth article>",
|
|
||||||
"<Article: Article 7>",
|
|
||||||
"<Article: Updated article 8>"])
|
|
||||||
|
|
||||||
# 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: Area man programs in Python>",
|
|
||||||
"<Article: Second article>",
|
|
||||||
"<Article: Third article>",
|
|
||||||
"<Article: Article 6>",
|
|
||||||
"<Article: Default headline>",
|
|
||||||
"<Article: Fourth article>",
|
|
||||||
"<Article: Article 7>",
|
|
||||||
"<Article: Updated article 8>"])
|
|
||||||
Article.objects.filter(id__lte=a4.id).delete()
|
|
||||||
self.assertQuerysetEqual(Article.objects.all(),
|
|
||||||
["<Article: Article 6>",
|
|
||||||
"<Article: Default headline>",
|
|
||||||
"<Article: Article 7>",
|
|
||||||
"<Article: Updated article 8>"])
|
|
||||||
|
|
||||||
@skipUnlessDBFeature('supports_microsecond_precision')
|
@skipUnlessDBFeature('supports_microsecond_precision')
|
||||||
def test_microsecond_precision(self):
|
def test_microsecond_precision(self):
|
||||||
# In PostgreSQL, microsecond-level precision is available.
|
# In PostgreSQL, microsecond-level precision is available.
|
||||||
|
|
|
@ -2,7 +2,9 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from django.db.models.fields import FieldDoesNotExist
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
from .models import Article, Comment, Category
|
from .models import Article, Comment, Category
|
||||||
|
|
||||||
|
@ -81,3 +83,40 @@ class DatesTests(TestCase):
|
||||||
],
|
],
|
||||||
lambda d: d,
|
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",
|
||||||
|
)
|
||||||
|
|
|
@ -97,3 +97,60 @@ class DateTimesTests(TestCase):
|
||||||
Article.objects.create(title="First one", pub_date=now)
|
Article.objects.create(title="First one", pub_date=now)
|
||||||
qs = Article.objects.datetimes('pub_date', 'second')
|
qs = Article.objects.datetimes('pub_date', 'second')
|
||||||
self.assertEqual(qs[0], now)
|
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)])
|
||||||
|
|
|
@ -364,10 +364,14 @@ class Plaything(models.Model):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Article(models.Model):
|
class Article(models.Model):
|
||||||
name = models.CharField(max_length=20)
|
name = models.CharField(max_length=20)
|
||||||
created = models.DateTimeField()
|
created = models.DateTimeField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Food(models.Model):
|
class Food(models.Model):
|
||||||
|
|
|
@ -2145,6 +2145,129 @@ class ValuesQuerysetTests(BaseQuerysetTest):
|
||||||
self.assertQuerysetEqual(qs, [72], self.identity)
|
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],
|
||||||
|
["<Article: Article 2>", "<Article: Article 3>"])
|
||||||
|
|
||||||
|
def test_slicing_with_steps_can_be_used(self):
|
||||||
|
self.assertQuerysetEqual(self.get_ordered_articles()[::2],
|
||||||
|
["<Article: Article 1>",
|
||||||
|
"<Article: Article 3>",
|
||||||
|
"<Article: Article 5>",
|
||||||
|
"<Article: Article 7>"])
|
||||||
|
|
||||||
|
@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)],
|
||||||
|
["<Article: Article 2>", "<Article: Article 3>"])
|
||||||
|
self.assertQuerysetEqual(self.get_ordered_articles()[::long(2)],
|
||||||
|
["<Article: Article 1>",
|
||||||
|
"<Article: Article 3>",
|
||||||
|
"<Article: Article 5>",
|
||||||
|
"<Article: Article 7>"])
|
||||||
|
|
||||||
|
# And can be mixed with ints.
|
||||||
|
self.assertQuerysetEqual(self.get_ordered_articles()[1:long(3)],
|
||||||
|
["<Article: Article 2>", "<Article: Article 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],
|
||||||
|
["<Article: Article 1>",
|
||||||
|
"<Article: Article 2>"])
|
||||||
|
self.assertQuerysetEqual(self.get_ordered_articles()[0:5][4:],
|
||||||
|
["<Article: Article 5>"])
|
||||||
|
self.assertQuerysetEqual(self.get_ordered_articles()[0:5][5:], [])
|
||||||
|
|
||||||
|
# Some more tests!
|
||||||
|
self.assertQuerysetEqual(self.get_ordered_articles()[2:][0:2],
|
||||||
|
["<Article: Article 3>", "<Article: Article 4>"])
|
||||||
|
self.assertQuerysetEqual(self.get_ordered_articles()[2:][:2],
|
||||||
|
["<Article: Article 3>", "<Article: Article 4>"])
|
||||||
|
self.assertQuerysetEqual(self.get_ordered_articles()[2:][2:3],
|
||||||
|
["<Article: Article 5>"])
|
||||||
|
|
||||||
|
# Using an offset without a limit is also possible.
|
||||||
|
self.assertQuerysetEqual(self.get_ordered_articles()[5:],
|
||||||
|
["<Article: Article 6>",
|
||||||
|
"<Article: Article 7>"])
|
||||||
|
|
||||||
|
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'),
|
||||||
|
["<Article: Article 1>",
|
||||||
|
"<Article: Article 2>"])
|
||||||
|
self.assertQuerysetEqual(s1 & s2, [])
|
||||||
|
|
||||||
|
|
||||||
class WeirdQuerysetSlicingTests(BaseQuerysetTest):
|
class WeirdQuerysetSlicingTests(BaseQuerysetTest):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
Number.objects.create(num=1)
|
Number.objects.create(num=1)
|
||||||
|
|
Loading…
Reference in New Issue