Repository: nautobot/nautobot

Issue:
Create a job with IntegerVar and having the default set to 0 evaluates to False.
<!--
    NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.

    This form is only for reporting reproducible bugs. If you need assistance
    with Nautobot installation, or if you have a general question, please start a
    discussion instead: https://github.com/nautobot/nautobot/discussions

    Please describe the environment in which you are running Nautobot. Be sure
    that you are running an unmodified instance of the latest stable release
    before submitting a bug report, and that any plugins have been disabled.
-->
### Environment
* Python version: python3.9
* Nautobot version: 1.3.7

<!--
    Describe in detail the exact steps that someone else can take to reproduce
    this bug using the current stable release of Nautobot. Begin with the
    creation of any necessary database objects and call out every operation
    being performed explicitly. If reporting a bug in the REST API, be sure to
    reconstruct the raw HTTP request(s) being made: Don't rely on a client
    library such as pynautobot.
-->
### Steps to Reproduce
1. create a new job with a variable definition of `IntegerVar(description="timer", default=0, required=True, min_value=0, max_value=3600)`
2. Load the job in the UI, default will be set to `None` and you must manually set 0.


<!-- What did you expect to happen? -->
### Expected Behavior
Default is used from the variable definition

<!-- What happened instead? -->
### Observed Behavior
Using IntegerVar in conjunction with default=0 will evaulate to False and the default will not be displayed.

Fix could be:
`if default is not None:` 
here:
https://github.com/nautobot/nautobot/blob/69b691443cbeb8843aee1915b8956d003b9d994a/nautobot/extras/jobs.py#L611
Create a job with IntegerVar and having the default set to 0 evaluates to False.
<!--
    NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.

    This form is only for reporting reproducible bugs. If you need assistance
    with Nautobot installation, or if you have a general question, please start a
    discussion instead: https://github.com/nautobot/nautobot/discussions

    Please describe the environment in which you are running Nautobot. Be sure
    that you are running an unmodified instance of the latest stable release
    before submitting a bug report, and that any plugins have been disabled.
-->
### Environment
* Python version: python3.9
* Nautobot version: 1.3.7

<!--
    Describe in detail the exact steps that someone else can take to reproduce
    this bug using the current stable release of Nautobot. Begin with the
    creation of any necessary database objects and call out every operation
    being performed explicitly. If reporting a bug in the REST API, be sure to
    reconstruct the raw HTTP request(s) being made: Don't rely on a client
    library such as pynautobot.
-->
### Steps to Reproduce
1. create a new job with a variable definition of `IntegerVar(description="timer", default=0, required=True, min_value=0, max_value=3600)`
2. Load the job in the UI, default will be set to `None` and you must manually set 0.


<!-- What did you expect to happen? -->
### Expected Behavior
Default is used from the variable definition

<!-- What happened instead? -->
### Observed Behavior
Using IntegerVar in conjunction with default=0 will evaulate to False and the default will not be displayed.

Fix could be:
`if default is not None:` 
here:
https://github.com/nautobot/nautobot/blob/69b691443cbeb8843aee1915b8956d003b9d994a/nautobot/extras/jobs.py#L611


Discussion:
Can be assigned to me

Can be assigned to me


Resolving patch:
diff --git a/nautobot/extras/jobs.py b/nautobot/extras/jobs.py
--- a/nautobot/extras/jobs.py
+++ b/nautobot/extras/jobs.py
@@ -608,7 +608,7 @@ def __init__(self, label="", description="", default=None, required=True, widget
             self.field_attrs["label"] = label
         if description:
             self.field_attrs["help_text"] = description
-        if default:
+        if default is not None:
             self.field_attrs["initial"] = default
         if widget:
             self.field_attrs["widget"] = widget


Test patch:
diff --git a/nautobot/extras/tests/example_jobs/test_field_default.py b/nautobot/extras/tests/example_jobs/test_field_default.py
new file mode 100644
--- /dev/null
+++ b/nautobot/extras/tests/example_jobs/test_field_default.py
@@ -0,0 +1,15 @@
+from nautobot.extras.jobs import IntegerVar, Job
+
+
+class TestFieldDefault(Job):
+    """My job demo."""
+
+    var_int = IntegerVar(default=0, min_value=0, max_value=3600, description="Test default of 0 Falsey")
+    var_int_no_default = IntegerVar(
+        required=False, min_value=0, max_value=3600, description="Test default without default"
+    )
+
+    class Meta:
+        """Metaclass attrs."""
+
+        field_order = ["var_int", "var_int_no_default"]
diff --git a/nautobot/extras/tests/test_filters.py b/nautobot/extras/tests/test_filters.py
--- a/nautobot/extras/tests/test_filters.py
+++ b/nautobot/extras/tests/test_filters.py
@@ -3,8 +3,17 @@
 from django.contrib.auth import get_user_model
 from django.contrib.contenttypes.models import ContentType
 from django.test import TestCase
-
-from nautobot.dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer, Platform, Rack, Region, Site
+from nautobot.dcim.models import (
+    Device,
+    DeviceRole,
+    DeviceType,
+    Interface,
+    Manufacturer,
+    Platform,
+    Rack,
+    Region,
+    Site,
+)
 from nautobot.extras.choices import (
     ObjectChangeActionChoices,
     SecretsGroupAccessTypeChoices,
@@ -24,8 +33,8 @@
     RelationshipAssociationFilterSet,
     RelationshipFilterSet,
     SecretFilterSet,
-    SecretsGroupFilterSet,
     SecretsGroupAssociationFilterSet,
+    SecretsGroupFilterSet,
     StatusFilterSet,
     TagFilterSet,
     WebhookFilterSet,
@@ -55,7 +64,6 @@
 from nautobot.utilities.choices import ColorChoices
 from nautobot.virtualization.models import Cluster, ClusterGroup, ClusterType
 
-
 # Use the proper swappable User model
 User = get_user_model()
 
@@ -602,13 +610,13 @@ def test_grouping(self):
 
     def test_installed(self):
         params = {"installed": True}
-        # 31 local jobs and 3 plugin jobs
-        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 34)
+        # 32 local jobs and 3 plugin jobs
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 35)
 
     def test_enabled(self):
         params = {"enabled": False}
-        # 31 local jobs and 3 plugin jobs
-        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 34)
+        # 32 local jobs and 3 plugin jobs
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 35)
 
     def test_commit_default(self):
         params = {"commit_default": False}
diff --git a/nautobot/extras/tests/test_jobs.py b/nautobot/extras/tests/test_jobs.py
--- a/nautobot/extras/tests/test_jobs.py
+++ b/nautobot/extras/tests/test_jobs.py
@@ -1,23 +1,26 @@
 import json
-from io import StringIO
 import re
 import uuid
+from io import StringIO
 
+from django.contrib.auth import get_user_model
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.core.management import call_command
 from django.core.management.base import CommandError
-from django.contrib.auth import get_user_model
 from django.test import override_settings
 from django.test.client import RequestFactory
-
 from nautobot.dcim.models import DeviceRole, Site
 from nautobot.extras.choices import JobResultStatusChoices, LogLevelChoices
 from nautobot.extras.jobs import get_job, run_job
-from nautobot.extras.models import FileProxy, Job, Status, CustomField, JobResult
+from nautobot.extras.models import CustomField, FileProxy, Job, JobResult, Status
 from nautobot.extras.models.models import JobLogEntry
-from nautobot.utilities.testing import CeleryTestCase, TransactionTestCase, run_job_for_testing
+from nautobot.utilities.testing import (
+    CeleryTestCase,
+    TransactionTestCase,
+    run_job_for_testing,
+)
 
 # Use the proper swappable User model
 User = get_user_model()
@@ -119,6 +122,29 @@ def test_job_fail(self):
         job_result = create_job_result_and_run_job(module, name, commit=False)
         self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_ERRORED)
 
+    def test_field_default(self):
+        """
+        Job test with field that is a default value that is falsey.
+        https://github.com/nautobot/nautobot/issues/2039
+        """
+        module = "test_field_default"
+        name = "TestFieldDefault"
+        job_class = get_job(f"local/{module}/{name}")
+        form = job_class().as_form()
+
+        self.assertHTMLEqual(
+            form.as_table(),
+            """<tr><th><label for="id_var_int">Var int:</label></th><td>
+<input class="form-control form-control" id="id_var_int" max="3600" name="var_int" placeholder="None" required type="number" value="0">
+<br><span class="helptext">Test default of 0 Falsey</span></td></tr>
+<tr><th><label for="id_var_int_no_default">Var int no default:</label></th><td>
+<input class="form-control form-control" id="id_var_int_no_default" max="3600" name="var_int_no_default" placeholder="None" type="number">
+<br><span class="helptext">Test default without default</span></td></tr>
+<tr><th><label for="id__commit">Commit changes:</label></th><td>
+<input checked id="id__commit" name="_commit" placeholder="Commit changes" type="checkbox">
+<br><span class="helptext">Commit changes to the database (uncheck for a dry-run)</span></td></tr>""",
+        )
+
     def test_field_order(self):
         """
         Job test with field order.