nautobot 2.4.0__py3-none-any.whl → 2.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/core/celery/schedulers.py +1 -1
- nautobot/core/filters.py +48 -21
- nautobot/core/jobs/bulk_actions.py +56 -19
- nautobot/core/models/__init__.py +2 -0
- nautobot/core/tables.py +5 -1
- nautobot/core/testing/filters.py +25 -13
- nautobot/core/testing/integration.py +86 -4
- nautobot/core/tests/test_filters.py +209 -246
- nautobot/core/tests/test_jobs.py +250 -93
- nautobot/core/tests/test_models.py +9 -0
- nautobot/core/views/generic.py +80 -48
- nautobot/core/views/mixins.py +34 -6
- nautobot/dcim/api/serializers.py +2 -2
- nautobot/dcim/constants.py +6 -13
- nautobot/dcim/factory.py +6 -1
- nautobot/dcim/tests/integration/test_device_bulk_delete.py +189 -0
- nautobot/dcim/tests/integration/test_device_bulk_edit.py +181 -0
- nautobot/dcim/tests/test_api.py +0 -2
- nautobot/dcim/tests/test_models.py +42 -28
- nautobot/extras/forms/mixins.py +1 -1
- nautobot/extras/jobs.py +15 -6
- nautobot/extras/templatetags/job_buttons.py +4 -4
- nautobot/extras/tests/test_forms.py +13 -0
- nautobot/extras/tests/test_jobs.py +18 -13
- nautobot/extras/tests/test_models.py +6 -0
- nautobot/extras/tests/test_views.py +4 -3
- nautobot/ipam/tests/test_api.py +20 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +36 -1
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/release-notes/version-2.4.html +108 -0
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +288 -288
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/wireless/tests/test_views.py +22 -1
- {nautobot-2.4.0.dist-info → nautobot-2.4.1.dist-info}/METADATA +2 -2
- {nautobot-2.4.0.dist-info → nautobot-2.4.1.dist-info}/RECORD +40 -38
- {nautobot-2.4.0.dist-info → nautobot-2.4.1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.1.dist-info}/NOTICE +0 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.1.dist-info}/WHEEL +0 -0
- {nautobot-2.4.0.dist-info → nautobot-2.4.1.dist-info}/entry_points.txt +0 -0
nautobot/core/tests/test_jobs.py
CHANGED
|
@@ -8,6 +8,7 @@ from django.core.files.base import ContentFile
|
|
|
8
8
|
from django.utils import timezone
|
|
9
9
|
import yaml
|
|
10
10
|
|
|
11
|
+
from nautobot.circuits.models import Circuit, CircuitType, Provider
|
|
11
12
|
from nautobot.core.jobs.cleanup import CleanupTypes
|
|
12
13
|
from nautobot.core.testing import create_job_result_and_run_job, TransactionTestCase
|
|
13
14
|
from nautobot.core.testing.context import load_event_broker_override_settings
|
|
@@ -27,7 +28,7 @@ from nautobot.extras.models import (
|
|
|
27
28
|
Tag,
|
|
28
29
|
)
|
|
29
30
|
from nautobot.extras.models.metadata import ObjectMetadata
|
|
30
|
-
from nautobot.ipam.models import Namespace, Prefix
|
|
31
|
+
from nautobot.ipam.models import IPAddress, Namespace, Prefix
|
|
31
32
|
from nautobot.users.models import ObjectPermission
|
|
32
33
|
|
|
33
34
|
|
|
@@ -619,6 +620,7 @@ class BulkEditTestCase(TransactionTestCase):
|
|
|
619
620
|
self.status_ct = ContentType.objects.get_for_model(Status)
|
|
620
621
|
self.namespace_ct = ContentType.objects.get_for_model(Namespace)
|
|
621
622
|
self.role_ct = ContentType.objects.get_for_model(Role)
|
|
623
|
+
self.ipaddress_ct = ContentType.objects.get_for_model(IPAddress)
|
|
622
624
|
self.tags = [Tag.objects.create(name=f"Example Tag {x}") for x in range(5)]
|
|
623
625
|
for tag in self.tags:
|
|
624
626
|
tag.content_types.add(self.namespace_ct)
|
|
@@ -633,7 +635,7 @@ class BulkEditTestCase(TransactionTestCase):
|
|
|
633
635
|
JobLogEntry.objects.filter(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR).exists()
|
|
634
636
|
)
|
|
635
637
|
|
|
636
|
-
def
|
|
638
|
+
def test_bulk_edit_objects_without_permission(self):
|
|
637
639
|
statuses = [Status.objects.create(name=f"Sample Status {x}") for x in range(3)]
|
|
638
640
|
pk_list = [str(status.id) for status in statuses]
|
|
639
641
|
job_result = create_job_result_and_run_job(
|
|
@@ -654,45 +656,72 @@ class BulkEditTestCase(TransactionTestCase):
|
|
|
654
656
|
status.refresh_from_db()
|
|
655
657
|
self.assertNotEqual(status.color, "aa1409")
|
|
656
658
|
|
|
657
|
-
def
|
|
658
|
-
|
|
659
|
+
def test_bulk_edit_objects_with_constrained_permission(self):
|
|
660
|
+
roles_to_update = [
|
|
661
|
+
Role.objects.create(name="Example Role 1"),
|
|
662
|
+
Role.objects.create(name="Example Role 2"),
|
|
663
|
+
]
|
|
664
|
+
pk_list = [str(role.pk) for role in roles_to_update]
|
|
665
|
+
|
|
666
|
+
obj_perm = ObjectPermission.objects.create(
|
|
667
|
+
name="Test permission",
|
|
668
|
+
constraints={"pk": pk_list[0]},
|
|
669
|
+
actions=["change", "view"],
|
|
670
|
+
)
|
|
671
|
+
obj_perm.users.add(self.user)
|
|
672
|
+
obj_perm.object_types.add(self.role_ct)
|
|
673
|
+
|
|
659
674
|
job_result = create_job_result_and_run_job(
|
|
660
675
|
"nautobot.core.jobs.bulk_actions",
|
|
661
676
|
"BulkEditObjects",
|
|
662
677
|
content_type=self.role_ct.id,
|
|
663
|
-
edit_all=
|
|
664
|
-
filter_query_params={},
|
|
665
|
-
form_data={"color": "aa1409"},
|
|
678
|
+
edit_all=False,
|
|
679
|
+
filter_query_params={"per_page": 2},
|
|
680
|
+
form_data={"pk": pk_list, "color": "aa1409"},
|
|
666
681
|
username=self.user.username,
|
|
667
682
|
)
|
|
668
|
-
|
|
683
|
+
error_log = JobLogEntry.objects.get(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR)
|
|
684
|
+
self.assertIn("Form validation unsuccessful", error_log.message)
|
|
685
|
+
self.assertIn(f"{roles_to_update[1].pk} is not one of the available choices.", error_log.message)
|
|
686
|
+
roles_to_update[0].refresh_from_db()
|
|
687
|
+
roles_to_update[1].refresh_from_db()
|
|
688
|
+
self.assertNotEqual(roles_to_update[0].color, "aa1409")
|
|
689
|
+
self.assertNotEqual(roles_to_update[1].color, "aa1409")
|
|
690
|
+
|
|
691
|
+
obj_perm.constraints = {"pk__in": pk_list}
|
|
692
|
+
obj_perm.save()
|
|
669
693
|
|
|
670
|
-
def test_bulk_edit_filter_all(self):
|
|
671
|
-
self.add_permissions("extras.change_status", "extras.view_status")
|
|
672
|
-
# By default Active and Available are some of the example of Status that starts with A
|
|
673
|
-
statuses = Status.objects.filter(name__istartswith="A")
|
|
674
|
-
status_to_ignore = Status.objects.create(name="Ignore Example Status")
|
|
675
|
-
self.assertNotEqual(statuses.count(), 0)
|
|
676
694
|
job_result = create_job_result_and_run_job(
|
|
677
695
|
"nautobot.core.jobs.bulk_actions",
|
|
678
696
|
"BulkEditObjects",
|
|
679
|
-
content_type=self.
|
|
680
|
-
edit_all=
|
|
681
|
-
filter_query_params={"
|
|
682
|
-
|
|
683
|
-
form_data={
|
|
684
|
-
"pk": [str(statuses[0].pk)],
|
|
685
|
-
"color": "aa1409",
|
|
686
|
-
"_all": "True",
|
|
687
|
-
},
|
|
697
|
+
content_type=self.role_ct.id,
|
|
698
|
+
edit_all=False,
|
|
699
|
+
filter_query_params={"sort": "name"},
|
|
700
|
+
form_data={"pk": pk_list, "color": "aa1409"},
|
|
688
701
|
username=self.user.username,
|
|
689
702
|
)
|
|
690
|
-
self._common_no_error_test_assertion(
|
|
691
|
-
|
|
703
|
+
self._common_no_error_test_assertion(Role, job_result, 2, pk__in=pk_list, color="aa1409")
|
|
704
|
+
|
|
705
|
+
def test_bulk_edit_objects_select_all(self):
|
|
706
|
+
"""
|
|
707
|
+
Bulk edit all Role instances.
|
|
708
|
+
"""
|
|
709
|
+
self.add_permissions("extras.change_role", "extras.view_role")
|
|
710
|
+
job_result = create_job_result_and_run_job(
|
|
711
|
+
"nautobot.core.jobs.bulk_actions",
|
|
712
|
+
"BulkEditObjects",
|
|
713
|
+
content_type=self.role_ct.id,
|
|
714
|
+
edit_all=True,
|
|
715
|
+
filter_query_params={},
|
|
716
|
+
form_data={"color": "aa1409"},
|
|
717
|
+
username=self.user.username,
|
|
692
718
|
)
|
|
693
|
-
self.
|
|
719
|
+
self._common_no_error_test_assertion(Role, job_result, Role.objects.all().count(), color="aa1409")
|
|
694
720
|
|
|
695
|
-
def
|
|
721
|
+
def test_bulk_edit_select_some(self):
|
|
722
|
+
"""
|
|
723
|
+
Bulk edit selected Namespace instances.
|
|
724
|
+
"""
|
|
696
725
|
self.add_permissions("ipam.change_namespace", "ipam.view_namespace", "extras.change_tag", "extras.view_tag")
|
|
697
726
|
namespaces = [Namespace.objects.create(name=f"Sample Namespace {x}") for x in range(5)]
|
|
698
727
|
for namespace in namespaces:
|
|
@@ -731,63 +760,170 @@ class BulkEditTestCase(TransactionTestCase):
|
|
|
731
760
|
self.assertFalse(namespace.tags.filter(pk__in=[tag.pk for tag in self.tags[:3]]).exists())
|
|
732
761
|
self.assertTrue(namespace.tags.filter(pk__in=[tag.pk for tag in self.tags[3:]]).exists())
|
|
733
762
|
|
|
734
|
-
def
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
763
|
+
def test_bulk_edit_objects_filter_all(self):
|
|
764
|
+
"""
|
|
765
|
+
Bulk edit all of the filtered Status instances.
|
|
766
|
+
"""
|
|
767
|
+
self.add_permissions("extras.change_status", "extras.view_status")
|
|
768
|
+
# By default Active and Available are some of the example of Status that starts with A
|
|
769
|
+
statuses = Status.objects.filter(name__istartswith="A")
|
|
770
|
+
status_to_ignore = Status.objects.create(name="Ignore Example Status")
|
|
771
|
+
self.assertNotEqual(statuses.count(), 0)
|
|
772
|
+
job_result = create_job_result_and_run_job(
|
|
773
|
+
"nautobot.core.jobs.bulk_actions",
|
|
774
|
+
"BulkEditObjects",
|
|
775
|
+
content_type=self.status_ct.id,
|
|
776
|
+
edit_all=True,
|
|
777
|
+
filter_query_params={"name__isw": "A"},
|
|
778
|
+
form_data={
|
|
779
|
+
"color": "aa1409",
|
|
780
|
+
"_all": "True",
|
|
781
|
+
},
|
|
782
|
+
username=self.user.username,
|
|
745
783
|
)
|
|
746
|
-
|
|
747
|
-
|
|
784
|
+
self._common_no_error_test_assertion(
|
|
785
|
+
Status, job_result, statuses.count(), name__istartswith="A", color="aa1409"
|
|
786
|
+
)
|
|
787
|
+
self.assertNotEqual(status_to_ignore.color, "aa1409")
|
|
748
788
|
|
|
789
|
+
def test_bulk_edit_objects_filter_some(self):
|
|
790
|
+
"""
|
|
791
|
+
Bulk edit some of the filtered Status instances.
|
|
792
|
+
"""
|
|
793
|
+
self.add_permissions("extras.change_status", "extras.view_status")
|
|
794
|
+
# By default Active and Available are some of the example of Status that starts with A
|
|
795
|
+
statuses = Status.objects.filter(name__istartswith="A")
|
|
796
|
+
status_to_ignore = Status.objects.create(name="Ignore Example Status")
|
|
797
|
+
self.assertNotEqual(statuses.count(), 0)
|
|
749
798
|
job_result = create_job_result_and_run_job(
|
|
750
799
|
"nautobot.core.jobs.bulk_actions",
|
|
751
800
|
"BulkEditObjects",
|
|
752
|
-
content_type=self.
|
|
801
|
+
content_type=self.status_ct.id,
|
|
753
802
|
edit_all=False,
|
|
754
|
-
filter_query_params={},
|
|
755
|
-
form_data={
|
|
803
|
+
filter_query_params={"name__isw": "A", "sort": "name"},
|
|
804
|
+
form_data={
|
|
805
|
+
"pk": [str(statuses[0].pk)],
|
|
806
|
+
"color": "aa1409",
|
|
807
|
+
},
|
|
756
808
|
username=self.user.username,
|
|
757
809
|
)
|
|
758
|
-
|
|
759
|
-
self.
|
|
760
|
-
self.assertIn(f"{roles_to_update[1].pk} is not one of the available choices.", error_log.message)
|
|
761
|
-
roles_to_update[0].refresh_from_db()
|
|
762
|
-
roles_to_update[1].refresh_from_db()
|
|
763
|
-
self.assertNotEqual(roles_to_update[0].color, "aa1409")
|
|
764
|
-
self.assertNotEqual(roles_to_update[1].color, "aa1409")
|
|
765
|
-
|
|
766
|
-
obj_perm.constraints = {"pk__in": pk_list}
|
|
767
|
-
obj_perm.save()
|
|
810
|
+
self._common_no_error_test_assertion(Status, job_result, 1, name__istartswith="A", color="aa1409")
|
|
811
|
+
self.assertNotEqual(status_to_ignore.color, "aa1409")
|
|
768
812
|
|
|
813
|
+
def test_bulk_edit_objects_passing_in_both_pk_list_and_edit_all(self):
|
|
814
|
+
"""
|
|
815
|
+
edit_all should override pk if both are passed.
|
|
816
|
+
"""
|
|
817
|
+
self.add_permissions("extras.change_status", "extras.view_status")
|
|
818
|
+
# By default Active and Available are some of the example of Status that starts with A
|
|
819
|
+
statuses = Status.objects.filter(name__istartswith="A")
|
|
820
|
+
status_to_ignore = Status.objects.create(name="Ignore Example Status")
|
|
821
|
+
self.assertNotEqual(statuses.count(), 0)
|
|
769
822
|
job_result = create_job_result_and_run_job(
|
|
770
823
|
"nautobot.core.jobs.bulk_actions",
|
|
771
824
|
"BulkEditObjects",
|
|
772
|
-
content_type=self.
|
|
773
|
-
edit_all=
|
|
774
|
-
filter_query_params={},
|
|
775
|
-
|
|
825
|
+
content_type=self.status_ct.id,
|
|
826
|
+
edit_all=True,
|
|
827
|
+
filter_query_params={"name__isw": "A"},
|
|
828
|
+
# pk ignored if edit_all is True
|
|
829
|
+
form_data={
|
|
830
|
+
"pk": [str(statuses[0].pk)],
|
|
831
|
+
"color": "aa1409",
|
|
832
|
+
"_all": "True",
|
|
833
|
+
},
|
|
776
834
|
username=self.user.username,
|
|
777
835
|
)
|
|
778
|
-
self._common_no_error_test_assertion(
|
|
836
|
+
self._common_no_error_test_assertion(
|
|
837
|
+
Status, job_result, statuses.count(), name__istartswith="A", color="aa1409"
|
|
838
|
+
)
|
|
839
|
+
self.assertNotEqual(status_to_ignore.color, "aa1409")
|
|
840
|
+
|
|
841
|
+
def test_bulk_edit_objects_updating_the_same_field_as_the_filter_param(self):
|
|
842
|
+
"""
|
|
843
|
+
Test bulk edit where the filter query param and the field to update are the same.
|
|
844
|
+
e.g.
|
|
845
|
+
form_data = {"status": "Test Deprecated"}
|
|
846
|
+
filter_params = {"status": "Test Active"}
|
|
847
|
+
"""
|
|
848
|
+
self.add_permissions("ipam.change_ipaddress", "extras.view_status")
|
|
849
|
+
# By default Active and Available are some of the example of Status that starts with A
|
|
850
|
+
active_status = Status.objects.create(name="Test Active")
|
|
851
|
+
deprecated_status = Status.objects.create(name="Test Deprecated")
|
|
852
|
+
active_status.content_types.add(self.ipaddress_ct)
|
|
853
|
+
deprecated_status.content_types.add(self.ipaddress_ct)
|
|
854
|
+
IPAddress.objects.all().update(status=active_status)
|
|
855
|
+
self.assertEqual(IPAddress.objects.all().count(), IPAddress.objects.filter(status=active_status).count())
|
|
856
|
+
|
|
857
|
+
# Update all IPAddresses with status=active_status to deprecated_statuses with no filters
|
|
858
|
+
create_job_result_and_run_job(
|
|
859
|
+
"nautobot.core.jobs.bulk_actions",
|
|
860
|
+
"BulkEditObjects",
|
|
861
|
+
content_type=self.ipaddress_ct.id,
|
|
862
|
+
edit_all=True,
|
|
863
|
+
# pk ignored if edit_all is True
|
|
864
|
+
form_data={
|
|
865
|
+
"status": str(deprecated_status.pk),
|
|
866
|
+
"_all": "True",
|
|
867
|
+
},
|
|
868
|
+
username=self.user.username,
|
|
869
|
+
)
|
|
870
|
+
self.assertEqual(IPAddress.objects.all().count(), IPAddress.objects.filter(status=deprecated_status).count())
|
|
871
|
+
# Update all IPAddresses with status=active_status to deprecated_statuses with status filter applied
|
|
872
|
+
create_job_result_and_run_job(
|
|
873
|
+
"nautobot.core.jobs.bulk_actions",
|
|
874
|
+
"BulkEditObjects",
|
|
875
|
+
content_type=self.ipaddress_ct.id,
|
|
876
|
+
edit_all=True,
|
|
877
|
+
filter_query_params={"status": "Test Deprecated"},
|
|
878
|
+
# pk ignored if edit_all is True
|
|
879
|
+
form_data={
|
|
880
|
+
"status": str(active_status.pk),
|
|
881
|
+
"per_page": 1,
|
|
882
|
+
"_all": "True",
|
|
883
|
+
},
|
|
884
|
+
username=self.user.username,
|
|
885
|
+
)
|
|
886
|
+
self.assertEqual(IPAddress.objects.all().count(), IPAddress.objects.filter(status=active_status).count())
|
|
779
887
|
|
|
780
888
|
|
|
781
889
|
class BulkDeleteTestCase(TransactionTestCase):
|
|
782
890
|
"""
|
|
783
|
-
Test the
|
|
891
|
+
Test the BulkDeleteObjects system job.
|
|
784
892
|
"""
|
|
785
893
|
|
|
786
894
|
def setUp(self):
|
|
787
895
|
super().setUp()
|
|
788
896
|
self.status_ct = ContentType.objects.get_for_model(Status)
|
|
789
897
|
self.role_ct = ContentType.objects.get_for_model(Role)
|
|
790
|
-
|
|
898
|
+
for x in range(10):
|
|
899
|
+
Status.objects.create(name=f"Example Status {x}")
|
|
900
|
+
|
|
901
|
+
statuses = Status.objects.get_for_model(Circuit)
|
|
902
|
+
circuit_type = CircuitType.objects.create(
|
|
903
|
+
name="Example Circuit Type",
|
|
904
|
+
)
|
|
905
|
+
provider = Provider.objects.create(
|
|
906
|
+
name="Example Provider",
|
|
907
|
+
)
|
|
908
|
+
|
|
909
|
+
Circuit.objects.create(
|
|
910
|
+
cid="Circuit 1",
|
|
911
|
+
provider=provider,
|
|
912
|
+
circuit_type=circuit_type,
|
|
913
|
+
status=statuses[0],
|
|
914
|
+
)
|
|
915
|
+
Circuit.objects.create(
|
|
916
|
+
cid="Circuit 2",
|
|
917
|
+
provider=provider,
|
|
918
|
+
circuit_type=circuit_type,
|
|
919
|
+
status=statuses[0],
|
|
920
|
+
)
|
|
921
|
+
Circuit.objects.create(
|
|
922
|
+
cid="Circuit 3",
|
|
923
|
+
provider=provider,
|
|
924
|
+
circuit_type=circuit_type,
|
|
925
|
+
status=statuses[0],
|
|
926
|
+
)
|
|
791
927
|
|
|
792
928
|
def _common_no_error_test_assertion(self, model, job_result, **filter_params):
|
|
793
929
|
self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_SUCCESS)
|
|
@@ -799,7 +935,7 @@ class BulkDeleteTestCase(TransactionTestCase):
|
|
|
799
935
|
JobLogEntry.objects.filter(job_result=job_result, log_level=LogLevelChoices.LOG_ERROR).exists()
|
|
800
936
|
)
|
|
801
937
|
|
|
802
|
-
def
|
|
938
|
+
def test_bulk_delete_objects_without_permission(self):
|
|
803
939
|
statuses_to_delete = [str(status) for status in Status.objects.all().values_list("pk", flat=True)[:2]]
|
|
804
940
|
job_result = create_job_result_and_run_job(
|
|
805
941
|
"nautobot.core.jobs.bulk_actions",
|
|
@@ -838,61 +974,66 @@ class BulkDeleteTestCase(TransactionTestCase):
|
|
|
838
974
|
)
|
|
839
975
|
self.assertEqual(Status.objects.filter(pk__in=statuses_to_delete).count(), len(statuses_to_delete))
|
|
840
976
|
|
|
841
|
-
def
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
self.
|
|
977
|
+
def test_bulk_delete_objects_select_all(self):
|
|
978
|
+
"""
|
|
979
|
+
Delete all Circuits objects.
|
|
980
|
+
"""
|
|
981
|
+
self.add_permissions("circuits.delete_circuit")
|
|
982
|
+
# Assert Circuit is not empty
|
|
983
|
+
self.assertNotEqual(Circuit.objects.all().count(), 0)
|
|
846
984
|
|
|
847
985
|
job_result = create_job_result_and_run_job(
|
|
848
986
|
"nautobot.core.jobs.bulk_actions",
|
|
849
987
|
"BulkDeleteObjects",
|
|
850
|
-
content_type=ContentType.objects.get_for_model(
|
|
988
|
+
content_type=ContentType.objects.get_for_model(Circuit).id,
|
|
851
989
|
delete_all=True,
|
|
852
|
-
filter_query_params={},
|
|
990
|
+
filter_query_params={"per_page": 10},
|
|
853
991
|
pk_list=[],
|
|
854
992
|
username=self.user.username,
|
|
855
993
|
)
|
|
856
|
-
self._common_no_error_test_assertion(
|
|
857
|
-
|
|
858
|
-
def test_bulk_delete_filter_all(self):
|
|
859
|
-
self.add_permissions("extras.delete_status")
|
|
860
|
-
for x in range(10):
|
|
861
|
-
Status.objects.create(name=f"Example Status {x}")
|
|
994
|
+
self._common_no_error_test_assertion(Circuit, job_result)
|
|
862
995
|
|
|
863
|
-
|
|
996
|
+
def test_bulk_delete_objects_select_some(self):
|
|
997
|
+
"""
|
|
998
|
+
Delete some of the Role objects with no filter queries applied.
|
|
999
|
+
"""
|
|
1000
|
+
self.add_permissions("extras.delete_role")
|
|
1001
|
+
roles_to_delete = [Role.objects.create(name=f"Example Role {x}") for x in range(3)]
|
|
1002
|
+
roles_to_ignore = Role.objects.create(name="Ignore Example Role")
|
|
1003
|
+
roles_pks = [str(role.pk) for role in roles_to_delete]
|
|
864
1004
|
job_result = create_job_result_and_run_job(
|
|
865
1005
|
"nautobot.core.jobs.bulk_actions",
|
|
866
1006
|
"BulkDeleteObjects",
|
|
867
|
-
content_type=self.
|
|
868
|
-
delete_all=
|
|
869
|
-
filter_query_params={
|
|
1007
|
+
content_type=self.role_ct.id,
|
|
1008
|
+
delete_all=False,
|
|
1009
|
+
filter_query_params={},
|
|
1010
|
+
pk_list=roles_pks,
|
|
870
1011
|
username=self.user.username,
|
|
871
1012
|
)
|
|
872
|
-
self._common_no_error_test_assertion(
|
|
873
|
-
self.assertTrue(
|
|
1013
|
+
self._common_no_error_test_assertion(Role, job_result, pk__in=roles_pks)
|
|
1014
|
+
self.assertTrue(Role.objects.filter(name=roles_to_ignore.name).exists())
|
|
874
1015
|
|
|
875
|
-
def
|
|
1016
|
+
def test_bulk_delete_objects_filter_all(self):
|
|
1017
|
+
"""
|
|
1018
|
+
Delete all of the Status objects that start with "Example Status".
|
|
1019
|
+
"""
|
|
876
1020
|
self.add_permissions("extras.delete_status")
|
|
877
|
-
|
|
1021
|
+
status_to_ignore = Status.objects.create(name="Ignore Example Status")
|
|
878
1022
|
job_result = create_job_result_and_run_job(
|
|
879
1023
|
"nautobot.core.jobs.bulk_actions",
|
|
880
1024
|
"BulkDeleteObjects",
|
|
881
1025
|
content_type=self.status_ct.id,
|
|
882
1026
|
delete_all=True,
|
|
883
|
-
filter_query_params={"name__isw": "Example Status"},
|
|
884
|
-
pk_list=[str(Status.objects.first().pk)],
|
|
1027
|
+
filter_query_params={"name__isw": "Example Status", "sort": "name"},
|
|
885
1028
|
username=self.user.username,
|
|
886
1029
|
)
|
|
887
|
-
self.
|
|
888
|
-
|
|
889
|
-
self.assertEqual(
|
|
890
|
-
job_log.message,
|
|
891
|
-
"You can either delete objs within `pk_list` provided or `delete_all` with `filter_query_params` if needed.",
|
|
892
|
-
)
|
|
893
|
-
self.assertEqual(status_count, Status.objects.all().count())
|
|
1030
|
+
self._common_no_error_test_assertion(Status, job_result, name__istartswith="Example Status")
|
|
1031
|
+
self.assertTrue(Status.objects.filter(name=status_to_ignore.name).exists())
|
|
894
1032
|
|
|
895
|
-
def
|
|
1033
|
+
def test_bulk_delete_objects_filter_some(self):
|
|
1034
|
+
"""
|
|
1035
|
+
Delete some of the Role objects that start with "Example Status".
|
|
1036
|
+
"""
|
|
896
1037
|
self.add_permissions("extras.delete_role")
|
|
897
1038
|
roles_to_delete = [Role.objects.create(name=f"Example Role {x}") for x in range(3)]
|
|
898
1039
|
roles_to_ignore = Role.objects.create(name="Ignore Example Role")
|
|
@@ -902,9 +1043,25 @@ class BulkDeleteTestCase(TransactionTestCase):
|
|
|
902
1043
|
"BulkDeleteObjects",
|
|
903
1044
|
content_type=self.role_ct.id,
|
|
904
1045
|
delete_all=False,
|
|
905
|
-
filter_query_params={},
|
|
1046
|
+
filter_query_params={"name__isw": "Example Status"},
|
|
906
1047
|
pk_list=roles_pks,
|
|
907
1048
|
username=self.user.username,
|
|
908
1049
|
)
|
|
909
1050
|
self._common_no_error_test_assertion(Role, job_result, pk__in=roles_pks)
|
|
910
1051
|
self.assertTrue(Role.objects.filter(name=roles_to_ignore.name).exists())
|
|
1052
|
+
|
|
1053
|
+
def test_bulk_delete_objects_passing_both_pk_list_and_delete_all(self):
|
|
1054
|
+
"""
|
|
1055
|
+
delete_all should override pk_list if both are passed.
|
|
1056
|
+
"""
|
|
1057
|
+
self.add_permissions("extras.delete_status")
|
|
1058
|
+
job_result = create_job_result_and_run_job(
|
|
1059
|
+
"nautobot.core.jobs.bulk_actions",
|
|
1060
|
+
"BulkDeleteObjects",
|
|
1061
|
+
content_type=self.status_ct.pk,
|
|
1062
|
+
delete_all=True,
|
|
1063
|
+
filter_query_params={"name__isw": "Example Status", "sort": "name"},
|
|
1064
|
+
pk_list=[str(Status.objects.first().pk)],
|
|
1065
|
+
username=self.user.username,
|
|
1066
|
+
)
|
|
1067
|
+
self._common_no_error_test_assertion(Role, job_result, name__istartswith="Example Status")
|
|
@@ -72,6 +72,15 @@ class NaturalKeyTestCase(BaseModelTest):
|
|
|
72
72
|
dt = DeviceType.objects.first()
|
|
73
73
|
self.assertEqual(dt.natural_key(), [dt.manufacturer.name, dt.model])
|
|
74
74
|
|
|
75
|
+
def test_natural_key_with_proxy_model(self):
|
|
76
|
+
"""Test that natural_key_field_lookups function returns the same value as its base class."""
|
|
77
|
+
|
|
78
|
+
class ProxyManufacturer(Manufacturer):
|
|
79
|
+
class Meta:
|
|
80
|
+
proxy = True
|
|
81
|
+
|
|
82
|
+
self.assertEqual(ProxyManufacturer.natural_key_field_lookups, Manufacturer.natural_key_field_lookups)
|
|
83
|
+
|
|
75
84
|
@skip("Composite keys aren't being supported at this time")
|
|
76
85
|
def test_composite_key(self):
|
|
77
86
|
"""Test the composite_key default implementation with some representative models."""
|