From edefe66ed87b3a22b91696f191cc7bb0e6d4f849 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Tue, 4 Aug 2020 17:07:10 +0530 Subject: [PATCH 01/24] typo fix --- docs/notequal_query.rst | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/notequal_query.rst b/docs/notequal_query.rst index 444d8ba..429b34c 100644 --- a/docs/notequal_query.rst +++ b/docs/notequal_query.rst @@ -22,11 +22,7 @@ Our SQL query for the above condition will look something like :: .. image:: sqluser_notquery.png -Method 1 using exclude - -.. code-block - - +Method 1 using exclude :: >>> queryset = User.objects.exclude(id__lt=5) >>> queryset From 1f4a6c4072d876d9de92f96bfe96c78c979d4336 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Tue, 4 Aug 2020 17:10:17 +0530 Subject: [PATCH 02/24] testcases are added for first 7 sections of documentation query which will first create data and then run documentation query against that data and check if queries are in working status on not --- heroes_and_monsters/events/tests.py | 145 ++++++++++++++++++++++++++- heroes_and_monsters/requirements.txt | 8 +- 2 files changed, 147 insertions(+), 6 deletions(-) diff --git a/heroes_and_monsters/events/tests.py b/heroes_and_monsters/events/tests.py index bffccb8..59ec4c7 100644 --- a/heroes_and_monsters/events/tests.py +++ b/heroes_and_monsters/events/tests.py @@ -1,7 +1,17 @@ +import unittest + +from django.db.models import Q, Subquery +from django.db.utils import OperationalError from django.test import TestCase -from .models import User +from .models import User, Event, EventVillain, UserParent + + +class GlobalUserTestData: + + def setUp(self): + User.objects.bulk_create([User(username="yash", first_name="Yash", last_name="Rastogi", email="yash@agiliq.com"), User(username="John", first_name="John", last_name="Kumar", email="john@gmail.com"), User(username="Ricky", first_name="Ricky", last_name="Dayal", email="ricky@gmail.com"), User(username="sharukh", first_name="Sharukh", last_name="Misra", email="sharukh@hotmail.com"), User(username="Ritesh", first_name="Ritesh", last_name="Deshmukh", email="ritesh@yahoo.com"), User(username="Billy", first_name="Billy", last_name="sharma", email="billy@gmail.com"), User(username="Radha", first_name="Radha", last_name="George", email="radha@gmail.com"), User(username="sohan", first_name="Sohan", last_name="Upadhyay", email="sohan@aol.com"), User(username="Raghu", first_name="Raghu", last_name="Khan", email="raghu@rediffmail.com"), User(username="rishab", first_name="Rishabh", last_name="Deol", email="rishabh@yahoo.com")]) + -# Create your tests here. class TestORM(TestCase): def test_update_result(self): @@ -21,3 +31,134 @@ def test_number_of_queries(self): # One more query added. self.assertNumQueries(2) + def test_associated_query(self): + output = 'SELECT "events_event"."id", "events_event"."epic_id", "events_event"."details", "events_event"."years_ago" FROM "events_event"' + queryset = Event.objects.all() + self.assertEqual(str(queryset.query), output) + + +class TestANDQuery(GlobalUserTestData, TestCase): + + def setUp(self): + super().setUp() + self.queryset_1 = User.objects.filter( + first_name__startswith='R', + last_name__startswith='D' + ) + self.queryset_2 = User.objects.filter( + first_name__startswith='R' + ) & User.objects.filter( + last_name__startswith='D' + ) + self.queryset_3 = User.objects.filter( + Q(first_name__startswith='R') & + Q(last_name__startswith='D') + ) + + def test_query(self): + output_names = ['Ricky', 'Ritesh', 'Rishabh'] + + self.assertEqual(self.queryset_1.count(), 3) + self.assertEqual(list(self.queryset_1.values_list("first_name", flat=True)), output_names) + + self.assertEqual(self.queryset_2.count(), 3) + self.assertEqual(list(self.queryset_2.values_list("first_name", flat=True)), output_names) + + self.assertEqual(self.queryset_3.count(), 3) + self.assertEqual(list(self.queryset_3.values_list("first_name", flat=True)), output_names) + + def test_different_query_are_same(self): + self.assertTrue(str(self.queryset_1.query) == str(self.queryset_2.query) == str(self.queryset_3.query)) + + +class TestORQuery(GlobalUserTestData, TestCase): + + def setUp(self): + super().setUp() + self.queryset_1 = User.objects.filter( + first_name__startswith='R' + ) | User.objects.filter( + last_name__startswith='D' + ) + self.queryset_2 = User.objects.filter(Q(first_name__startswith='R')|Q(last_name__startswith='D')) + + def test_or_query(self): + output_names = ['Ricky', 'Ritesh', 'Radha', 'Raghu', 'Rishabh'] + + self.assertEqual(self.queryset_1.count(), 5) + self.assertEqual(list(self.queryset_1.values_list("first_name", flat=True)), output_names) + + self.assertEqual(self.queryset_2.count(), 5) + self.assertEqual(list(self.queryset_2.values_list("first_name", flat=True)), output_names) + + def test_different_query_are_same(self): + self.assertTrue(str(self.queryset_1.query) == str(self.queryset_2.query)) + + +class TestNotQuery(GlobalUserTestData, TestCase): + + def test_not_query(self): + self.queryset = User.objects.filter(~Q(id__lt=5)) + + output_names = ['Ritesh', 'Billy', 'Radha', 'Sohan', 'Raghu', 'Rishabh'] + + self.assertEqual(self.queryset.count(), 6) + self.assertEqual(list(self.queryset.values_list("first_name", flat=True)), output_names) + + +class TestUnionQuery(GlobalUserTestData, TestCase): + + def setUp(self): + super().setUp() + self.q1 = User.objects.filter(id__gte=5) + self.q2 = User.objects.filter(id__lte=9) + self.q3 = self.q1.union(self.q2) + self.q4 = self.q2.union(self.q1) + + def test_union_query(self): + output_names = ['Billy', 'John', 'Radha', 'Raghu', 'Ricky', 'Rishabh', 'Ritesh', 'Sharukh', 'Sohan', 'Yash'] + + self.assertEqual(self.q3.count(), 10) + self.assertEqual(self.q4.count(), 10) + self.assertEqual(list(self.q3.values_list("first_name", flat=True)), output_names) + self.assertEqual(list(self.q4.values_list("first_name", flat=True)), output_names) + + @unittest.expectedFailure + def test_union_exception(self): + q3 = EventVillain.objects.all() + self.assertRaises(OperationalError, self.q1.union(q3)) + + +class TestSomeFieldQuery(GlobalUserTestData, TestCase): + + def test_values(self): + queryset = User.objects.filter( + first_name__startswith='R' + ).values('first_name', 'last_name') + output = [{'first_name': 'Ricky', 'last_name': 'Dayal'}, {'first_name': 'Ritesh', 'last_name': 'Deshmukh'}, {'first_name': 'Radha', 'last_name': 'George'}, {'first_name': 'Raghu', 'last_name': 'Khan'}, {'first_name': 'Rishabh', 'last_name': 'Deol'}] + self.assertEqual(queryset.count(), 5) + self.assertEqual(list(queryset), output) + + def test_only(self): + queryset = User.objects.filter( + first_name__startswith='R' + ).only("first_name", "last_name") + self.assertEqual(queryset.count(), 5) + + +class TestSubQuery(GlobalUserTestData, TestCase): + + def setUp(self): + super().setUp() + u1 = User.objects.get(first_name='Ritesh', last_name='Deshmukh') + u2 = User.objects.get(first_name='Sohan', last_name='Upadhyay') + p1 = UserParent(user=u1, father_name='Vilasrao Deshmukh', mother_name='Vaishali Deshmukh') + p1.save() + p2 = UserParent(user=u2, father_name='Mr R S Upadhyay', mother_name='Mrs S K Upadhyay') + p2.save() + + def test_sub_query(self): + users = User.objects.all() + queryset = UserParent.objects.filter(user_id__in=Subquery(users.values('id'))) + + self.assertEqual(list(queryset.values_list("user_id", flat=True)), [5, 8]) diff --git a/heroes_and_monsters/requirements.txt b/heroes_and_monsters/requirements.txt index 1382360..4070187 100644 --- a/heroes_and_monsters/requirements.txt +++ b/heroes_and_monsters/requirements.txt @@ -1,4 +1,4 @@ -django==2.0.1 -Pillow -django-extensions==2.0.6 -dj-database-url==0.4.2 +dj-database-url==0.5.0 +Django==3.0.9 +django-extensions==3.0.3 +Pillow==7.2.0 From cb7680355e6f93e5c618a75452ae47fc181ae1fe Mon Sep 17 00:00:00 2001 From: Shekhar Nunia <40422523+Shekharnunia@users.noreply.github.com> Date: Tue, 4 Aug 2020 17:21:25 +0530 Subject: [PATCH 03/24] Create django.yml --- .github/workflows/django.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/django.yml diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml new file mode 100644 index 0000000..f833a26 --- /dev/null +++ b/.github/workflows/django.yml @@ -0,0 +1,30 @@ +name: Django CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + max-parallel: 4 + matrix: + python-version: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r heroes_and_monsters/requirements.txt + - name: Run Tests + run: | + python manage.py test From 244711241b1a91bc90df910805b2ec88ae5aed53 Mon Sep 17 00:00:00 2001 From: Shekhar Nunia <40422523+Shekharnunia@users.noreply.github.com> Date: Tue, 4 Aug 2020 17:23:07 +0530 Subject: [PATCH 04/24] Update django.yml --- .github/workflows/django.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index f833a26..a99d4ac 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -27,4 +27,4 @@ jobs: pip install -r heroes_and_monsters/requirements.txt - name: Run Tests run: | - python manage.py test + python heroes_and_monsters/manage.py test From 0389847c5e728958941e3fa7c02a56c88236fe8c Mon Sep 17 00:00:00 2001 From: Shekhar Nunia <40422523+Shekharnunia@users.noreply.github.com> Date: Tue, 4 Aug 2020 18:23:33 +0530 Subject: [PATCH 05/24] Update django.yml --- .github/workflows/django.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index a99d4ac..08e6e09 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -27,4 +27,4 @@ jobs: pip install -r heroes_and_monsters/requirements.txt - name: Run Tests run: | - python heroes_and_monsters/manage.py test + cd heroes_and_monsters && python manage.py test From fe6bf2642a3bb338302036ea2730b95e10c04f47 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Tue, 4 Aug 2020 18:29:17 +0530 Subject: [PATCH 06/24] python decople added into requirements and sqlite database added into environemt file --- heroes_and_monsters/.env | 2 +- heroes_and_monsters/heroes_and_monsters/settings.py | 5 ++++- heroes_and_monsters/requirements.txt | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/heroes_and_monsters/.env b/heroes_and_monsters/.env index e05ccda..d78f163 100644 --- a/heroes_and_monsters/.env +++ b/heroes_and_monsters/.env @@ -1,2 +1,2 @@ -DATABASE_URL=postgres://umsra:umsra@localhost:5432/umsra +DATABASE_URL=sqlite:///db.sqlite3 export DATABASE_URL diff --git a/heroes_and_monsters/heroes_and_monsters/settings.py b/heroes_and_monsters/heroes_and_monsters/settings.py index ad457db..32f07cf 100644 --- a/heroes_and_monsters/heroes_and_monsters/settings.py +++ b/heroes_and_monsters/heroes_and_monsters/settings.py @@ -12,6 +12,7 @@ import os import dj_database_url +from decouple import config # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -80,7 +81,9 @@ # https://docs.djangoproject.com/en/2.0/ref/settings/#databases DATABASES = { - 'default': dj_database_url.config() + 'default': dj_database_url.config( + default=config('DATABASE_URL') + ) } diff --git a/heroes_and_monsters/requirements.txt b/heroes_and_monsters/requirements.txt index 4070187..82ecfcb 100644 --- a/heroes_and_monsters/requirements.txt +++ b/heroes_and_monsters/requirements.txt @@ -2,3 +2,4 @@ dj-database-url==0.5.0 Django==3.0.9 django-extensions==3.0.3 Pillow==7.2.0 +python-decouple==3.1 \ No newline at end of file From 5a6d161e849d51f0df3e4674b7423a71b2630604 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 11:52:38 +0530 Subject: [PATCH 07/24] complex subquery test case added into entities app and black formatting applied on both app test file --- heroes_and_monsters/entities/tests.py | 61 +++++++- heroes_and_monsters/events/tests.py | 198 +++++++++++++++++++------- 2 files changed, 206 insertions(+), 53 deletions(-) diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index 7ce503c..7e2f254 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -1,3 +1,62 @@ +from django.db.models import OuterRef, Subquery from django.test import TestCase -# Create your tests here. +from events.tests import GlobalUserTestData +from .models import Hero, Category, Origin + + +class TestSubQuery(TestCase): + def setUp(self): + Category.objects.bulk_create( + [ + Category(name="category_1", hero_count=12, villain_count=15), + Category(name="category_2", hero_count=25, villain_count=5), + Category(name="category_3", hero_count=17, villain_count=13), + ] + ) + Origin.objects.create(name="origin_1") + Hero.objects.bulk_create( + [ + Hero( + name="Zeus", + description="A greek God", + benevolence_factor=80, + category_id=2, + origin_id=1, + ), + Hero( + name="ZeuX", + description="A greek God", + benevolence_factor=80, + category_id=2, + origin_id=1, + ), + Hero( + name="Xeus", + description="A greek God", + benevolence_factor=80, + category_id=2, + origin_id=1, + ), + Hero( + name="Poseidon", + description="A greek God", + benevolence_factor=80, + category_id=2, + origin_id=1, + ), + ] + ) + + def test_sub_query(self): + hero_qs = Hero.objects.filter(category=OuterRef("pk")).order_by( + "-benevolence_factor" + ) + + cateogories = Category.objects.all().annotate( + most_benevolent_hero=Subquery(hero_qs.values("name")[:1]) + ) + output_category = ["category_1", "category_2", "category_3"] + self.assertEqual( + list(cateogories.values_list("name", flat=True)), output_category + ) diff --git a/heroes_and_monsters/events/tests.py b/heroes_and_monsters/events/tests.py index 59ec4c7..564e32a 100644 --- a/heroes_and_monsters/events/tests.py +++ b/heroes_and_monsters/events/tests.py @@ -7,27 +7,90 @@ class GlobalUserTestData: - def setUp(self): - User.objects.bulk_create([User(username="yash", first_name="Yash", last_name="Rastogi", email="yash@agiliq.com"), User(username="John", first_name="John", last_name="Kumar", email="john@gmail.com"), User(username="Ricky", first_name="Ricky", last_name="Dayal", email="ricky@gmail.com"), User(username="sharukh", first_name="Sharukh", last_name="Misra", email="sharukh@hotmail.com"), User(username="Ritesh", first_name="Ritesh", last_name="Deshmukh", email="ritesh@yahoo.com"), User(username="Billy", first_name="Billy", last_name="sharma", email="billy@gmail.com"), User(username="Radha", first_name="Radha", last_name="George", email="radha@gmail.com"), User(username="sohan", first_name="Sohan", last_name="Upadhyay", email="sohan@aol.com"), User(username="Raghu", first_name="Raghu", last_name="Khan", email="raghu@rediffmail.com"), User(username="rishab", first_name="Rishabh", last_name="Deol", email="rishabh@yahoo.com")]) + User.objects.bulk_create( + [ + User( + username="yash", + first_name="Yash", + last_name="Rastogi", + email="yash@agiliq.com", + ), + User( + username="John", + first_name="John", + last_name="Kumar", + email="john@gmail.com", + ), + User( + username="Ricky", + first_name="Ricky", + last_name="Dayal", + email="ricky@gmail.com", + ), + User( + username="sharukh", + first_name="Sharukh", + last_name="Misra", + email="sharukh@hotmail.com", + ), + User( + username="Ritesh", + first_name="Ritesh", + last_name="Deshmukh", + email="ritesh@yahoo.com", + ), + User( + username="Billy", + first_name="Billy", + last_name="sharma", + email="billy@gmail.com", + ), + User( + username="Radha", + first_name="Radha", + last_name="George", + email="radha@gmail.com", + ), + User( + username="sohan", + first_name="Sohan", + last_name="Upadhyay", + email="sohan@aol.com", + ), + User( + username="Raghu", + first_name="Raghu", + last_name="Khan", + email="raghu@rediffmail.com", + ), + User( + username="rishab", + first_name="Rishabh", + last_name="Deol", + email="rishabh@yahoo.com", + ), + ] + ) class TestORM(TestCase): - def test_update_result(self): - userobject = User.objects.create(username='testuser', first_name='Test', last_name='user') - User.objects.filter(username='testuser').update(username='test1user') + userobject = User.objects.create( + username="testuser", first_name="Test", last_name="user" + ) + User.objects.filter(username="testuser").update(username="test1user") # At this point userobject.val is still testuser, but the value in the database # was updated to test1user. The object's updated value needs to be reloaded # from the database. userobject.refresh_from_db() - self.assertEqual(userobject.username, 'test1user') + self.assertEqual(userobject.username, "test1user") def test_number_of_queries(self): - User.objects.create(username='testuser1', first_name='Test', last_name='user1') + User.objects.create(username="testuser1", first_name="Test", last_name="user1") # Above ORM create will run only one query. self.assertNumQueries(1) - User.objects.filter(username='testuser').update(username='test1user') + User.objects.filter(username="testuser").update(username="test1user") # One more query added. self.assertNumQueries(2) @@ -38,76 +101,84 @@ def test_associated_query(self): class TestANDQuery(GlobalUserTestData, TestCase): - def setUp(self): super().setUp() self.queryset_1 = User.objects.filter( - first_name__startswith='R', - last_name__startswith='D' + first_name__startswith="R", last_name__startswith="D" ) self.queryset_2 = User.objects.filter( - first_name__startswith='R' - ) & User.objects.filter( - last_name__startswith='D' - ) + first_name__startswith="R" + ) & User.objects.filter(last_name__startswith="D") self.queryset_3 = User.objects.filter( - Q(first_name__startswith='R') & - Q(last_name__startswith='D') + Q(first_name__startswith="R") & Q(last_name__startswith="D") ) def test_query(self): - output_names = ['Ricky', 'Ritesh', 'Rishabh'] + output_names = ["Ricky", "Ritesh", "Rishabh"] self.assertEqual(self.queryset_1.count(), 3) - self.assertEqual(list(self.queryset_1.values_list("first_name", flat=True)), output_names) + self.assertEqual( + list(self.queryset_1.values_list("first_name", flat=True)), output_names + ) self.assertEqual(self.queryset_2.count(), 3) - self.assertEqual(list(self.queryset_2.values_list("first_name", flat=True)), output_names) + self.assertEqual( + list(self.queryset_2.values_list("first_name", flat=True)), output_names + ) self.assertEqual(self.queryset_3.count(), 3) - self.assertEqual(list(self.queryset_3.values_list("first_name", flat=True)), output_names) + self.assertEqual( + list(self.queryset_3.values_list("first_name", flat=True)), output_names + ) def test_different_query_are_same(self): - self.assertTrue(str(self.queryset_1.query) == str(self.queryset_2.query) == str(self.queryset_3.query)) + self.assertTrue( + str(self.queryset_1.query) + == str(self.queryset_2.query) + == str(self.queryset_3.query) + ) class TestORQuery(GlobalUserTestData, TestCase): - def setUp(self): super().setUp() self.queryset_1 = User.objects.filter( - first_name__startswith='R' - ) | User.objects.filter( - last_name__startswith='D' + first_name__startswith="R" + ) | User.objects.filter(last_name__startswith="D") + self.queryset_2 = User.objects.filter( + Q(first_name__startswith="R") | Q(last_name__startswith="D") ) - self.queryset_2 = User.objects.filter(Q(first_name__startswith='R')|Q(last_name__startswith='D')) def test_or_query(self): - output_names = ['Ricky', 'Ritesh', 'Radha', 'Raghu', 'Rishabh'] + output_names = ["Ricky", "Ritesh", "Radha", "Raghu", "Rishabh"] self.assertEqual(self.queryset_1.count(), 5) - self.assertEqual(list(self.queryset_1.values_list("first_name", flat=True)), output_names) + self.assertEqual( + list(self.queryset_1.values_list("first_name", flat=True)), output_names + ) self.assertEqual(self.queryset_2.count(), 5) - self.assertEqual(list(self.queryset_2.values_list("first_name", flat=True)), output_names) + self.assertEqual( + list(self.queryset_2.values_list("first_name", flat=True)), output_names + ) def test_different_query_are_same(self): self.assertTrue(str(self.queryset_1.query) == str(self.queryset_2.query)) class TestNotQuery(GlobalUserTestData, TestCase): - def test_not_query(self): self.queryset = User.objects.filter(~Q(id__lt=5)) - output_names = ['Ritesh', 'Billy', 'Radha', 'Sohan', 'Raghu', 'Rishabh'] + output_names = ["Ritesh", "Billy", "Radha", "Sohan", "Raghu", "Rishabh"] self.assertEqual(self.queryset.count(), 6) - self.assertEqual(list(self.queryset.values_list("first_name", flat=True)), output_names) + self.assertEqual( + list(self.queryset.values_list("first_name", flat=True)), output_names + ) class TestUnionQuery(GlobalUserTestData, TestCase): - def setUp(self): super().setUp() self.q1 = User.objects.filter(id__gte=5) @@ -115,13 +186,28 @@ def setUp(self): self.q3 = self.q1.union(self.q2) self.q4 = self.q2.union(self.q1) - def test_union_query(self): - output_names = ['Billy', 'John', 'Radha', 'Raghu', 'Ricky', 'Rishabh', 'Ritesh', 'Sharukh', 'Sohan', 'Yash'] + def test_union_query(self): + output_names = [ + "Billy", + "John", + "Radha", + "Raghu", + "Ricky", + "Rishabh", + "Ritesh", + "Sharukh", + "Sohan", + "Yash", + ] self.assertEqual(self.q3.count(), 10) self.assertEqual(self.q4.count(), 10) - self.assertEqual(list(self.q3.values_list("first_name", flat=True)), output_names) - self.assertEqual(list(self.q4.values_list("first_name", flat=True)), output_names) + self.assertEqual( + list(self.q3.values_list("first_name", flat=True)), output_names + ) + self.assertEqual( + list(self.q4.values_list("first_name", flat=True)), output_names + ) @unittest.expectedFailure def test_union_exception(self): @@ -130,35 +216,43 @@ def test_union_exception(self): class TestSomeFieldQuery(GlobalUserTestData, TestCase): - def test_values(self): - queryset = User.objects.filter( - first_name__startswith='R' - ).values('first_name', 'last_name') - output = [{'first_name': 'Ricky', 'last_name': 'Dayal'}, {'first_name': 'Ritesh', 'last_name': 'Deshmukh'}, {'first_name': 'Radha', 'last_name': 'George'}, {'first_name': 'Raghu', 'last_name': 'Khan'}, {'first_name': 'Rishabh', 'last_name': 'Deol'}] + queryset = User.objects.filter(first_name__startswith="R").values( + "first_name", "last_name" + ) + output = [ + {"first_name": "Ricky", "last_name": "Dayal"}, + {"first_name": "Ritesh", "last_name": "Deshmukh"}, + {"first_name": "Radha", "last_name": "George"}, + {"first_name": "Raghu", "last_name": "Khan"}, + {"first_name": "Rishabh", "last_name": "Deol"}, + ] self.assertEqual(queryset.count(), 5) self.assertEqual(list(queryset), output) def test_only(self): - queryset = User.objects.filter( - first_name__startswith='R' - ).only("first_name", "last_name") + queryset = User.objects.filter(first_name__startswith="R").only( + "first_name", "last_name" + ) self.assertEqual(queryset.count(), 5) class TestSubQuery(GlobalUserTestData, TestCase): - def setUp(self): super().setUp() - u1 = User.objects.get(first_name='Ritesh', last_name='Deshmukh') - u2 = User.objects.get(first_name='Sohan', last_name='Upadhyay') - p1 = UserParent(user=u1, father_name='Vilasrao Deshmukh', mother_name='Vaishali Deshmukh') + u1 = User.objects.get(first_name="Ritesh", last_name="Deshmukh") + u2 = User.objects.get(first_name="Sohan", last_name="Upadhyay") + p1 = UserParent( + user=u1, father_name="Vilasrao Deshmukh", mother_name="Vaishali Deshmukh" + ) p1.save() - p2 = UserParent(user=u2, father_name='Mr R S Upadhyay', mother_name='Mrs S K Upadhyay') + p2 = UserParent( + user=u2, father_name="Mr R S Upadhyay", mother_name="Mrs S K Upadhyay" + ) p2.save() def test_sub_query(self): users = User.objects.all() - queryset = UserParent.objects.filter(user_id__in=Subquery(users.values('id'))) + queryset = UserParent.objects.filter(user_id__in=Subquery(users.values("id"))) self.assertEqual(list(queryset.values_list("user_id", flat=True)), [5, 8]) From bd47cf54bbac1346b2d547534d9fb50ef0d2b05d Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 12:31:16 +0530 Subject: [PATCH 08/24] F expression test cases are added which will first check for simple F expression query and then a complex query which includes substr and annotation --- heroes_and_monsters/entities/tests.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index 7e2f254..3065475 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -1,4 +1,6 @@ -from django.db.models import OuterRef, Subquery +from django.contrib.auth.models import User +from django.db.models import OuterRef, Subquery, F +from django.db.models.functions import Substr from django.test import TestCase from events.tests import GlobalUserTestData @@ -60,3 +62,22 @@ def test_sub_query(self): self.assertEqual( list(cateogories.values_list("name", flat=True)), output_category ) + + +class TestFQuery(TestCase): + + def setUp(self): + User.objects.create_user(email="shabda@example.com", username="shabda", first_name="Shabda", last_name="Raaj") + User.objects.create_user(email="guido@example.com", username="Guido", first_name="Guido", last_name="Guido") + + def test_simple_f_expression(self): + users = User.objects.filter(last_name=F("first_name")) + output_user = ["Guido"] + self.assertEqual(list(users.values_list("first_name", flat=True)), output_user) + + def test_annotate_f_expression_with_substr(self): + User.objects.create_user(email="guido@example.com", username="Tim", first_name="Tim", last_name="Teters") + users = User.objects.annotate(first=Substr("first_name", 1, 1), last=Substr("last_name", 1, 1)).filter(first=F("last")) + output_user = ["Guido", "Tim"] + self.assertEqual(list(users.values_list("first_name", flat=True)), output_user) + From 2185750e7eebc867b79180bdda5ff841655b90ec Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 13:26:48 +0530 Subject: [PATCH 09/24] Join operation test added which will verify if the given django orm query is equal to given database query, also test for second largest, duplication and distinct query are also added --- heroes_and_monsters/events/tests.py | 60 ++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/heroes_and_monsters/events/tests.py b/heroes_and_monsters/events/tests.py index 564e32a..15dba4e 100644 --- a/heroes_and_monsters/events/tests.py +++ b/heroes_and_monsters/events/tests.py @@ -1,9 +1,9 @@ import unittest -from django.db.models import Q, Subquery +from django.db.models import Q, Subquery, Count from django.db.utils import OperationalError from django.test import TestCase -from .models import User, Event, EventVillain, UserParent +from .models import User, Event, EventVillain, UserParent, Article class GlobalUserTestData: @@ -256,3 +256,59 @@ def test_sub_query(self): queryset = UserParent.objects.filter(user_id__in=Subquery(users.values("id"))) self.assertEqual(list(queryset.values_list("user_id", flat=True)), [5, 8]) + + +class TestJoinOperation(TestCase): + + def test_join_query(self): + a1 = Article.objects.select_related('reporter') + output_query = 'SELECT "events_article"."id", "events_article"."headline", "events_article"."pub_date", "events_article"."reporter_id", "events_article"."slug", "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" FROM "events_article" INNER JOIN "auth_user" ON ("events_article"."reporter_id" = "auth_user"."id") ORDER BY "events_article"."headline" ASC' + self.assertEqual(str(a1.query), output_query) + + +class TestSecondLargestRecord(GlobalUserTestData, TestCase): + def test_second_largest_record(self): + user = User.objects.order_by('-last_login')[1] + self.assertEqual(user.username, "John") + + +class TestDuplicateRecord(GlobalUserTestData, TestCase): + def setUp(self): + super().setUp() + User.objects.create_user( + username="yash2", + first_name="Yash", + last_name="Rastogi2", + email="yash@example.com", + ) + + def test_duplicate(self): + duplicates = User.objects.values( + 'first_name' + ).annotate(name_count=Count('first_name')).filter(name_count__gt=1) + self.assertEqual(list(duplicates), [{"first_name": "Yash", "name_count": 2}]) + + records = User.objects.filter(first_name__in=[item['first_name'] for item in duplicates]) + self.assertEqual([item.id for item in records], [1, 11]) + + +class TestDistinctRecord(GlobalUserTestData, TestCase): + def test_distinct_user(self): + distinct = User.objects.values( + 'first_name' + ).annotate( + name_count=Count('first_name') + ).filter(name_count=1) + output_user = [ + {'first_name': 'Billy', 'name_count': 1}, + {'first_name': 'John', 'name_count': 1}, + {'first_name': 'Radha', 'name_count': 1}, + {'first_name': 'Raghu', 'name_count': 1}, + {'first_name': 'Ricky', 'name_count': 1}, + {'first_name': 'Rishabh', 'name_count': 1}, + {'first_name': 'Ritesh', 'name_count': 1}, + {'first_name': 'Sharukh', 'name_count': 1}, + {'first_name': 'Sohan', 'name_count': 1}, + {'first_name': 'Yash', 'name_count': 1} + ] + self.assertEqual(list(distinct), output_user) \ No newline at end of file From fa7deff36867a3be49e378a80245ee2fe2bfc2c5 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 15:10:16 +0530 Subject: [PATCH 10/24] test case for grouping queries are added --- heroes_and_monsters/entities/tests.py | 27 ++++++++--- heroes_and_monsters/events/tests.py | 69 +++++++++++++++++---------- 2 files changed, 66 insertions(+), 30 deletions(-) diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index 3065475..a26d9d9 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -65,10 +65,19 @@ def test_sub_query(self): class TestFQuery(TestCase): - def setUp(self): - User.objects.create_user(email="shabda@example.com", username="shabda", first_name="Shabda", last_name="Raaj") - User.objects.create_user(email="guido@example.com", username="Guido", first_name="Guido", last_name="Guido") + User.objects.create_user( + email="shabda@example.com", + username="shabda", + first_name="Shabda", + last_name="Raaj", + ) + User.objects.create_user( + email="guido@example.com", + username="Guido", + first_name="Guido", + last_name="Guido", + ) def test_simple_f_expression(self): users = User.objects.filter(last_name=F("first_name")) @@ -76,8 +85,14 @@ def test_simple_f_expression(self): self.assertEqual(list(users.values_list("first_name", flat=True)), output_user) def test_annotate_f_expression_with_substr(self): - User.objects.create_user(email="guido@example.com", username="Tim", first_name="Tim", last_name="Teters") - users = User.objects.annotate(first=Substr("first_name", 1, 1), last=Substr("last_name", 1, 1)).filter(first=F("last")) + User.objects.create_user( + email="guido@example.com", + username="Tim", + first_name="Tim", + last_name="Teters", + ) + users = User.objects.annotate( + first=Substr("first_name", 1, 1), last=Substr("last_name", 1, 1) + ).filter(first=F("last")) output_user = ["Guido", "Tim"] self.assertEqual(list(users.values_list("first_name", flat=True)), output_user) - diff --git a/heroes_and_monsters/events/tests.py b/heroes_and_monsters/events/tests.py index 15dba4e..f02ae3a 100644 --- a/heroes_and_monsters/events/tests.py +++ b/heroes_and_monsters/events/tests.py @@ -1,6 +1,6 @@ import unittest -from django.db.models import Q, Subquery, Count +from django.db.models import Q, Subquery, Avg, Max, Min, Sum, Count from django.db.utils import OperationalError from django.test import TestCase from .models import User, Event, EventVillain, UserParent, Article @@ -259,16 +259,15 @@ def test_sub_query(self): class TestJoinOperation(TestCase): - def test_join_query(self): - a1 = Article.objects.select_related('reporter') + a1 = Article.objects.select_related("reporter") output_query = 'SELECT "events_article"."id", "events_article"."headline", "events_article"."pub_date", "events_article"."reporter_id", "events_article"."slug", "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" FROM "events_article" INNER JOIN "auth_user" ON ("events_article"."reporter_id" = "auth_user"."id") ORDER BY "events_article"."headline" ASC' self.assertEqual(str(a1.query), output_query) class TestSecondLargestRecord(GlobalUserTestData, TestCase): def test_second_largest_record(self): - user = User.objects.order_by('-last_login')[1] + user = User.objects.order_by("-last_login")[1] self.assertEqual(user.username, "John") @@ -283,32 +282,54 @@ def setUp(self): ) def test_duplicate(self): - duplicates = User.objects.values( - 'first_name' - ).annotate(name_count=Count('first_name')).filter(name_count__gt=1) + duplicates = ( + User.objects.values("first_name") + .annotate(name_count=Count("first_name")) + .filter(name_count__gt=1) + ) self.assertEqual(list(duplicates), [{"first_name": "Yash", "name_count": 2}]) - records = User.objects.filter(first_name__in=[item['first_name'] for item in duplicates]) + records = User.objects.filter( + first_name__in=[item["first_name"] for item in duplicates] + ) self.assertEqual([item.id for item in records], [1, 11]) class TestDistinctRecord(GlobalUserTestData, TestCase): def test_distinct_user(self): - distinct = User.objects.values( - 'first_name' - ).annotate( - name_count=Count('first_name') - ).filter(name_count=1) + distinct = ( + User.objects.values("first_name") + .annotate(name_count=Count("first_name")) + .filter(name_count=1) + ) output_user = [ - {'first_name': 'Billy', 'name_count': 1}, - {'first_name': 'John', 'name_count': 1}, - {'first_name': 'Radha', 'name_count': 1}, - {'first_name': 'Raghu', 'name_count': 1}, - {'first_name': 'Ricky', 'name_count': 1}, - {'first_name': 'Rishabh', 'name_count': 1}, - {'first_name': 'Ritesh', 'name_count': 1}, - {'first_name': 'Sharukh', 'name_count': 1}, - {'first_name': 'Sohan', 'name_count': 1}, - {'first_name': 'Yash', 'name_count': 1} + {"first_name": "Billy", "name_count": 1}, + {"first_name": "John", "name_count": 1}, + {"first_name": "Radha", "name_count": 1}, + {"first_name": "Raghu", "name_count": 1}, + {"first_name": "Ricky", "name_count": 1}, + {"first_name": "Rishabh", "name_count": 1}, + {"first_name": "Ritesh", "name_count": 1}, + {"first_name": "Sharukh", "name_count": 1}, + {"first_name": "Sohan", "name_count": 1}, + {"first_name": "Yash", "name_count": 1}, ] - self.assertEqual(list(distinct), output_user) \ No newline at end of file + self.assertEqual(list(distinct), output_user) + + +class TestGroupQuery(GlobalUserTestData, TestCase): + def test_avg(self): + avg_id = User.objects.all().aggregate(Avg("id")) + self.assertEqual(avg_id["id__avg"], 5.5) + + def test_max(self): + max_id = User.objects.all().aggregate(Max("id")) + self.assertEqual(max_id["id__max"], 10) + + def test_min(self): + min_id = User.objects.all().aggregate(Min("id")) + self.assertEqual(min_id["id__min"], 1) + + def test_sum(self): + sum_id = User.objects.all().aggregate(Sum("id")) + self.assertEqual(sum_id["id__sum"], 55) From 31766c67bf3c02830d01b88b31ed4ede43c0446f Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 15:26:49 +0530 Subject: [PATCH 11/24] bulk create query test cases are added --- heroes_and_monsters/entities/tests.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index a26d9d9..67e1918 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -96,3 +96,18 @@ def test_annotate_f_expression_with_substr(self): ).filter(first=F("last")) output_user = ["Guido", "Tim"] self.assertEqual(list(users.values_list("first_name", flat=True)), output_user) + + +class TestBulkCreate(TestCase): + def test_bulk_create(self): + category_count = Category.objects.all().count() + self.assertEqual(category_count, 0) + Category.objects.bulk_create( + [ + Category(name="God", hero_count=0, villain_count=0), + Category(name="Demi God", hero_count=0, villain_count=0), + Category(name="Mortal", hero_count=0, villain_count=0), + ] + ) + category_count = Category.objects.all().count() + self.assertEqual(category_count, 3) From f80251530204ed1e9b65f3e2bdcf126e89ac365d Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 15:40:52 +0530 Subject: [PATCH 12/24] object copy query test care are written --- heroes_and_monsters/entities/tests.py | 47 +++++++++++++++++++-------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index 67e1918..f150a6e 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -7,15 +7,23 @@ from .models import Hero, Category, Origin -class TestSubQuery(TestCase): +class GlobalCategoryTestData: def setUp(self): + self.bulk_create_category() + + def bulk_create_category(self): Category.objects.bulk_create( [ - Category(name="category_1", hero_count=12, villain_count=15), - Category(name="category_2", hero_count=25, villain_count=5), - Category(name="category_3", hero_count=17, villain_count=13), + Category(name="God", hero_count=0, villain_count=0), + Category(name="Demi God", hero_count=0, villain_count=0), + Category(name="Mortal", hero_count=0, villain_count=0), ] ) + + +class TestSubQuery(GlobalCategoryTestData, TestCase): + def setUp(self): + super().setUp() Origin.objects.create(name="origin_1") Hero.objects.bulk_create( [ @@ -58,7 +66,7 @@ def test_sub_query(self): cateogories = Category.objects.all().annotate( most_benevolent_hero=Subquery(hero_qs.values("name")[:1]) ) - output_category = ["category_1", "category_2", "category_3"] + output_category = ["God", "Demi God", "Mortal"] self.assertEqual( list(cateogories.values_list("name", flat=True)), output_category ) @@ -98,16 +106,29 @@ def test_annotate_f_expression_with_substr(self): self.assertEqual(list(users.values_list("first_name", flat=True)), output_user) -class TestBulkCreate(TestCase): +class TestBulkCreate(GlobalCategoryTestData, TestCase): + def setUp(self): + pass + def test_bulk_create(self): category_count = Category.objects.all().count() self.assertEqual(category_count, 0) - Category.objects.bulk_create( - [ - Category(name="God", hero_count=0, villain_count=0), - Category(name="Demi God", hero_count=0, villain_count=0), - Category(name="Mortal", hero_count=0, villain_count=0), - ] - ) + + self.bulk_create_category() + category_count = Category.objects.all().count() self.assertEqual(category_count, 3) + + +class TestObjectCopy(GlobalCategoryTestData, TestCase): + def test_object_copy(self): + category_count = Category.objects.count() + self.assertEqual(category_count, 3) + + category = Category.objects.last() + category.pk = None + category.save() + + category_count = Category.objects.count() + self.assertEqual(category_count, 4) + self.assertEqual(Category.objects.last().name, "Mortal") From 157751c790e6bc3d9cd6ee92e34bb376e0f1c23a Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 15:52:38 +0530 Subject: [PATCH 13/24] test case added to check that only single object will be created a model --- heroes_and_monsters/entities/tests.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index f150a6e..87cf4ed 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -1,6 +1,7 @@ from django.contrib.auth.models import User from django.db.models import OuterRef, Subquery, F from django.db.models.functions import Substr +from django.db.utils import IntegrityError from django.test import TestCase from events.tests import GlobalUserTestData @@ -132,3 +133,15 @@ def test_object_copy(self): category_count = Category.objects.count() self.assertEqual(category_count, 4) self.assertEqual(Category.objects.last().name, "Mortal") + + +class TestSingleObjectCreate(TestCase): + def test_single_object_create(self): + Origin.objects.create(name="origin 1") + self.assertEqual(Origin.objects.count(), 1) + + try: + Origin.objects.create(name="origin 2") + except IntegrityError: + pass + self.assertEqual(Origin.objects.count(), 1) From 04e986ac5368f3a3a1d4dfd4dbfd574156aa3b99 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 17:27:03 +0530 Subject: [PATCH 14/24] test case for denormalized data is added --- heroes_and_monsters/entities/models.py | 36 +++++++++++++++----------- heroes_and_monsters/entities/tests.py | 23 ++++++++++++++-- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/heroes_and_monsters/entities/models.py b/heroes_and_monsters/entities/models.py index b4e4346..b18b320 100644 --- a/heroes_and_monsters/entities/models.py +++ b/heroes_and_monsters/entities/models.py @@ -116,6 +116,11 @@ class Meta: "self", related_name="+", null=True, blank=True, on_delete=models.SET_NULL ) + def save(self, *args, **kwargs): + if not self.pk: + Category.objects.filter(pk=self.category_id).update(hero_count=F('hero_count')+1) + super().save(*args, **kwargs) + class HeroProxy(Hero): @@ -134,9 +139,10 @@ class Villain(Entity): is_unique = models.BooleanField(default=True) count = models.PositiveSmallIntegerField(default=1) - # def save(self, *args, **kwargs): - # super().save(*args, **kwargs) - # Category.objects.filter(pk=self.category_pk).update(villain_count=F(villain_count)+1) + def save(self, *args, **kwargs): + if not self.pk: + Category.objects.filter(pk=self.category_id).update(villain_count=F('villain_count')+1) + super().save(*args, **kwargs) class HeroAcquaintance(models.Model): @@ -156,18 +162,18 @@ class Meta: db_table = "entities_entity" -from django.db.models.signals import pre_save -from django.dispatch import receiver +# from django.db.models.signals import pre_save +# from django.dispatch import receiver -@receiver(pre_save, sender=Hero, dispatch_uid="update_hero_count") -def update_hero_count(sender, **kwargs): - hero = kwargs['instance'] - if hero.pk: - Category.objects.filter(pk=hero.category_id).update(hero_count=F('hero_count')+1) +# @receiver(pre_save, sender=Hero, dispatch_uid="update_hero_count") +# def update_hero_count(sender, **kwargs): +# hero = kwargs['instance'] +# if hero.pk: +# Category.objects.filter(pk=hero.category_id).update(hero_count=F('hero_count')+1) -@receiver(pre_save, sender=Villain, dispatch_uid="update_villain_count") -def update_villain_count(sender, **kwargs): - villain = kwargs['instance'] - if villain.pk: - Category.objects.filter(pk=villain.category_id).update(villain_count=F('villain_count')+1) +# @receiver(pre_save, sender=Villain, dispatch_uid="update_villain_count") +# def update_villain_count(sender, **kwargs): +# villain = kwargs['instance'] +# if villain.pk: +# Category.objects.filter(pk=villain.category_id).update(villain_count=F('villain_count')+1) diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index 87cf4ed..717fcaa 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -22,7 +22,7 @@ def bulk_create_category(self): ) -class TestSubQuery(GlobalCategoryTestData, TestCase): +class GlobalHeroTestData(GlobalCategoryTestData): def setUp(self): super().setUp() Origin.objects.create(name="origin_1") @@ -59,6 +59,9 @@ def setUp(self): ] ) + +class TestSubQuery(GlobalHeroTestData, TestCase): + def test_sub_query(self): hero_qs = Hero.objects.filter(category=OuterRef("pk")).order_by( "-benevolence_factor" @@ -144,4 +147,20 @@ def test_single_object_create(self): Origin.objects.create(name="origin 2") except IntegrityError: pass - self.assertEqual(Origin.objects.count(), 1) + + +class TestDenormalizedColumnUpdate(GlobalHeroTestData, TestCase): + def test_hero_count(self): + category = Category.objects.get(id=2) + + hero_count = category.hero_count + Hero.objects.create( + name="Iron Man", + description="Iron Man", + benevolence_factor=90, + category_id=2, + origin_id=1, + ) + category = Category.objects.get(id=2) + + self.assertEqual(category.hero_count, hero_count+1) \ No newline at end of file From 6080409c1c25d4f13ae5f91cf53c315d092f3dd8 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 17:28:56 +0530 Subject: [PATCH 15/24] black formatting applied for models and test files --- heroes_and_monsters/entities/models.py | 44 ++++++++++++++------------ heroes_and_monsters/entities/tests.py | 15 ++++----- heroes_and_monsters/events/models.py | 18 ++++++----- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/heroes_and_monsters/entities/models.py b/heroes_and_monsters/entities/models.py index b18b320..42fc941 100644 --- a/heroes_and_monsters/entities/models.py +++ b/heroes_and_monsters/entities/models.py @@ -19,21 +19,21 @@ def truncate(cls): with connection.cursor() as cursor: cursor.execute('TRUNCATE TABLE "{0}" CASCADE'.format(cls._meta.db_table)) - def __str__(self): return self.name from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType + # ... + class FlexCategory(models.Model): name = models.SlugField() content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() - content_object = GenericForeignKey('content_type', 'object_id') - + content_object = GenericForeignKey("content_type", "object_id") class Origin(models.Model): @@ -54,13 +54,10 @@ class Entity(models.Model): GENDER_OTHERS = "Others/Unknown" name = models.CharField(max_length=100) - alternative_name = models.CharField( - max_length=100, null=True, blank=True - ) - + alternative_name = models.CharField(max_length=100, null=True, blank=True) category = models.ForeignKey(Category, on_delete=models.CASCADE) - flex_category = GenericRelation(FlexCategory, related_query_name='flex_category') + flex_category = GenericRelation(FlexCategory, related_query_name="flex_category") origin = models.ForeignKey(Origin, on_delete=models.CASCADE) gender = models.CharField( max_length=100, @@ -68,12 +65,13 @@ class Entity(models.Model): (GENDER_MALE, GENDER_MALE), (GENDER_FEMALE, GENDER_FEMALE), (GENDER_OTHERS, GENDER_OTHERS), - ) + ), ) description = models.TextField() - added_by = models.ForeignKey(settings.AUTH_USER_MODEL, - null=True, blank=True, on_delete=models.SET_NULL) + added_by = models.ForeignKey( + settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL + ) added_on = models.DateField(auto_now=True) def __str__(self): @@ -84,7 +82,6 @@ class Meta: class Hero(Entity): - class Meta: verbose_name_plural = "Heroes" @@ -95,19 +92,21 @@ class Meta: is_immortal = models.BooleanField(default=True) benevolence_factor = models.PositiveSmallIntegerField( - help_text="How benevolent this hero is?", - default=50 + help_text="How benevolent this hero is?", default=50 ) arbitrariness_factor = models.PositiveSmallIntegerField( - help_text="How arbitrary this hero is?", - default=50 + help_text="How arbitrary this hero is?", default=50 ) headshot = models.ImageField(null=True, blank=True, upload_to="hero_headshots/") # relationships father = models.ForeignKey( - "self", related_name="children", null=True, blank=True, on_delete=models.SET_NULL + "self", + related_name="children", + null=True, + blank=True, + on_delete=models.SET_NULL, ) mother = models.ForeignKey( "self", related_name="+", null=True, blank=True, on_delete=models.SET_NULL @@ -118,15 +117,17 @@ class Meta: def save(self, *args, **kwargs): if not self.pk: - Category.objects.filter(pk=self.category_id).update(hero_count=F('hero_count')+1) + Category.objects.filter(pk=self.category_id).update( + hero_count=F("hero_count") + 1 + ) super().save(*args, **kwargs) class HeroProxy(Hero): - class Meta: proxy = True + class Villain(Entity): is_immortal = models.BooleanField(default=False) @@ -141,7 +142,9 @@ class Villain(Entity): def save(self, *args, **kwargs): if not self.pk: - Category.objects.filter(pk=self.category_id).update(villain_count=F('villain_count')+1) + Category.objects.filter(pk=self.category_id).update( + villain_count=F("villain_count") + 1 + ) super().save(*args, **kwargs) @@ -176,4 +179,3 @@ class Meta: # villain = kwargs['instance'] # if villain.pk: # Category.objects.filter(pk=villain.category_id).update(villain_count=F('villain_count')+1) - diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index 717fcaa..5b03433 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -61,7 +61,6 @@ def setUp(self): class TestSubQuery(GlobalHeroTestData, TestCase): - def test_sub_query(self): hero_qs = Hero.objects.filter(category=OuterRef("pk")).order_by( "-benevolence_factor" @@ -155,12 +154,12 @@ def test_hero_count(self): hero_count = category.hero_count Hero.objects.create( - name="Iron Man", - description="Iron Man", - benevolence_factor=90, - category_id=2, - origin_id=1, - ) + name="Iron Man", + description="Iron Man", + benevolence_factor=90, + category_id=2, + origin_id=1, + ) category = Category.objects.get(id=2) - self.assertEqual(category.hero_count, hero_count+1) \ No newline at end of file + self.assertEqual(category.hero_count, hero_count + 1) diff --git a/heroes_and_monsters/events/models.py b/heroes_and_monsters/events/models.py index eb1ad1d..22398eb 100644 --- a/heroes_and_monsters/events/models.py +++ b/heroes_and_monsters/events/models.py @@ -4,6 +4,7 @@ from django.contrib.auth.models import User import uuid + class Epic(models.Model): name = models.CharField(max_length=255) participating_heroes = models.ManyToManyField(Hero) @@ -29,28 +30,29 @@ class EventVillain(models.Model): class UserParent(models.Model): - user = models.OneToOneField( - User, - on_delete=models.CASCADE, - primary_key=True, - ) + user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True,) father_name = models.CharField(max_length=100) mother_name = models.CharField(max_length=100) + class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() - reporter = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reporter') + reporter = models.ForeignKey( + User, on_delete=models.CASCADE, related_name="reporter" + ) slug = models.SlugField() def save(self, *args, **kwargs): self.slug = slugify(self.headline) super(Article, self).save(*args, **kwargs) + def __str__(self): return self.headline class Meta: - ordering = ('headline',) + ordering = ("headline",) + class TempUser(models.Model): first_name = models.CharField(max_length=100) @@ -61,7 +63,7 @@ class Meta: class ColumnName(models.Model): - a = models.CharField(max_length=40,db_column='column1') + a = models.CharField(max_length=40, db_column="column1") column2 = models.CharField(max_length=50) def __str__(self): From 68a0ca84f45fa9a8dfa90589d7c698b3d27f8b8c Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 17:43:57 +0530 Subject: [PATCH 16/24] test case for truncate query is added and for current working cursor method changed to sqlite sql code --- heroes_and_monsters/entities/models.py | 2 +- heroes_and_monsters/entities/tests.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/heroes_and_monsters/entities/models.py b/heroes_and_monsters/entities/models.py index 42fc941..8ab3d8a 100644 --- a/heroes_and_monsters/entities/models.py +++ b/heroes_and_monsters/entities/models.py @@ -17,7 +17,7 @@ class Meta: @classmethod def truncate(cls): with connection.cursor() as cursor: - cursor.execute('TRUNCATE TABLE "{0}" CASCADE'.format(cls._meta.db_table)) + cursor.execute('DELETE FROM "{0}"'.format(cls._meta.db_table)) def __str__(self): return self.name diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index 5b03433..f43a98f 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -163,3 +163,15 @@ def test_hero_count(self): category = Category.objects.get(id=2) self.assertEqual(category.hero_count, hero_count + 1) + + +class TestTruncateQuery(GlobalCategoryTestData, TestCase): + def test_truncate_data(self): + self.assertEqual(Category.objects.all().count(), 3) + Category.objects.all().delete() + self.assertEqual(Category.objects.all().count(), 0) + + def test_truncate_data_using_cursor(self): + self.assertEqual(Category.objects.all().count(), 3) + Category.truncate() + self.assertEqual(Category.objects.all().count(), 0) \ No newline at end of file From 1156cb547ebee2ed70701ab7779327179d57f4a6 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 18:19:07 +0530 Subject: [PATCH 17/24] test case for storing date from string into datefield is added --- heroes_and_monsters/events/tests.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/heroes_and_monsters/events/tests.py b/heroes_and_monsters/events/tests.py index f02ae3a..b290457 100644 --- a/heroes_and_monsters/events/tests.py +++ b/heroes_and_monsters/events/tests.py @@ -1,8 +1,11 @@ +from datetime import date, datetime import unittest from django.db.models import Q, Subquery, Avg, Max, Min, Sum, Count from django.db.utils import OperationalError from django.test import TestCase +from django.utils.dateparse import parse_date + from .models import User, Event, EventVillain, UserParent, Article @@ -333,3 +336,25 @@ def test_min(self): def test_sum(self): sum_id = User.objects.all().aggregate(Sum("id")) self.assertEqual(sum_id["id__sum"], 55) + + +class TestDateTimeParse(GlobalUserTestData, TestCase): + def test_str_date_input(self): + user = User.objects.get(id=1) + date_str = "2020-08-05" + temp_date = parse_date(date_str) + a1 = Article( + headline="String converted to date", pub_date=temp_date, reporter=user + ) + a1.save() + self.assertEqual(a1.id, 1) + self.assertEqual(a1.pub_date, date(year=2020, month=8, day=5)) + + temp_date = datetime.strptime(date_str, "%Y-%m-%d").date() + a2 = Article( + headline="String converted to date way 2", pub_date=temp_date, reporter=user + ) + a2.save() + a2.pub_date + self.assertEqual(a2.id, 2) + self.assertEqual(a2.pub_date, date(year=2020, month=8, day=5)) From b86a910e8e60cb22d9a58126df0ba967c47f0a12 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 18:36:48 +0530 Subject: [PATCH 18/24] test case for order by with case insensitivity is added --- heroes_and_monsters/events/tests.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/heroes_and_monsters/events/tests.py b/heroes_and_monsters/events/tests.py index b290457..e190423 100644 --- a/heroes_and_monsters/events/tests.py +++ b/heroes_and_monsters/events/tests.py @@ -2,6 +2,7 @@ import unittest from django.db.models import Q, Subquery, Avg, Max, Min, Sum, Count +from django.db.models.functions import Lower from django.db.utils import OperationalError from django.test import TestCase from django.utils.dateparse import parse_date @@ -358,3 +359,17 @@ def test_str_date_input(self): a2.pub_date self.assertEqual(a2.id, 2) self.assertEqual(a2.pub_date, date(year=2020, month=8, day=5)) + + +class TestCaseInsensitiveOrderBy(GlobalUserTestData, TestCase): + def test_plan_orderby_for_case_insensitive(self): + users = User.objects.all().order_by(Lower('username')).values_list('username', flat=True) + output_user = ['Billy', 'John', 'Radha', 'Raghu', 'Ricky', 'rishab', 'Ritesh', 'sharukh', 'sohan', 'yash'] + self.assertEqual(list(users), output_user) + + def test_annotate_orderby_for_case_insensitive(self): + users = User.objects.annotate( + uname=Lower('username') + ).order_by('uname').values_list('username', flat=True) + output_user = ['Billy', 'John', 'Radha', 'Raghu', 'Ricky', 'rishab', 'Ritesh', 'sharukh', 'sohan', 'yash'] + self.assertEqual(list(users), output_user) From 538813e4de82dcaf853b11daed986c1732439da3 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Wed, 5 Aug 2020 19:21:07 +0530 Subject: [PATCH 19/24] test case of orderby on two fields is added --- heroes_and_monsters/events/tests.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/heroes_and_monsters/events/tests.py b/heroes_and_monsters/events/tests.py index e190423..03e569a 100644 --- a/heroes_and_monsters/events/tests.py +++ b/heroes_and_monsters/events/tests.py @@ -361,7 +361,7 @@ def test_str_date_input(self): self.assertEqual(a2.pub_date, date(year=2020, month=8, day=5)) -class TestCaseInsensitiveOrderBy(GlobalUserTestData, TestCase): +class TestOrderBy(GlobalUserTestData, TestCase): def test_plan_orderby_for_case_insensitive(self): users = User.objects.all().order_by(Lower('username')).values_list('username', flat=True) output_user = ['Billy', 'John', 'Radha', 'Raghu', 'Ricky', 'rishab', 'Ritesh', 'sharukh', 'sohan', 'yash'] @@ -373,3 +373,8 @@ def test_annotate_orderby_for_case_insensitive(self): ).order_by('uname').values_list('username', flat=True) output_user = ['Billy', 'John', 'Radha', 'Raghu', 'Ricky', 'rishab', 'Ritesh', 'sharukh', 'sohan', 'yash'] self.assertEqual(list(users), output_user) + + def test_orderby_on_two_fields(self): + users = User.objects.all().order_by("is_active", "-last_login", "first_name").values_list("first_name", flat=True) + output_order = ['Billy', 'John', 'Radha', 'Raghu', 'Ricky', 'Rishabh', 'Ritesh', 'Sharukh', 'Sohan', 'Yash'] + self.assertEqual(list(users), output_order) From 320bd5fa3f5ac4c94bedd75de57470ffc1a4b8ba Mon Sep 17 00:00:00 2001 From: Shekhar Date: Thu, 6 Aug 2020 11:29:25 +0530 Subject: [PATCH 20/24] test case for order by on foreignkey field is added --- heroes_and_monsters/entities/tests.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index f43a98f..5cfd79c 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -174,4 +174,14 @@ def test_truncate_data(self): def test_truncate_data_using_cursor(self): self.assertEqual(Category.objects.all().count(), 3) Category.truncate() - self.assertEqual(Category.objects.all().count(), 0) \ No newline at end of file + self.assertEqual(Category.objects.all().count(), 0) + + +class TestOrderBy(GlobalHeroTestData, TestCase): + def test_orderby_on_related_field(self): + hereos = Hero.objects.all().order_by( + 'category__name', 'name' + ).values_list("name", flat=True) + output_hereos = ['Poseidon', 'Xeus', 'ZeuX', 'Zeus'] + + self.assertEqual(list(hereos), output_hereos) \ No newline at end of file From fac8504f61bbba046c89b5e139a544d5c6ce6479 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Thu, 6 Aug 2020 11:37:09 +0530 Subject: [PATCH 21/24] test case for orderby with annotation is added --- heroes_and_monsters/entities/tests.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index 5cfd79c..1ea4f4f 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -1,5 +1,5 @@ from django.contrib.auth.models import User -from django.db.models import OuterRef, Subquery, F +from django.db.models import OuterRef, Subquery, F, Count from django.db.models.functions import Substr from django.db.utils import IntegrityError from django.test import TestCase @@ -184,4 +184,14 @@ def test_orderby_on_related_field(self): ).values_list("name", flat=True) output_hereos = ['Poseidon', 'Xeus', 'ZeuX', 'Zeus'] - self.assertEqual(list(hereos), output_hereos) \ No newline at end of file + self.assertEqual(list(hereos), output_hereos) + + def test_orderby_on_annotate(self): + categories = Category.objects.annotate( + hero_count_annotate=Count("hero") + ).order_by( + "-hero_count_annotate" + ).values_list("name", flat=True) + output_category = ['Demi God', 'God', 'Mortal'] + + self.assertEqual(list(categories), output_category) \ No newline at end of file From 768ca9f140ff3047d613be3e151f92cba2904649 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Thu, 6 Aug 2020 11:58:59 +0530 Subject: [PATCH 22/24] test case for one to one model relationship is added --- heroes_and_monsters/events/tests.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/heroes_and_monsters/events/tests.py b/heroes_and_monsters/events/tests.py index 03e569a..530b30d 100644 --- a/heroes_and_monsters/events/tests.py +++ b/heroes_and_monsters/events/tests.py @@ -7,7 +7,7 @@ from django.test import TestCase from django.utils.dateparse import parse_date -from .models import User, Event, EventVillain, UserParent, Article +from .models import User, Event, EventVillain, UserParent, Article, UserParent class GlobalUserTestData: @@ -378,3 +378,15 @@ def test_orderby_on_two_fields(self): users = User.objects.all().order_by("is_active", "-last_login", "first_name").values_list("first_name", flat=True) output_order = ['Billy', 'John', 'Radha', 'Raghu', 'Ricky', 'Rishabh', 'Ritesh', 'Sharukh', 'Sohan', 'Yash'] self.assertEqual(list(users), output_order) + + +class TestModelRelationShip(GlobalUserTestData, TestCase): + def test_one_to_one_relationship(self): + u1 = User.objects.get(first_name='Ritesh', last_name='Deshmukh') + u2 = User.objects.get(first_name='Sohan', last_name='Upadhyay') + p1 = UserParent(user=u1, father_name='Vilasrao Deshmukh', mother_name='Vaishali Deshmukh') + p1.save() + self.assertEqual(p1.user.first_name, 'Ritesh') + p2 = UserParent(user=u2, father_name='Mr R S Upadhyay', mother_name='Mrs S K Upadhyay') + p2.save() + self.assertEqual(p2.user.last_name, 'Upadhyay') \ No newline at end of file From a022b0b5cb10d302977a795f3c2f9dd14783a803 Mon Sep 17 00:00:00 2001 From: Shekhar Date: Thu, 6 Aug 2020 16:40:43 +0530 Subject: [PATCH 23/24] typo fix in one to many documentation page --- docs/one_to_many.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/one_to_many.rst b/docs/one_to_many.rst index 263d6f7..0749654 100644 --- a/docs/one_to_many.rst +++ b/docs/one_to_many.rst @@ -30,7 +30,7 @@ To define a many-to-one relationship, use `ForeignKey`.:: If you try to assign an object before saving it you will encounter a ValueError :: >>> u3 = User(username='someuser', first_name='Some', last_name='User', email='some@example.com') - >>> Article.objects.create(headline="This is a test", pub_date=date(2018, 3, 7), reporter=u1) + >>> Article.objects.create(headline="This is a test", pub_date=date(2018, 3, 7), reporter=u3) Traceback (most recent call last): ... ValueError: save() prohibited to prevent data loss due to unsaved related object 'reporter'. From 6b5404ecb438ccf1192441c9bbaa6ef858b36c6f Mon Sep 17 00:00:00 2001 From: Shekhar Date: Thu, 6 Aug 2020 16:53:59 +0530 Subject: [PATCH 24/24] test case for one to many relation is added --- heroes_and_monsters/events/tests.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/heroes_and_monsters/events/tests.py b/heroes_and_monsters/events/tests.py index 530b30d..34816ed 100644 --- a/heroes_and_monsters/events/tests.py +++ b/heroes_and_monsters/events/tests.py @@ -389,4 +389,19 @@ def test_one_to_one_relationship(self): self.assertEqual(p1.user.first_name, 'Ritesh') p2 = UserParent(user=u2, father_name='Mr R S Upadhyay', mother_name='Mrs S K Upadhyay') p2.save() - self.assertEqual(p2.user.last_name, 'Upadhyay') \ No newline at end of file + self.assertEqual(p2.user.last_name, 'Upadhyay') + + def test_one_to_many_relationship(self): + u1 = User(username='johny1', first_name='Johny', last_name='Smith', email='johny@example.com') + u1.save() + u2 = User(username='alien', first_name='Alien', last_name='Mars', email='alien@example.com') + u2.save() + from datetime import date + a1 = Article(headline="This is a test", pub_date=date(2018, 3, 6), reporter=u1) + a1.save() + self.assertEqual(a1.reporter.id, 11) + self.assertEqual(a1.reporter.username, "johny1") + + Article.objects.create(headline="This is a test", pub_date=date(2018, 3, 7), reporter=u1) + articles = Article.objects.filter(reporter=u1) + self.assertEqual(list(articles.values_list("headline", flat=True)), ["This is a test", "This is a test"])