nautobot 1.6.26__py3-none-any.whl → 1.6.27__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.
@@ -7,6 +7,7 @@ from django.contrib.contenttypes.models import ContentType
7
7
  from django.core.exceptions import ValidationError
8
8
  from django.core.management.base import BaseCommand, CommandError
9
9
  from django.db import models
10
+ from django.db.models.fields.json import KeyTransform
10
11
 
11
12
  from nautobot.circuits.models import Circuit
12
13
  from nautobot.dcim.models import (
@@ -376,42 +377,61 @@ def check_permissions_constraints(command):
376
377
  continue
377
378
 
378
379
  for lookup in qs._query.where.children:
379
- related_model = lookup.lhs.target.related_model or lookup.lhs.target.model
380
-
381
- if related_model in replaced_models:
382
- cls = f"{related_model.__module__}.{related_model.__name__}"
383
- warnings.append(
384
- f"ObjectPermission '{perm}' (id: {perm.id}) has a constraint that references "
385
- f"a model ({cls}) that will be migrated to a new model by the Nautobot 2.0 migration.\n"
386
- + json.dumps(perm.constraints, indent=4)
387
- )
388
-
389
- if related_model in field_change_models:
390
- field_name = lookup.lhs.field.name
391
- if field_name in field_change_models[related_model]:
380
+ lhs = lookup.lhs
381
+
382
+ # Check if the left-hand side (lhs) of the lookup is a KeyTransform.
383
+ # KeyTransform is used for accessing nested keys in JSON fields.
384
+ if isinstance(lhs, KeyTransform):
385
+ # If lhs is a KeyTransform, it means we're dealing with a JSON field lookup usually a CustomField.
386
+ # `lhs.source_expressions` returns a list of `Col` objects representing the columns involved in the lookup.
387
+ cols = lhs.source_expressions
388
+ # - `col.field.name` gives the base field name (_custom_field_data).
389
+ # - `lhs.key_name` provides the specific custom field name (test_custom_field).
390
+ # Construct the field name by combining these with a double underscore.
391
+ suffix = f"__{lhs.key_name}"
392
+ else:
393
+ # If lhs is not a KeyTransform, it is a direct column reference.
394
+ # Wrap lhs in a list to handle it uniformly in the next step.
395
+ cols = [lhs]
396
+ suffix = ""
397
+
398
+ for col in cols:
399
+ field_name = f"{col.field.name}{suffix}"
400
+ related_model = col.target.related_model or col.target.model
401
+
402
+ if related_model in replaced_models:
392
403
  cls = f"{related_model.__module__}.{related_model.__name__}"
393
404
  warnings.append(
394
405
  f"ObjectPermission '{perm}' (id: {perm.id}) has a constraint that references "
395
- f"a model field ({cls}.{field_name}) that may be changed by the Nautobot 2.0 migration.\n"
406
+ f"a model ({cls}) that will be migrated to a new model by the Nautobot 2.0 migration.\n"
396
407
  + json.dumps(perm.constraints, indent=4)
397
408
  )
398
409
 
399
- if lookup.lhs.field.name == "slug" and related_model is not GitRepository:
400
- warnings.append(
401
- f"ObjectPermission '{perm}' (id: {perm.id}) has a constraint that references "
402
- f"a 'slug' field that will be deleted by the Nautobot 2.0 migration.\n"
403
- + json.dumps(perm.constraints, indent=4)
404
- )
410
+ if related_model in field_change_models:
411
+ if col.field.name in field_change_models[related_model]:
412
+ cls = f"{related_model.__module__}.{related_model.__name__}"
413
+ warnings.append(
414
+ f"ObjectPermission '{perm}' (id: {perm.id}) has a constraint that references "
415
+ f"a model field ({cls}.{field_name}) that may be changed by the Nautobot 2.0 migration.\n"
416
+ + json.dumps(perm.constraints, indent=4)
417
+ )
405
418
 
406
- if related_model in pk_change_models:
407
- if isinstance(lookup.lhs.target, (models.fields.related.RelatedField, models.fields.UUIDField)):
408
- cls = f"{related_model.__module__}.{related_model.__name__}"
419
+ if field_name == "slug" and related_model is not GitRepository:
409
420
  warnings.append(
410
421
  f"ObjectPermission '{perm}' (id: {perm.id}) has a constraint that references "
411
- f"an object ({cls}) that may have its primary key changed by the Nautobot 2.0 migration.\n"
422
+ f"a 'slug' field that will be deleted by the Nautobot 2.0 migration.\n"
412
423
  + json.dumps(perm.constraints, indent=4)
413
424
  )
414
425
 
426
+ if related_model in pk_change_models:
427
+ if isinstance(col.target, (models.fields.related.RelatedField, models.fields.UUIDField)):
428
+ cls = f"{related_model.__module__}.{related_model.__name__}"
429
+ warnings.append(
430
+ f"ObjectPermission '{perm}' (id: {perm.id}) has a constraint that references "
431
+ f"an object ({cls}) that may have its primary key changed by the Nautobot 2.0 migration.\n"
432
+ + json.dumps(perm.constraints, indent=4)
433
+ )
434
+
415
435
  if warnings:
416
436
  msg = """
417
437
  One or more permission constraints may be affected by the Nautobot 2.0 migration.
@@ -5,6 +5,11 @@ from django.core.management import call_command
5
5
  from django.core.management.base import CommandError
6
6
  from django.contrib.contenttypes.models import ContentType
7
7
 
8
+ from nautobot.dcim.models.devices import Device
9
+ from nautobot.dcim.models.sites import Site
10
+ from nautobot.extras.choices import CustomFieldTypeChoices
11
+ from nautobot.extras.models.customfields import CustomField
12
+ from nautobot.users.models import ObjectPermission
8
13
  from nautobot.utilities.testing import TestCase
9
14
  from nautobot.dcim.models import VirtualChassis
10
15
  from nautobot.extras.datasources.registry import get_datasource_contents
@@ -25,6 +30,17 @@ class PreMigrateCommandTest(TestCase):
25
30
  )
26
31
  self.git_repo.save(trigger_resync=False)
27
32
 
33
+ # Adding this test data to assert that https://github.com/nautobot/nautobot/issues/6081 is fixed
34
+ ct = ContentType.objects.get_for_model(GitRepository)
35
+ custom_field = CustomField.objects.create(type=CustomFieldTypeChoices.TYPE_TEXT, name="test_custom_field")
36
+ custom_field.content_types.add(ct)
37
+ self.obj_perm = ObjectPermission.objects.create(
38
+ name="Test permission",
39
+ constraints={"_custom_field_data__test_custom_field__in": ["test-1", "test-2"]},
40
+ actions=["view"],
41
+ )
42
+ self.obj_perm.object_types.add(ct)
43
+
28
44
  def run_command(self, *args):
29
45
  out = StringIO()
30
46
  err = StringIO()
@@ -42,6 +58,8 @@ class PreMigrateCommandTest(TestCase):
42
58
  out, err = self.run_command()
43
59
 
44
60
  self.assertIn("All pre-migration checks passed.", out)
61
+ # Assert Permission constrain warning not logged
62
+ self.assertNotIn(f"ObjectPermission '{self.obj_perm.name}'", out)
45
63
  self.assertEqual("", err)
46
64
 
47
65
  def test_configcontext_failure(self):
@@ -80,3 +98,22 @@ class PreMigrateCommandTest(TestCase):
80
98
 
81
99
  with self.assertRaises(CommandError):
82
100
  self.run_command()
101
+
102
+ def test_permission_constraints_failure(self):
103
+ """Test permission constraints logs warning when needed for CustomField."""
104
+ device_ct = ContentType.objects.get_for_model(Device)
105
+ site_ct = ContentType.objects.get_for_model(Site)
106
+ custom_field = CustomField.objects.create(type=CustomFieldTypeChoices.TYPE_TEXT, name="custom_field")
107
+ custom_field.content_types.add(site_ct)
108
+ obj_perm = ObjectPermission.objects.create(
109
+ name="Test permission 2",
110
+ constraints={"site___custom_field_data__custom_field__in": ["test-1", "test-2"]},
111
+ actions=["view"],
112
+ )
113
+ obj_perm.object_types.add(device_ct)
114
+
115
+ out, _ = self.run_command()
116
+ self.assertIn(
117
+ f"ObjectPermission 'Test permission 2' (id: {obj_perm.pk}) has a constraint that references a model (nautobot.dcim.models.sites.Site) that will be migrated to a new model by the Nautobot 2.0 migration.",
118
+ out,
119
+ )