diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml new file mode 100644 index 0000000..08e6e09 --- /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: | + cd heroes_and_monsters && python manage.py test 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 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'. 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/entities/models.py b/heroes_and_monsters/entities/models.py index b4e4346..8ab3d8a 100644 --- a/heroes_and_monsters/entities/models.py +++ b/heroes_and_monsters/entities/models.py @@ -17,8 +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 @@ -26,14 +25,15 @@ def __str__(self): 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 @@ -116,12 +115,19 @@ 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): - class Meta: proxy = True + class Villain(Entity): is_immortal = models.BooleanField(default=False) @@ -134,9 +140,12 @@ 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 +165,17 @@ class Meta: db_table = "entities_entity" -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) +# from django.db.models.signals import pre_save +# from django.dispatch import receiver -@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=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) diff --git a/heroes_and_monsters/entities/tests.py b/heroes_and_monsters/entities/tests.py index 7ce503c..1ea4f4f 100644 --- a/heroes_and_monsters/entities/tests.py +++ b/heroes_and_monsters/entities/tests.py @@ -1,3 +1,197 @@ +from django.contrib.auth.models import User +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 -# Create your tests here. +from events.tests import GlobalUserTestData +from .models import Hero, Category, Origin + + +class GlobalCategoryTestData: + def setUp(self): + self.bulk_create_category() + + def bulk_create_category(self): + 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), + ] + ) + + +class GlobalHeroTestData(GlobalCategoryTestData): + def setUp(self): + super().setUp() + 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, + ), + ] + ) + + +class TestSubQuery(GlobalHeroTestData, TestCase): + 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 = ["God", "Demi God", "Mortal"] + 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) + + +class TestBulkCreate(GlobalCategoryTestData, TestCase): + def setUp(self): + pass + + def test_bulk_create(self): + category_count = Category.objects.all().count() + self.assertEqual(category_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") + + +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 + + +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) + + +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) + + +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) + + 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 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): diff --git a/heroes_and_monsters/events/tests.py b/heroes_and_monsters/events/tests.py index bffccb8..34816ed 100644 --- a/heroes_and_monsters/events/tests.py +++ b/heroes_and_monsters/events/tests.py @@ -1,23 +1,407 @@ +from datetime import date, datetime +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 .models import User +from django.utils.dateparse import parse_date -# Create your tests here. -class TestORM(TestCase): +from .models import User, Event, EventVillain, UserParent, Article, 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", + ), + ] + ) + +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) + 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]) + + +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) + + +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) + + +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)) + + +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'] + 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) + + 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') + + 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"]) 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 1382360..82ecfcb 100644 --- a/heroes_and_monsters/requirements.txt +++ b/heroes_and_monsters/requirements.txt @@ -1,4 +1,5 @@ -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 +python-decouple==3.1 \ No newline at end of file