Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

cleanup for #18586 (modeltests.basic.model test.test object creation) #2671

Closed
wants to merge 1 commit into from

3 participants

@zsoldosp

https://code.djangoproject.com/ticket/18586

Split up 300 line test method (inherited from doctest days)

  • Extracted method's relevant test assertions into multiple test methods on new class ModelInstanceCreationTests
  • moved the extra assertions from the test method as separate test methods to the relevant existing testclasses where possible, created new ones where couldn't find an appropriate one
  • Test data was changed to make the new tests more readable in isolation and to accommodate the various different Article models across the various test folders

I left the PR as multiple commits to be able to find refactoring mistakes while the PR is being reviewed, can be squashed later.

tests/basic/tests.py
((6 lines not shown))
+ 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(0, Article.objects.all().count())
+
+ # Save it into the database. You have to call save() explicitly.
+ a.save()
+ self.assertIsNotNone(a.id)
+ self.assertEquals(1, Article.objects.all().count())
+
+ def test_can_initialize_model_instance_using_positional_arguments(self):
+ """You can initialize a model instance using positional arguments,
@MarkusH Collaborator
MarkusH added a note

Can you please format the multi line comments like

def foo():
    """
    My very long comment that
    spans multiple lines
    """

and short comments like

def foo():
    """Short comment"""

At least the indention of the 2nd+x line is not recommended in PEP-257. Additionally, putting the first line of the comment on a separate line looks prettier to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
tests/basic/tests.py
((8 lines not shown))
+ id=None,
+ headline='Area man programs in Python',
+ pub_date=datetime(2005, 7, 28),
+ )
+ self.assertIsNone(a.id)
+ self.assertEquals(0, Article.objects.all().count())
+
+ # Save it into the database. You have to call save() explicitly.
+ a.save()
+ self.assertIsNotNone(a.id)
+ self.assertEquals(1, Article.objects.all().count())
+
+ 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."""
+ a2 = Article(None, 'Second article', datetime(2005, 7, 29))
@MarkusH Collaborator
MarkusH added a note

you can use a here again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
tests/basic/tests.py
@@ -16,7 +16,167 @@
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(0, Article.objects.all().count())
@timgraham Owner

assertEquals (deprecated alias) -> assertEqual
I would also reverse the order of the arguments and use self.assertEqual(Article.objects.all().count(), 0). That is what I have seen most often in tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
tests/basic/tests.py
((10 lines not shown))
+ pub_date=datetime(2005, 7, 28),
+ )
+ self.assertIsNone(a.id)
+ self.assertEquals(0, Article.objects.all().count())
+
+ # Save it into the database. You have to call save() explicitly.
+ a.save()
+ self.assertIsNotNone(a.id)
+ self.assertEquals(1, Article.objects.all().count())
+
+ 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.
+ """
+ a2 = Article(None, 'Second article', datetime(2005, 7, 29))
@timgraham Owner

can simply use "a" rather than "a#" in this, the next, and some of the other tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
tests/queries/tests.py
@@ -2145,6 +2145,130 @@ def test_flat_extra_values_list(self):
self.assertQuerysetEqual(qs, [72], self.identity)
+class QuerysetSupportsPythonIdioms(BaseQuerysetTest):
@timgraham Owner

looks like plain TestCase will be sufficient here and we can remove the super call to setUp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
tests/datetimes/tests.py
((46 lines not shown))
+ 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)])
+
@timgraham Owner

chop blank line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham
Owner

Please go ahead and squash, thanks.

@zsoldosp zsoldosp cleanup for #18586 (modeltests.basic.model test.test object creation)
https://code.djangoproject.com/ticket/18586

Split up 300 line test method (inherited from doctest days)

* Extracted method's relevant test assertions into multiple test
  methods on new class ModelInstanceCreationTests
* moved the extra assertions from the test method as separate test
  methods to the relevant existing testclasses where possible,
  created new ones where couldn't find an appropriate one
* Test data was changed to make the new tests more readable in
  isolation and to accommodate the various different Article
  models across the various test folders
f9edb7e
@zsoldosp

@timgraham squashed - sorry for the delay, I thought I've done it a long time ago!

@timgraham
Owner

I left some comments for improvement above, can you incorporate them?

Could you also amend the commit message to follow our guidelines?

See also our patch review checklist.

@zsoldosp

oops, overlooked those. will do - but I'll be offline now for a week, so only around June 16

@timgraham
Owner

merged in 7e2c804.

@timgraham timgraham closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 6, 2014
  1. @zsoldosp

    cleanup for #18586 (modeltests.basic.model test.test object creation)

    zsoldosp authored
    https://code.djangoproject.com/ticket/18586
    
    Split up 300 line test method (inherited from doctest days)
    
    * Extracted method's relevant test assertions into multiple test
      methods on new class ModelInstanceCreationTests
    * moved the extra assertions from the test method as separate test
      methods to the relevant existing testclasses where possible,
      created new ones where couldn't find an appropriate one
    * Test data was changed to make the new tests more readable in
      isolation and to accommodate the various different Article
      models across the various test folders
This page is out of date. Refresh to see the latest.
View
476 tests/basic/tests.py
@@ -6,7 +6,7 @@
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 .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(0, Article.objects.all().count())
@timgraham Owner

assertEquals (deprecated alias) -> assertEqual
I would also reverse the order of the arguments and use self.assertEqual(Article.objects.all().count(), 0). That is what I have seen most often in tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ # Save it into the database. You have to call save() explicitly.
+ a.save()
+ self.assertIsNotNone(a.id)
+ self.assertEquals(1, Article.objects.all().count())
+
+ 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.
+ """
+ a2 = Article(None, 'Second article', datetime(2005, 7, 29))
@timgraham Owner

can simply use "a" rather than "a#" in this, the next, and some of the other tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ a2.save()
+
+ self.assertEqual(a2.headline, 'Second article')
+ self.assertEqual(a2.pub_date, datetime(2005, 7, 29, 0, 0))
+
+ def test_can_create_instance_using_kwargs(self):
+ a3 = Article(
+ id=None,
+ headline='Third article',
+ pub_date=datetime(2005, 7, 30),
+ )
+ a3.save()
+ self.assertEqual(a3.headline, 'Third article')
+ self.assertEqual(a3.pub_date, datetime(2005, 7, 30, 0, 0))
+
+ def test_autofields_generate_different_values_for_each_instance(self):
+ a = 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, a.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.
+ a4 = Article(None, 'Fourth article', pub_date=datetime(2005, 7, 31))
+ a4.save()
+ self.assertEqual(a4.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().
+ """
+ a5 = Article(headline='Article 5', pub_date=datetime(2005, 7, 31))
+ a5.save()
+ self.assertEqual(a5.headline, 'Article 5')
+ self.assertNotEqual(a5.id, None)
+
+ def test_leaving_off_a_field_with_default_set_the_default_will_be_saved(self):
+ a6 = Article(pub_date=datetime(2005, 7, 31))
+ a6.save()
+ self.assertEqual(a6.headline, 'Default headline')
+
+ def test_for_datetimefields_saves_as_much_precision_as_was_given(self):
+ """as much precision in *seconds*"""
+ 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))
+
+ 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: 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):
# No articles are in the system yet.
@@ -186,320 +346,6 @@ def test_multiple_objects_max_num_fetched(self):
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')
def test_microsecond_precision(self):
# In PostgreSQL, microsecond-level precision is available.
View
39 tests/dates/tests.py
@@ -2,7 +2,9 @@
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 @@ def test_related_model_traverse(self):
],
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",
+ )
View
58 tests/datetimes/tests.py
@@ -97,3 +97,61 @@ def test_21432(self):
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)])
+
@timgraham Owner

chop blank line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
View
3  tests/queries/models.py
@@ -368,6 +368,9 @@ 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):
View
124 tests/queries/tests.py
@@ -2145,6 +2145,130 @@ def test_flat_extra_values_list(self):
self.assertQuerysetEqual(qs, [72], self.identity)
+class QuerysetSupportsPythonIdioms(BaseQuerysetTest):
@timgraham Owner

looks like plain TestCase will be sufficient here and we can remove the super call to setUp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ def setUp(self):
+ super(QuerysetSupportsPythonIdioms, self).setUp()
+ 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):
def setUp(self):
Number.objects.create(num=1)
Something went wrong with that request. Please try again.