|
2 | 2 |
|
3 | 3 | from typing import Any, Dict, Iterable, List, Mapping, MutableMapping, Optional, Set, Type |
4 | 4 |
|
5 | | -from diffsync import DiffSync, DiffSyncModel |
| 5 | +from diffsync import Adapter, DiffSyncModel |
6 | 6 | from diffsync.enum import DiffSyncModelFlags |
7 | 7 | from django.contrib.contenttypes.models import ContentType |
8 | 8 | from django.db.models import Max |
9 | 9 | from django.db.transaction import atomic |
10 | 10 | from nautobot.core.utils.lookup import get_model_from_name |
11 | 11 | from nautobot.extras.models import Tag |
| 12 | +from pydantic import Field, create_model |
12 | 13 |
|
13 | 14 | from nautobot_netbox_importer.base import FieldName, RecordData, logger |
14 | | -from nautobot_netbox_importer.summary import ( |
15 | | - ImporterIssue, |
16 | | - ImportSummary, |
17 | | - NautobotModelStats, |
18 | | - NautobotModelSummary, |
19 | | - get_issue_tag, |
20 | | -) |
21 | | - |
22 | | -from .base import ( |
| 15 | +from nautobot_netbox_importer.generator.base import ( |
23 | 16 | AUTO_ADD_FIELDS, |
24 | 17 | EMPTY_VALUES, |
25 | 18 | INTERNAL_AUTO_INC_TYPES, |
|
34 | 27 | InternalFieldType, |
35 | 28 | NautobotBaseModel, |
36 | 29 | NautobotBaseModelType, |
37 | | - PydanticField, |
38 | 30 | StrToInternalFieldType, |
39 | 31 | Uid, |
40 | 32 | get_nautobot_field_and_type, |
41 | 33 | normalize_datetime, |
42 | 34 | source_pk_to_uuid, |
43 | 35 | ) |
44 | | -from .exceptions import NautobotModelNotFound |
| 36 | +from nautobot_netbox_importer.generator.exceptions import NautobotModelNotFound |
| 37 | +from nautobot_netbox_importer.summary import ( |
| 38 | + ImporterIssue, |
| 39 | + ImportSummary, |
| 40 | + NautobotModelStats, |
| 41 | + NautobotModelSummary, |
| 42 | + get_issue_tag, |
| 43 | +) |
45 | 44 |
|
46 | 45 | # Helper to determine the import order of models. |
47 | 46 | # Due to dependencies among Nautobot models, certain models must be imported first to ensure successful `instance.save()` calls without errors. |
@@ -265,48 +264,50 @@ def diffsync_class(self) -> Type["DiffSyncBaseModel"]: |
265 | 264 | if self.disabled: |
266 | 265 | raise RuntimeError("Cannot create importer for disabled model") |
267 | 266 |
|
268 | | - annotations = {} |
269 | 267 | attributes = [] |
| 268 | + field_definitions = {} |
270 | 269 | identifiers = [] |
271 | 270 |
|
272 | | - class_definition = { |
273 | | - "__annotations__": annotations, |
274 | | - "_attributes": attributes, |
275 | | - "_identifiers": identifiers, |
276 | | - "_modelname": self.content_type.replace(".", "_"), |
277 | | - "_wrapper": self, |
278 | | - } |
279 | | - |
280 | | - annotations[self.pk_field.name] = INTERNAL_TYPE_TO_ANNOTATION[self.pk_field.internal_type] |
| 271 | + pk_type = INTERNAL_TYPE_TO_ANNOTATION[self.pk_field.internal_type] |
| 272 | + field_definitions[self.pk_field.name] = (pk_type, Field()) |
281 | 273 | identifiers.append(self.pk_field.name) |
282 | | - class_definition[self.pk_field.name] = PydanticField() |
283 | 274 |
|
284 | 275 | for field in self.fields.values(): |
285 | 276 | if field.name in identifiers or not field.can_import: |
286 | 277 | continue |
287 | 278 |
|
288 | 279 | if field.is_reference: |
289 | 280 | related_type = StrToInternalFieldType[field.related_meta.pk.get_internal_type()] # type: ignore |
290 | | - annotation = INTERNAL_TYPE_TO_ANNOTATION[related_type] |
| 281 | + field_type = INTERNAL_TYPE_TO_ANNOTATION[related_type] |
291 | 282 | if field.internal_type == InternalFieldType.MANY_TO_MANY_FIELD: |
292 | | - annotation = Set[annotation] |
| 283 | + field_type = Set[field_type] |
293 | 284 | else: |
294 | | - annotation = INTERNAL_TYPE_TO_ANNOTATION[field.internal_type] |
| 285 | + field_type = INTERNAL_TYPE_TO_ANNOTATION[field.internal_type] |
| 286 | + |
| 287 | + field_type = Optional[field_type] |
295 | 288 |
|
| 289 | + field_definitions[field.name] = (field_type, Field(default=None)) |
296 | 290 | attributes.append(field.name) |
297 | | - if not field.required: |
298 | | - annotation = Optional[annotation] |
299 | | - annotations[field.name] = annotation |
300 | | - class_definition[field.name] = PydanticField(default=None) |
| 291 | + |
| 292 | + model_name = self.content_type.replace(".", "_") |
301 | 293 |
|
302 | 294 | try: |
303 | | - result = type(class_definition["_modelname"], (DiffSyncBaseModel,), class_definition) |
| 295 | + result = create_model(model_name, __base__=DiffSyncBaseModel, **field_definitions) |
| 296 | + # pylint: disable=protected-access |
| 297 | + result._modelname = model_name |
| 298 | + # pylint: disable=protected-access |
| 299 | + result._attributes = tuple(attributes) |
| 300 | + # pylint: disable=protected-access |
| 301 | + result._identifiers = tuple(identifiers) |
| 302 | + # pylint: disable=protected-access |
| 303 | + result._wrapper = self |
| 304 | + |
304 | 305 | self._diffsync_class = result |
305 | 306 | except Exception: |
306 | | - logger.error("Failed to create DiffSync Model %s", class_definition, exc_info=True) |
| 307 | + logger.error("Failed to create DiffSync Model %s", field_definitions, exc_info=True) |
307 | 308 | raise |
308 | 309 |
|
309 | | - logger.debug("Created DiffSync Model %s", class_definition) |
| 310 | + logger.debug("Created DiffSync result %s", field_definitions) |
310 | 311 |
|
311 | 312 | return result |
312 | 313 |
|
@@ -770,14 +771,14 @@ class DiffSyncBaseModel(DiffSyncModel): |
770 | 771 | _wrapper: NautobotModelWrapper |
771 | 772 |
|
772 | 773 | @classmethod |
773 | | - def create(cls, diffsync: DiffSync, ids: dict, attrs: dict) -> Optional["DiffSyncBaseModel"]: |
| 774 | + def create(cls, adapter: Adapter, ids: dict, attrs: dict) -> Optional["DiffSyncBaseModel"]: |
774 | 775 | """Create this model instance, both in Nautobot and in DiffSync.""" |
775 | 776 | wrapper = cls._wrapper |
776 | 777 |
|
777 | 778 | instance = None |
778 | 779 | try: |
779 | 780 | instance = wrapper.model(**wrapper.constructor_kwargs, **ids) |
780 | | - result = super().create(diffsync, ids, attrs) |
| 781 | + result = super().create(adapter, ids, attrs) |
781 | 782 | # pylint: disable=broad-exception-caught |
782 | 783 | except Exception as error: |
783 | 784 | wrapper.add_issue( |
|
0 commit comments