prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  `nautobot.core.api.serializers.WritableNestedSerializer` docstring is incorrect and confusing
  <!--
      NOTE: This template is for use by maintainers only. Please do not submit
      an issue using this template unless you have been specifically asked to
      do so.
  -->
  ### Proposed Changes
  The docstring should read something like this:
  > Returns a nested representation of an object on read, but accepts either the nested representation or just the PK value on write operations.
  
  <!-- Provide justification for the proposed change(s). -->
  ### Justification
  The current docstring is in conflict with the actual implementation by stating that only the PK value is supported on write, which is incorrect.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `nautobot/core/api/serializers.py`
  Content:
  ```
  1 import uuid
  2 
  3 from django.core.exceptions import (
  4     FieldError,
  5     MultipleObjectsReturned,
  6     ObjectDoesNotExist,
  7 )
  8 from django.db.models import AutoField, ManyToManyField
  9 from drf_yasg.utils import swagger_serializer_method
  10 from rest_framework import serializers
  11 from rest_framework.exceptions import ValidationError
  12 
  13 from nautobot.utilities.utils import dict_to_filter_params
  14 
  15 
  16 class BaseModelSerializer(serializers.ModelSerializer):
  17     """
  18     This base serializer implements common fields and logic for all ModelSerializers.
  19     Namely it defines the `display` field which exposes a human friendly value for the given object.
  20     """
  21 
  22     display = serializers.SerializerMethodField(read_only=True, help_text="Human friendly display value")
  23 
  24     @swagger_serializer_method(serializer_or_field=serializers.CharField)
  25     def get_display(self, instance):
  26         """
  27         Return either the `display` property of the instance or `str(instance)`
  28         """
  29         return getattr(instance, "display", str(instance))
  30 
  31     def get_field_names(self, declared_fields, info):
  32         """
  33         Override get_field_names() to append the `display` field so it is always included in the
  34         serializer's `Meta.fields`.
  35 
  36         DRF does not automatically add declared fields to `Meta.fields`, nor does it require that declared fields
  37         on a super class be included in `Meta.fields` to allow for a subclass to include only a subset of declared
  38         fields from the super. This means either we intercept and append the display field at this level, or
  39         enforce by convention that all consumers of BaseModelSerializer include `display` in their `Meta.fields`
  40         which would surely lead to errors of omission; therefore we have chosen the former approach.
  41         """
  42         fields = list(super().get_field_names(declared_fields, info))  # Meta.fields could be defined as a tuple
  43         fields.append("display")
  44 
  45         return fields
  46 
  47 
  48 class ValidatedModelSerializer(BaseModelSerializer):
  49     """
  50     Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during
  51     validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)
  52     """
  53 
  54     def validate(self, data):
  55 
  56         # Remove custom fields data and tags (if any) prior to model validation
  57         attrs = data.copy()
  58         attrs.pop("custom_fields", None)
  59         attrs.pop("tags", None)
  60 
  61         # Skip ManyToManyFields
  62         for field in self.Meta.model._meta.get_fields():
  63             if isinstance(field, ManyToManyField):
  64                 attrs.pop(field.name, None)
  65 
  66         # Run clean() on an instance of the model
  67         if self.instance is None:
  68             instance = self.Meta.model(**attrs)
  69         else:
  70             instance = self.instance
  71             for k, v in attrs.items():
  72                 setattr(instance, k, v)
  73         instance.full_clean()
  74 
  75         return data
  76 
  77 
  78 class WritableNestedSerializer(BaseModelSerializer):
  79     """
  80     Returns a nested representation of an object on read, but accepts only a primary key on write.
  81     """
  82 
  83     def to_internal_value(self, data):
  84 
  85         if data is None:
  86             return None
  87 
  88         # Dictionary of related object attributes
  89         if isinstance(data, dict):
  90             params = dict_to_filter_params(data)
  91             queryset = self.Meta.model.objects
  92             try:
  93                 return queryset.get(**params)
  94             except ObjectDoesNotExist:
  95                 raise ValidationError("Related object not found using the provided attributes: {}".format(params))
  96             except MultipleObjectsReturned:
  97                 raise ValidationError("Multiple objects match the provided attributes: {}".format(params))
  98             except FieldError as e:
  99                 raise ValidationError(e)
  100 
  101         queryset = self.Meta.model.objects
  102         pk = None
  103 
  104         if isinstance(self.Meta.model._meta.pk, AutoField):
  105             # PK is an int for this model. This is usually the User model
  106             try:
  107                 pk = int(data)
  108             except (TypeError, ValueError):
  109                 raise ValidationError(
  110                     "Related objects must be referenced by ID or by dictionary of attributes. Received an "
  111                     "unrecognized value: {}".format(data)
  112                 )
  113 
  114         else:
  115             # We assume a type of UUIDField for all other models
  116 
  117             # PK of related object
  118             try:
  119                 # Ensure the pk is a valid UUID
  120                 pk = uuid.UUID(str(data))
  121             except (TypeError, ValueError):
  122                 raise ValidationError(
  123                     "Related objects must be referenced by ID or by dictionary of attributes. Received an "
  124                     "unrecognized value: {}".format(data)
  125                 )
  126 
  127         try:
  128             return queryset.get(pk=pk)
  129         except ObjectDoesNotExist:
  130             raise ValidationError("Related object not found using the provided ID: {}".format(pk))
  131 
  132 
  133 class BulkOperationSerializer(serializers.Serializer):
  134     id = serializers.CharField()  # This supports both UUIDs and numeric ID for the User model
  135 
  136 
  137 #
  138 # GraphQL, used by the openapi doc, not by the view
  139 #
  140 
  141 
  142 class GraphQLAPISerializer(serializers.Serializer):
  143     query = serializers.CharField(required=True, help_text="GraphQL query")
  144     variables = serializers.JSONField(required=False, help_text="Variables in JSON Format")
  145
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/nautobot/core/api/serializers.py b/nautobot/core/api/serializers.py
  --- a/nautobot/core/api/serializers.py
  +++ b/nautobot/core/api/serializers.py
  @@ -77,7 +77,8 @@
   
   class WritableNestedSerializer(BaseModelSerializer):
       """
  -    Returns a nested representation of an object on read, but accepts only a primary key on write.
  +    Returns a nested representation of an object on read, but accepts either the nested representation or the
  +    primary key value on write operations.
       """
   
       def to_internal_value(self, data):
  
verification_info: {"golden_diff": "diff --git a/nautobot/core/api/serializers.py b/nautobot/core/api/serializers.py\n--- a/nautobot/core/api/serializers.py\n+++ b/nautobot/core/api/serializers.py\n@@ -77,7 +77,8 @@\n \n class WritableNestedSerializer(BaseModelSerializer):\n     \"\"\"\n-    Returns a nested representation of an object on read, but accepts only a primary key on write.\n+    Returns a nested representation of an object on read, but accepts either the nested representation or the\n+    primary key value on write operations.\n     \"\"\"\n \n     def to_internal_value(self, data):\n", "issue": "`nautobot.core.api.serializers.WritableNestedSerializer` docstring is incorrect and confusing\n<!--\r\n    NOTE: This template is for use by maintainers only. Please do not submit\r\n    an issue using this template unless you have been specifically asked to\r\n    do so.\r\n-->\r\n### Proposed Changes\r\nThe docstring should read something like this:\r\n> Returns a nested representation of an object on read, but accepts either the nested representation or just the PK value on write operations.\r\n\r\n<!-- Provide justification for the proposed change(s). -->\r\n### Justification\r\nThe current docstring is in conflict with the actual implementation by stating that only the PK value is supported on write, which is incorrect.\n", "before_files": [{"content": "import uuid\n\nfrom django.core.exceptions import (\n    FieldError,\n    MultipleObjectsReturned,\n    ObjectDoesNotExist,\n)\nfrom django.db.models import AutoField, ManyToManyField\nfrom drf_yasg.utils import swagger_serializer_method\nfrom rest_framework import serializers\nfrom rest_framework.exceptions import ValidationError\n\nfrom nautobot.utilities.utils import dict_to_filter_params\n\n\nclass BaseModelSerializer(serializers.ModelSerializer):\n    \"\"\"\n    This base serializer implements common fields and logic for all ModelSerializers.\n    Namely it defines the `display` field which exposes a human friendly value for the given object.\n    \"\"\"\n\n    display = serializers.SerializerMethodField(read_only=True, help_text=\"Human friendly display value\")\n\n    @swagger_serializer_method(serializer_or_field=serializers.CharField)\n    def get_display(self, instance):\n        \"\"\"\n        Return either the `display` property of the instance or `str(instance)`\n        \"\"\"\n        return getattr(instance, \"display\", str(instance))\n\n    def get_field_names(self, declared_fields, info):\n        \"\"\"\n        Override get_field_names() to append the `display` field so it is always included in the\n        serializer's `Meta.fields`.\n\n        DRF does not automatically add declared fields to `Meta.fields`, nor does it require that declared fields\n        on a super class be included in `Meta.fields` to allow for a subclass to include only a subset of declared\n        fields from the super. This means either we intercept and append the display field at this level, or\n        enforce by convention that all consumers of BaseModelSerializer include `display` in their `Meta.fields`\n        which would surely lead to errors of omission; therefore we have chosen the former approach.\n        \"\"\"\n        fields = list(super().get_field_names(declared_fields, info))  # Meta.fields could be defined as a tuple\n        fields.append(\"display\")\n\n        return fields\n\n\nclass ValidatedModelSerializer(BaseModelSerializer):\n    \"\"\"\n    Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\n    validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)\n    \"\"\"\n\n    def validate(self, data):\n\n        # Remove custom fields data and tags (if any) prior to model validation\n        attrs = data.copy()\n        attrs.pop(\"custom_fields\", None)\n        attrs.pop(\"tags\", None)\n\n        # Skip ManyToManyFields\n        for field in self.Meta.model._meta.get_fields():\n            if isinstance(field, ManyToManyField):\n                attrs.pop(field.name, None)\n\n        # Run clean() on an instance of the model\n        if self.instance is None:\n            instance = self.Meta.model(**attrs)\n        else:\n            instance = self.instance\n            for k, v in attrs.items():\n                setattr(instance, k, v)\n        instance.full_clean()\n\n        return data\n\n\nclass WritableNestedSerializer(BaseModelSerializer):\n    \"\"\"\n    Returns a nested representation of an object on read, but accepts only a primary key on write.\n    \"\"\"\n\n    def to_internal_value(self, data):\n\n        if data is None:\n            return None\n\n        # Dictionary of related object attributes\n        if isinstance(data, dict):\n            params = dict_to_filter_params(data)\n            queryset = self.Meta.model.objects\n            try:\n                return queryset.get(**params)\n            except ObjectDoesNotExist:\n                raise ValidationError(\"Related object not found using the provided attributes: {}\".format(params))\n            except MultipleObjectsReturned:\n                raise ValidationError(\"Multiple objects match the provided attributes: {}\".format(params))\n            except FieldError as e:\n                raise ValidationError(e)\n\n        queryset = self.Meta.model.objects\n        pk = None\n\n        if isinstance(self.Meta.model._meta.pk, AutoField):\n            # PK is an int for this model. This is usually the User model\n            try:\n                pk = int(data)\n            except (TypeError, ValueError):\n                raise ValidationError(\n                    \"Related objects must be referenced by ID or by dictionary of attributes. Received an \"\n                    \"unrecognized value: {}\".format(data)\n                )\n\n        else:\n            # We assume a type of UUIDField for all other models\n\n            # PK of related object\n            try:\n                # Ensure the pk is a valid UUID\n                pk = uuid.UUID(str(data))\n            except (TypeError, ValueError):\n                raise ValidationError(\n                    \"Related objects must be referenced by ID or by dictionary of attributes. Received an \"\n                    \"unrecognized value: {}\".format(data)\n                )\n\n        try:\n            return queryset.get(pk=pk)\n        except ObjectDoesNotExist:\n            raise ValidationError(\"Related object not found using the provided ID: {}\".format(pk))\n\n\nclass BulkOperationSerializer(serializers.Serializer):\n    id = serializers.CharField()  # This supports both UUIDs and numeric ID for the User model\n\n\n#\n# GraphQL, used by the openapi doc, not by the view\n#\n\n\nclass GraphQLAPISerializer(serializers.Serializer):\n    query = serializers.CharField(required=True, help_text=\"GraphQL query\")\n    variables = serializers.JSONField(required=False, help_text=\"Variables in JSON Format\")\n", "path": "nautobot/core/api/serializers.py"}], "after_files": [{"content": "import uuid\n\nfrom django.core.exceptions import (\n    FieldError,\n    MultipleObjectsReturned,\n    ObjectDoesNotExist,\n)\nfrom django.db.models import AutoField, ManyToManyField\nfrom drf_yasg.utils import swagger_serializer_method\nfrom rest_framework import serializers\nfrom rest_framework.exceptions import ValidationError\n\nfrom nautobot.utilities.utils import dict_to_filter_params\n\n\nclass BaseModelSerializer(serializers.ModelSerializer):\n    \"\"\"\n    This base serializer implements common fields and logic for all ModelSerializers.\n    Namely it defines the `display` field which exposes a human friendly value for the given object.\n    \"\"\"\n\n    display = serializers.SerializerMethodField(read_only=True, help_text=\"Human friendly display value\")\n\n    @swagger_serializer_method(serializer_or_field=serializers.CharField)\n    def get_display(self, instance):\n        \"\"\"\n        Return either the `display` property of the instance or `str(instance)`\n        \"\"\"\n        return getattr(instance, \"display\", str(instance))\n\n    def get_field_names(self, declared_fields, info):\n        \"\"\"\n        Override get_field_names() to append the `display` field so it is always included in the\n        serializer's `Meta.fields`.\n\n        DRF does not automatically add declared fields to `Meta.fields`, nor does it require that declared fields\n        on a super class be included in `Meta.fields` to allow for a subclass to include only a subset of declared\n        fields from the super. This means either we intercept and append the display field at this level, or\n        enforce by convention that all consumers of BaseModelSerializer include `display` in their `Meta.fields`\n        which would surely lead to errors of omission; therefore we have chosen the former approach.\n        \"\"\"\n        fields = list(super().get_field_names(declared_fields, info))  # Meta.fields could be defined as a tuple\n        fields.append(\"display\")\n\n        return fields\n\n\nclass ValidatedModelSerializer(BaseModelSerializer):\n    \"\"\"\n    Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\n    validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)\n    \"\"\"\n\n    def validate(self, data):\n\n        # Remove custom fields data and tags (if any) prior to model validation\n        attrs = data.copy()\n        attrs.pop(\"custom_fields\", None)\n        attrs.pop(\"tags\", None)\n\n        # Skip ManyToManyFields\n        for field in self.Meta.model._meta.get_fields():\n            if isinstance(field, ManyToManyField):\n                attrs.pop(field.name, None)\n\n        # Run clean() on an instance of the model\n        if self.instance is None:\n            instance = self.Meta.model(**attrs)\n        else:\n            instance = self.instance\n            for k, v in attrs.items():\n                setattr(instance, k, v)\n        instance.full_clean()\n\n        return data\n\n\nclass WritableNestedSerializer(BaseModelSerializer):\n    \"\"\"\n    Returns a nested representation of an object on read, but accepts either the nested representation or the\n    primary key value on write operations.\n    \"\"\"\n\n    def to_internal_value(self, data):\n\n        if data is None:\n            return None\n\n        # Dictionary of related object attributes\n        if isinstance(data, dict):\n            params = dict_to_filter_params(data)\n            queryset = self.Meta.model.objects\n            try:\n                return queryset.get(**params)\n            except ObjectDoesNotExist:\n                raise ValidationError(\"Related object not found using the provided attributes: {}\".format(params))\n            except MultipleObjectsReturned:\n                raise ValidationError(\"Multiple objects match the provided attributes: {}\".format(params))\n            except FieldError as e:\n                raise ValidationError(e)\n\n        queryset = self.Meta.model.objects\n        pk = None\n\n        if isinstance(self.Meta.model._meta.pk, AutoField):\n            # PK is an int for this model. This is usually the User model\n            try:\n                pk = int(data)\n            except (TypeError, ValueError):\n                raise ValidationError(\n                    \"Related objects must be referenced by ID or by dictionary of attributes. Received an \"\n                    \"unrecognized value: {}\".format(data)\n                )\n\n        else:\n            # We assume a type of UUIDField for all other models\n\n            # PK of related object\n            try:\n                # Ensure the pk is a valid UUID\n                pk = uuid.UUID(str(data))\n            except (TypeError, ValueError):\n                raise ValidationError(\n                    \"Related objects must be referenced by ID or by dictionary of attributes. Received an \"\n                    \"unrecognized value: {}\".format(data)\n                )\n\n        try:\n            return queryset.get(pk=pk)\n        except ObjectDoesNotExist:\n            raise ValidationError(\"Related object not found using the provided ID: {}\".format(pk))\n\n\nclass BulkOperationSerializer(serializers.Serializer):\n    id = serializers.CharField()  # This supports both UUIDs and numeric ID for the User model\n\n\n#\n# GraphQL, used by the openapi doc, not by the view\n#\n\n\nclass GraphQLAPISerializer(serializers.Serializer):\n    query = serializers.CharField(required=True, help_text=\"GraphQL query\")\n    variables = serializers.JSONField(required=False, help_text=\"Variables in JSON Format\")\n", "path": "nautobot/core/api/serializers.py"}]}