{"id":2708,"date":"2021-12-22T21:37:58","date_gmt":"2021-12-22T21:37:58","guid":{"rendered":"http:\/\/10.0.0.14:5556\/?p=2708"},"modified":"2021-12-29T02:41:28","modified_gmt":"2021-12-29T02:41:28","slug":"tdd-with-gatsby-django-docker-part-2-tdd-with-django","status":"publish","type":"post","link":"https:\/\/tutorials.leesonresearch.com\/tutorials\/2021\/12\/22\/tdd-with-gatsby-django-docker-part-2-tdd-with-django\/","title":{"rendered":"TDD with Django, GraphQL &#038; Docker Part 2 &#8212; Test Driven Development"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">TDD of Django API<\/h2>\n\n\n\n<p>In <a rel=\"noreferrer noopener\" href=\"\/tdd-with-django-graphene-docker-part-1\/\" target=\"_blank\">Part 1<\/a> we dockerized our Django Todo api. In this tutorial we are going to use <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Test-driven_development\" target=\"_blank\">Test Driven Development<\/a> to build out the Todo api.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Architecture of the API<\/h3>\n\n\n\n<p>Our objective is to build a Todo api that will allow us to create Todo tasks that are assigned to a project.<\/p>\n\n\n\n<p>We also want to be able to add a category attribute to each Todo task which will allow us to bundle our tasks in each project by categories. For example we may have a number Todo tasks for the building out of the back end of our app and a number of other tasks for the front end of our app.  It would be nice to be able to list our Todo tasks by:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Project<ul><li>Category<ul><li>Todo task<\/li><\/ul><\/li><\/ul><\/li><\/ul>\n\n\n\n<p>This also gives up the opportunity to use Django&#8217;s powerful MPTT, (Modified Preorder Tree Traversal) which allows us to store hierarchical data in our database. Very cool. You can read all about <a rel=\"noreferrer noopener\" href=\"https:\/\/django-mptt.readthedocs.io\/en\/latest\/overview.html\" target=\"_blank\">here<\/a>.<\/p>\n\n\n\n<p>We begin the build process of our mptt category model by first writing a test for the model that we will write.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">TDD to Test the Model<\/h4>\n\n\n\n<p>Our test is doomed to fail because we have yet to write the category model but that&#8217;s what TDD is all about. First we write the tests which describes what we want the app <em>to be able to do<\/em>.<\/p>\n\n\n\n<p>Begin by populating the file \/server\/todo_app\/tests.py with the following:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# \/server\/todo_app\/tests.py\n\nfrom django.test import TestCase\n\nfrom todo_app.models import Category\n\nclass CategoryModelTest(TestCase):\n  def test_string_representation(self):\n    category = Category(name=&quot;my category name&quot;)\n    self.assertEqual(str(category), category.name)\n<\/pre><\/div>\n\n\n<p>Above we are using the base class TestCase for our Unit Test. TestCase class creates a clean database before tests are run and runs every test function in its own transaction.<\/p>\n\n\n\n<p>First we create a data entry for our first category database and give it title, then using <strong>self.assertEqual(str(category), category.name)<\/strong> we test that our category entry\u2019s string representation is equal to its name.<\/p>\n\n\n\n<p>Run the test:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py test\nStarting postgres_dj02 ... done\nSystem check identified no issues (0 silenced).\nE\n======================================================================\nERROR: todo_app.tests (unittest.loader._FailedTest)\n----------------------------------------------------------------------\nImportError: Failed to import test module: todo_app.tests\nTraceback (most recent call last):\n  File &quot;\/usr\/local\/lib\/python3.8\/unittest\/loader.py&quot;, line 436, in _find_test_path\n    module = self._get_module_from_name(name)\n  File &quot;\/usr\/local\/lib\/python3.8\/unittest\/loader.py&quot;, line 377, in _get_module_from_name\n    __import__(name)\n  File &quot;\/var\/www\/server\/todo_app\/tests.py&quot;, line 5, in &amp;lt;module&gt;\n    from .models import Category\nImportError: cannot import name 'Category' from 'todo_app.models' (\/var\/www\/server\/todo_app\/models.py)\n\n\n----------------------------------------------------------------------\nRan 1 test in 0.001s\n\nFAILED (errors=1)\n<\/pre><\/div>\n\n\n<p>Our test failed as expected since we don&#8217;t have category model yet to test so, lets write that model.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Category Model<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# \/server\/todo_app\/models.py\n\nfrom django.db import models\nfrom autoslug import AutoSlugField\nfrom django.utils import timezone\nfrom datetime import datetime\nfrom django.conf import settings\nfrom mptt.models import MPTTModel, TreeForeignKey\n\nclass Category(MPTTModel):\n  name = models.CharField(max_length=50, unique=True)\n  parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')\n  slug = AutoSlugField(populate_from='name', default=None)\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False) \n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;)) \n\n  def __str__(self): \n    return self.name \n\n  class MPTTMeta: \n    order_insertion_by = &#x5B;'name'] \n    unique_together = ('slug', 'parent') \n\n  class Meta: \n    verbose_name_plural = 'categories'\n<\/pre><\/div>\n\n\n<p>Note the import statement for mptt. If you copied and pasted the code from the requirements.txt file in chapter one, the Dockerfile installed all the packages that we will need for this tutorial including MPTT.<\/p>\n\n\n\n<p><strong>Install Django MPTT<\/strong><\/p>\n\n\n\n<p>Add mptt to our \/server\/todo_proj\/settings.py file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [11]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# \/server\/todo_proj\/settings.py\n\nINSTALLED_APPS = &#x5B;\n'django.contrib.admin',\n'django.contrib.auth',\n'django.contrib.contenttypes',\n'django.contrib.sessions',\n'django.contrib.messages',\n'django.contrib.staticfiles',\n'todo_app',\n'mptt',\n]\n<\/pre><\/div>\n\n\n<p>Run django migrations:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py makemigrations\ndocker-compose run server .\/manage.py migrate\n\n# reload the django settings\ndocker-compose down\ndocker-compose up -d database\ndocker-compose up -d server\n<\/pre><\/div>\n\n\n<p>Run our test again:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py test\n\nStarting postgres ... done\nCreating test database for alias 'default'...\nSystem check identified no issues (0 silenced).\n.\n----------------------------------------------------------------------\nRan 1 test in 0.003s\n\nOK\nDestroying test database for alias 'default'...\n<\/pre><\/div>\n\n\n<p>Our test passed. Excellent!<\/p>\n\n\n\n<p>Success! What&#8217;s next? Let&#8217;s write another test. This one for a Todo task using the same drill we used for the Category model.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Write the Todo Test<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [5,12,13,14,15]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_app\/tests.py\n\nfrom django.test import TestCase\n\nfrom todo_app.models import Category, Todo\n\nclass CategoryModelTest(TestCase):\n  def test_string_representation(self):\n    category = Category(name=&quot;my category name&quot;)\n    self.assertEqual(str(category), category.name)\n\nclass TodoModelTest(TestCase):\n  def test_string_representation(self):\n    todo = Todo(title=&quot;my todo title&quot;)\n    self.assertEqual(str(todo), todo.title)\n\n<\/pre><\/div>\n\n\n<p>Run the test:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py test\n<\/pre><\/div>\n\n\n<p>&#8230; and the test fails as expected:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nStarting postgres_dj02 ... done\nCreating test database for alias 'default'...\nSystem check identified no issues (0 silenced).\n.E\n======================================================================\nERROR: test_string_representation (todo_app.tests.TodoModelTest)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n  File &quot;\/var\/www\/server\/todo_app\/tests.py&quot;, line 15, in test_string_representation\n    todo = Todo(title=&quot;my todo title&quot;)\nNameError: name 'Todo' is not defined\n\n----------------------------------------------------------------------\nRan 2 tests in 0.021s\n\nFAILED (errors=1)\nDestroying test database for alias 'default'...\n<\/pre><\/div>\n\n\n<p>Now let&#8217;s create a model for Todo with a title. Also we need to add the <code>__str__<\/code> method so that our model returns the todo title. Our <code>models.py<\/code> file should look something like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [28,29,30,31,32]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nfrom django.db import models\n\nfrom django.db import models\nfrom autoslug import AutoSlugField\nfrom django.utils import timezone\nfrom datetime import datetime\nfrom django.conf import settings\nfrom mptt.models import MPTTModel, TreeForeignKey\n\nclass Category(MPTTModel):\n  name = models.CharField(max_length=50, unique=True)\n  parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')\n  slug = AutoSlugField(populate_from='name', default=None)\n\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n\n  def __str__(self):\n    return self.name\n\n  class MPTTMeta:\n    order_insertion_by = &#x5B;'name']\n    unique_together = ('slug', 'parent')\n\n  class Meta:\n    verbose_name_plural = 'categories'\n\nclass Todo(models.Model):\n  title = (models.CharField(max_length=500))\n\n  def __str__(self):\n    return self.title\n<\/pre><\/div>\n\n\n<p>With the Todo class defined we make and run migrations:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py makemigrations\ndocker-compose run server .\/manage.py migrate\n<\/pre><\/div>\n\n\n<p>Now our test should be successful:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py test\nStarting postgres_dj02 ... done\nCreating test database for alias 'default'...\nSystem check identified no issues (0 silenced).\n..\n----------------------------------------------------------------------\nRan 2 tests in 0.012s\n\nOK\nDestroying test database for alias 'default'...\n<\/pre><\/div>\n\n\n<p>Continuing on the same track we add additional Todo model fields to our tests.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [17,18,19,20,21,22,23,24,25,26,27]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_app\/tests.py\n\nfrom django.test import TestCase\n\nfrom todo_app.models import Category, Todo\n\nclass CategoryModelTest(TestCase):\n  def test_string_representation(self):\n    category = Category(name=&quot;my category name&quot;)\n    self.assertEqual(str(category), category.name)\n\nclass TodoModelTest(TestCase):\n  def test_string_representation(self):\n    todo = Todo(title=&quot;my todo title&quot;)\n    self.assertEqual(str(todo), todo.title)\n\n  def test_todo_field(self):\n    todo = Todo(\n      title = &quot;my todo title&quot;,\n      task = &quot;foo&quot;,\n      is_completed = False,\n      due_date = &quot;2020-06-24&quot;,\n    )\n    self.assertEqual(todo.title, &quot;my todo title&quot;)\n    self.assertEqual(todo.task, &quot;foo&quot;)\n    self.assertEqual(todo.is_completed, False)\n    self.assertEqual(todo.due_date, &quot;2020-06-24&quot;)\n\n<\/pre><\/div>\n\n\n<p>If we were to run tests again they would fail because these fields haven&#8217;t been defined in the model so, let&#8217;s define them:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [30,31,32,33,34,35,36]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nfrom django.db import models\n\nfrom django.db import models\nfrom autoslug import AutoSlugField\nfrom django.utils import timezone\nfrom datetime import datetime\nfrom django.conf import settings\nfrom mptt.models import MPTTModel, TreeForeignKey\n\nclass Category(MPTTModel):\n  name = models.CharField(max_length=50, unique=True)\n  parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')\n  slug = AutoSlugField(populate_from='name', default=None)\n\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n\n  def __str__(self):\n    return self.name\n\n  class MPTTMeta:\n    order_insertion_by = &#x5B;'name']\n    unique_together = ('slug', 'parent')\n\n  class Meta:\n    verbose_name_plural = 'categories'\n\nclass Todo(models.Model):\n  title = (models.CharField(max_length=500))\n  task = models.TextField(blank=True)\n  slug = AutoSlugField(populate_from='title', default=None)\n  is_completed = models.BooleanField(default=False)\n\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n  due_date = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n\n  def __str__(self):\n    return self.title\n<\/pre><\/div>\n\n\n<p>Run migrations again:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# we can combine the makemigrations and migrate docker-compose into one command\ndocker-compose run server .\/manage.py makemigrations &amp;amp;&amp;amp; docker-compose run server .\/manage.py migrate\n<\/pre><\/div>\n\n\n<p>Run our tests again and&#8230;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py test\n<\/pre><\/div>\n\n\n<p>&#8230; and voila! The tests run with 0 issues. Success!<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nStarting postgres_dj02 ... done\nCreating test database for alias 'default'Creating network &quot;todo_mptt_2_default&quot; with the default driver\nCreating postgres_dj02 ... done\nCreating dj02          ... done\nweb:todo_mptt_2 ronleeson$ docker-compose run server .\/manage.py test\nStarting postgres_dj02 ... done\nCreating test database for alias 'default'...\nSystem check identified no issues (0 silenced).\n...\n----------------------------------------------------------------------\nRan 3 tests in 0.014s\n\nOK\nDestroying test database for alias 'default'......\nSystem check identified no issues (0 silenced).\n..\n----------------------------------------------------------------------\nRan 2 tests in 0.014s\n\nOK\nDestroying test database for alias 'default'...\n<\/pre><\/div>\n\n\n<p>Full speed ahead we add the relationship between the Todo and the Category model:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [36]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# \/server\/todo_app\/models.py\n\nfrom django.db import models\n \nfrom django.db import models\nfrom autoslug import AutoSlugField\nfrom django.utils import timezone\nfrom datetime import datetime\nfrom django.conf import settings\nfrom mptt.models import MPTTModel, TreeForeignKey\n \nclass Category(MPTTModel):\n  name = models.CharField(max_length=50, unique=True)\n  parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')\n  slug = AutoSlugField(populate_from='name', default=None)\n \n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n \n  def __str__(self):\n    return self.name\n \n  class MPTTMeta:\n    order_insertion_by = &#x5B;'name']\n    unique_together = ('slug', 'parent')\n \n  class Meta:\n    verbose_name_plural = 'categories'\n \nclass Todo(models.Model):\n  title = (models.CharField(max_length=500))\n  task = models.TextField(blank=True)\n  slug = AutoSlugField(populate_from='title', default=None)\n  is_completed = models.BooleanField(default=False)\n\n  category = TreeForeignKey('Category', on_delete=models.CASCADE, null=True, blank=True, related_name='todos')\n \n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n  due_date = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n \n  def __str__(self):\n    return self.title\n<\/pre><\/div>\n\n\n<p>&#8230; and update the TodoModelTest in the tests.py file by defining a category object and assigning the object to our new Todo object<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [16,17,22,28]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# \/server\/todo_app\/tests.py\n\nfrom django.test import TestCase\n\nfrom todo_app.models import Category, Todo\n\n# ... code\n\nclass TodoModelTest(TestCase):\n  def test_string_representation(self):\n    todo = Todo(title=&quot;my todo title&quot;)\n    self.assertEqual(str(todo), todo.title)\n\n  def test_todo_field(self):\n\n    # create a category object\n    cat = Category(name=&quot;Frontend&quot;)\n\n    todo = Todo(\n      title = &quot;my todo title&quot;,\n      task = &quot;foo&quot;,\n      category = cat,\n      is_completed = False,\n      due_date = &quot;2020-06-24&quot;,\n    )\n    self.assertEqual(todo.title, &quot;my todo title&quot;)\n    self.assertEqual(todo.task, &quot;foo&quot;)\n    self.assertEqual(todo.category.name, &quot;Frontend&quot;)\n    self.assertEqual(todo.is_completed, False)\n    self.assertEqual(todo.due_date, &quot;2020-06-24&quot;)\n<\/pre><\/div>\n\n\n<p>Run migrations:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [21,22]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py makemigrations &amp;amp;&amp;amp; docker-compose run server .\/manage.py migrate\n\n# run tests again\ndocker-compose run server .\/manage.py test\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py test\nStarting postgres_dj02 ... done\nCreating test database for alias 'default'...\nSystem check identified no issues (0 silenced).\n...\n----------------------------------------------------------------------\nRan 3 tests in 0.019s\n\nOK\nDestroying test database for alias 'default'...\n<\/pre><\/div>\n\n\n<p>Success once again! Pushing on to our final model to test, Project. Update the tests.py file like so:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [34,35,36,37,38,39,40,41,42,43,44,45]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# \/server\/todo_app\/tests.py\n\nfrom django.test import TestCase\n\nfrom todo_app.models import Category, Todo, Project\n\nclass CategoryModelTest(TestCase):\n  def test_string_representation(self):\n    category = Category(name=&quot;my category name&quot;)\n    self.assertEqual(str(category), category.name)\n\nclass TodoModelTest(TestCase):\n  def test_string_representation(self):\n    todo = Todo(title=&quot;my todo title&quot;)\n    self.assertEqual(str(todo), todo.title)\n\n  def test_todo_field(self):\n    # create category object\n    cat = Category(name=&quot;Frontend&quot;)\n\n    todo = Todo(\n      title = &quot;my todo title&quot;,\n      task = &quot;foo&quot;,\n      category = cat,\n      is_completed = False,\n      due_date = &quot;2021-07-30&quot;,\n    )\n    self.assertEqual(todo.title, &quot;my todo title&quot;)\n    self.assertEqual(todo.task, &quot;foo&quot;)\n    self.assertEqual(todo.category.name, &quot;Frontend&quot;)\n    self.assertEqual(todo.is_completed, False)\n    self.assertEqual(todo.due_date, &quot;2021-07-30&quot;)\n\nclass ProjectModelTest(TestCase):\n  def test_string_representation(self):\n    project = Project(name = &quot;my project name&quot;)\n    self.assertEqual(str(project), project.name)\n\n  def test_project_field(self):\n    proj = Project(\n      name = &quot;my project name&quot;,\n      description = &quot;my project description&quot;,\n    )\n    self.assertEqual(proj.name, &quot;my project name&quot;)\n    self.assertEqual(proj.description, &quot;my project description&quot;)\n\n\n<\/pre><\/div>\n\n\n<p>&#8230; and this test will fail until we add our Project model which we can do now:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [45,46,47,48,49,50,51,52,53,54,55,56]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# \/server\/todo_app\/models.py\n\nfrom django.db import models\n\nfrom django.db import models\nfrom autoslug import AutoSlugField\nfrom django.utils import timezone\nfrom datetime import datetime\nfrom django.conf import settings\nfrom mptt.models import MPTTModel, TreeForeignKey\n\nclass Category(MPTTModel):\n  name = models.CharField(max_length=50, unique=True)\n  parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')\n  slug = AutoSlugField(populate_from='name', default=None)\n\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n\n  def __str__(self):\n    return self.name\n\n  class MPTTMeta:\n    order_insertion_by = &#x5B;'name']\n    unique_together = ('slug', 'parent')\n\n  class Meta:\n    verbose_name_plural = 'categories'\n\nclass Todo(models.Model):\n  title = (models.CharField(max_length=500))\n  task = models.TextField(blank=True)\n  slug = AutoSlugField(populate_from='title', default=None)\n  is_completed = models.BooleanField(default=False)\n\n  category = TreeForeignKey('Category', on_delete=models.CASCADE, null=True, blank=True, related_name='todos')\n\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n  due_date = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n\n  def __str__(self):\n    return self.title\n\nclass Project(models.Model):\n  name = models.CharField(max_length=500)\n  description = models.TextField(max_length=2500, blank=True, null=True, help_text=&quot;Description of your project.&quot;)\n  slug = AutoSlugField(populate_from='name', default=None)\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False) \n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;)) \n\n  def __str__(self): \n    return self.name \n\n  class Meta: \n    verbose_name_plural = 'projects'\n<\/pre><\/div>\n\n\n<p>Now that we have the Project model we can associate a foreign key relationship between Todo and Project.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [38]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# \/server\/todo_app\/models.py\n\nfrom django.db import models\n\nfrom django.db import models\nfrom autoslug import AutoSlugField\nfrom django.utils import timezone\nfrom datetime import datetime\nfrom django.conf import settings\nfrom mptt.models import MPTTModel, TreeForeignKey\n\nclass Category(MPTTModel):\n  name = models.CharField(max_length=50, unique=True)\n  parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')\n  slug = AutoSlugField(populate_from='name', default=None)\n\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n\n  def __str__(self):\n    return self.name\n\n  class MPTTMeta:\n    order_insertion_by = &#x5B;'name']\n    unique_together = ('slug', 'parent')\n\n  class Meta:\n    verbose_name_plural = 'categories'\n\nclass Todo(models.Model):\n  title = (models.CharField(max_length=500))\n  task = models.TextField(blank=True)\n  slug = AutoSlugField(populate_from='title', default=None)\n  is_completed = models.BooleanField(default=False)\n\n  category = TreeForeignKey('Category', on_delete=models.CASCADE, null=True, blank=True, related_name='todos')\n\n  project = models.ForeignKey('Project', related_name='todo_project', on_delete=models.CASCADE, null=True, blank=True)\n\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n  due_date = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;))\n\n  def __str__(self):\n    return self.title\n\nclass Project(models.Model):\n  name = models.CharField(max_length=500)\n  description = models.TextField(max_length=2500, blank=True, null=True, help_text=&quot;Description of your project.&quot;)\n  slug = AutoSlugField(populate_from='name', default=None)\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False) \n  created = models.DateField(default=timezone.now().strftime(&quot;%Y-%m-%d&quot;)) \n\n  def __str__(self): \n    return self.name \n\n  class Meta: \n    verbose_name_plural = 'projects'\n<\/pre><\/div>\n\n\n<p>Update tests.py to test the Todo model for the foreign key relationship with Project.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [5,21,22]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# \/server\/todo_app\/tests.py\n \nfrom django.test import TestCase\n \nfrom todo_app.models import Category, Todo, Project\n \nclass CategoryModelTest(TestCase):\n  def test_string_representation(self):\n    category = Category(name=&quot;my category name&quot;)\n    self.assertEqual(str(category), category.name)\n \nclass TodoModelTest(TestCase):\n  def test_string_representation(self):\n    todo = Todo(title=&quot;my todo title&quot;)\n    self.assertEqual(str(todo), todo.title)\n \n  def test_todo_field(self):\n    # create category object\n    cat = Category(name=&quot;Frontend&quot;)\n\n    # create project object\n    proj = Project(name=&quot;My Project&quot;)\n \n    todo = Todo(\n      title = &quot;my todo title&quot;,\n      task = &quot;foo&quot;,\n      category = cat,\n      project = proj,\n      is_completed = False,\n      due_date = &quot;2021-07-30&quot;,\n    )\n    self.assertEqual(todo.title, &quot;my todo title&quot;)\n    self.assertEqual(todo.task, &quot;foo&quot;)\n    self.assertEqual(todo.category.name, &quot;Frontend&quot;)\n    self.assertEqual(todo.project.name, &quot;My Project&quot;)\n    self.assertEqual(todo.is_completed, False)\n    self.assertEqual(todo.due_date, &quot;2021-07-30&quot;)\n \nclass ProjectModelTest(TestCase):\n  def test_string_representation(self):\n    project = Project(name = &quot;my project name&quot;)\n    self.assertEqual(str(project), project.name)\n \n  def test_project_field(self):\n    proj = Project(\n      name = &quot;my project name&quot;,\n      description = &quot;my project description&quot;,\n    )\n    self.assertEqual(proj.name, &quot;my project name&quot;)\n    self.assertEqual(proj.description, &quot;my project description&quot;)\n<\/pre><\/div>\n\n\n<p>&#8230; run migrations&#8230;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py makemigrations &amp;amp;&amp;amp; docker-compose run server .\/manage.py migrate\n<\/pre><\/div>\n\n\n<p>&#8230; run tests&#8230;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py test\nStarting postgres_dj02 ... done\nCreating test database for alias 'default'...\nSystem check identified no issues (0 silenced).\n.....\n----------------------------------------------------------------------\nRan 5 tests in 0.024s\n\nOK\nDestroying test database for alias 'default'...\n<\/pre><\/div>\n\n\n<p>&#8230; once again, success!<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Organize Our Tests<\/h4>\n\n\n\n<p>If we start writing tests for a complex project the tests.py file could get messy so, we will breakup our test file into separate files.  In this case we create a file just for model tests. In order to do this we will need to reorganize the todo_app directory.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# create a tests folder\nmkdir server\/todo_app\/tests\n\n# move the tests.py file into the new tests folder\nmv server\/todo_app\/tests.py server\/todo_app\/tests\/test_models.py\n\n# create the __init__.py so python treats directories containing it \/\n# as modules\ntouch server\/todo_app\/tests\/__init__.py\n<\/pre><\/div>\n\n\n<ul class=\"wp-block-list\"><li>todo_app<ul><li>tests [folder]<ul><li>__init__.py<\/li><\/ul><ul><li>test_models.py<\/li><\/ul><\/li><\/ul><\/li><\/ul>\n\n\n\n<p>Now we should be able to run our tests again and django will look in our tests directory for all our tests:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py test\nStarting postgres_dj02 ... done\nCreating test database for alias 'default'...\nSystem check identified no issues (0 silenced).\n.....\n----------------------------------------------------------------------\nRan 5 tests in 0.025s\n\nOK\nDestroying test database for alias 'default'...\n<\/pre><\/div>\n\n\n<p>And our tests run successfully!<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Loading Data from the Django Shell<\/h4>\n\n\n\n<p>Let&#8217;s enter the Django shell via docker-compose. Open a new terminal tab in our Todo App GraphQL project and enter the following:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py shell\n<\/pre><\/div>\n\n\n<p>This should kick us into Django shell. From here we can import our todo_app model and enter data for Project, Category, and Todo:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n&gt;&gt;&gt; from todo_app.models import Category, Project, Todo\n\n&gt;&gt;&gt; proj = Project(name='Project 1', description='Project 1 description')\n&gt;&gt;&gt; proj.save()\n\n&gt;&gt;&gt; cat = Category(name='Category 1')\n&gt;&gt;&gt; cat.save()\n\n&gt;&gt;&gt; todo = Todo(title='My First Todo', task='Do it', category=cat, project= proj)\n&gt;&gt;&gt; todo.save()\n\n&gt;&gt;&gt; quit()\n<\/pre><\/div>\n\n\n<p>Voila! The data is there. <\/p>\n\n\n\n<p>Lets see if the data is really there by using the Django admin UI.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Configure Django Admin<\/h4>\n\n\n\n<p>Add the following code to our <strong>todo-app-graphql\/server\/todo_app\/admin.py<\/strong> file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_app\/admin.py\n\nfrom django.contrib import admin\nfrom mptt.admin import MPTTModelAdmin\nfrom mptt.admin import DraggableMPTTAdmin\nfrom mptt.admin import TreeRelatedFieldListFilter\n\nfrom todo_app.models import( \n        Category, \n        Todo,\n        Project,\n    )\n\nadmin.site.register(\n    Category, \n    DraggableMPTTAdmin,\n    list_display=(\n        'tree_actions',\n        'indented_title',\n    ),\n    list_display_links=(\n        'indented_title',\n    )\n)\n\nclass ProjectAdmin(admin.ModelAdmin):\n    list_display = ('name', 'created', 'modified')\n\nadmin.site.register(Project, ProjectAdmin)\n\nclass TodoAdmin(admin.ModelAdmin):\n    model = Todo \n    list_filter = (\n        ('category', TreeRelatedFieldListFilter),\n    )\n    list_display = ('title', 'is_completed', 'created', 'modified')\n\nadmin.site.register(Todo, TodoAdmin)\n<\/pre><\/div>\n\n\n<p>Reboot the app:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose down &amp;amp;&amp;amp; docker-compose up -d\n<\/pre><\/div>\n\n\n<p>Now we can add data for Category, Project and Todo from <a href=\"http:\/\/localhost:5555\/admin\/todo_app\/todo\/\" target=\"_blank\" rel=\"noreferrer noopener\">http:\/\/localhost:5555\/admin\/todo_app\/todo\/<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"397\" src=\"http:\/\/10.0.0.14:5556\/wp-content\/uploads\/2021\/12\/django_admin_ch2-1024x397.jpg\" alt=\"\" class=\"wp-image-2728\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/12\/django_admin_ch2-1024x397.jpg 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/12\/django_admin_ch2-300x116.jpg 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/12\/django_admin_ch2-768x298.jpg 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/12\/django_admin_ch2-600x233.jpg 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/12\/django_admin_ch2-945x366.jpg 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/12\/django_admin_ch2.jpg 1145w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Coming Up Part 3: TDD Django Graphene<\/h4>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"tdd-with-django-graphene-docker-part-1-setup-the-django-development-environment\">&lt; Previous: Part 1<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"tdd-with-django-docker-part-3-django-graphene\">Next: Part 3 &gt;<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>TDD of Django API In Part 1 we dockerized our Django Todo api. In this tutorial we are going to use Test Driven Development to build out the Todo api. Architecture of the API Our objective is to build a&#8230; <a class=\"more-link\" href=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/2021\/12\/22\/tdd-with-gatsby-django-docker-part-2-tdd-with-django\/\">Continue Reading &rarr;<\/a><\/p>\n","protected":false},"author":1,"featured_media":312,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,20],"tags":[15,47,29,35],"class_list":["post-2708","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-django","category-featured-tutorial","tag-django","tag-mptt","tag-tdd","tag-unit-testing"],"_links":{"self":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/2708","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/comments?post=2708"}],"version-history":[{"count":22,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/2708\/revisions"}],"predecessor-version":[{"id":2757,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/2708\/revisions\/2757"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media\/312"}],"wp:attachment":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media?parent=2708"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/categories?post=2708"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/tags?post=2708"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}