prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Changing event rule action type messes up Conditions field
  ### Deployment Type
  
  Self-hosted
  
  ### NetBox Version
  
  v3.7.1
  
  ### Python Version
  
  3.11
  
  ### Steps to Reproduce
  
  1. Go to **Operations - Event Rules - Add**
  2. Set Conditions = `{ "and": [{"attr": "status.value", "value": "deprecated"}]}`
  3. Select Action type = Script
  
  ### Expected Behavior
  
  Conditions field stays as-is
  
  ### Observed Behavior
  
  Conditions field is rewritten as invalid JSON:
  
  `"{ \"and\": [{\"attr\": \"status.value\", \"value\": \"deprecated\"}]}"`
  
  The quoting is added with every change of the Action type.
  
  This also happens if editing an existing event rule and changing the Action type.
  
  ### Workaround
  
  - Copy the Conditions field somewhere
  - Change the Action type
  - Paste the Conditions field back
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `netbox/utilities/forms/fields/fields.py`
  Content:
  ```
  1 import json
  2 
  3 from django import forms
  4 from django.db.models import Count
  5 from django.forms.fields import JSONField as _JSONField, InvalidJSONInput
  6 from django.templatetags.static import static
  7 from django.utils.translation import gettext_lazy as _
  8 from netaddr import AddrFormatError, EUI
  9 
  10 from utilities.forms import widgets
  11 from utilities.validators import EnhancedURLValidator
  12 
  13 __all__ = (
  14     'ColorField',
  15     'CommentField',
  16     'JSONField',
  17     'LaxURLField',
  18     'MACAddressField',
  19     'SlugField',
  20     'TagFilterField',
  21 )
  22 
  23 
  24 class CommentField(forms.CharField):
  25     """
  26     A textarea with support for Markdown rendering. Exists mostly just to add a standard `help_text`.
  27     """
  28     widget = widgets.MarkdownWidget
  29     label = _('Comments')
  30     help_text = _(
  31         '<i class="mdi mdi-information-outline"></i> '
  32         '<a href="{url}" target="_blank" tabindex="-1">Markdown</a> syntax is supported'
  33     ).format(url=static('docs/reference/markdown/'))
  34 
  35     def __init__(self, *, label=label, help_text=help_text, required=False, **kwargs):
  36         super().__init__(label=label, help_text=help_text, required=required, **kwargs)
  37 
  38 
  39 class SlugField(forms.SlugField):
  40     """
  41     Extend Django's built-in SlugField to automatically populate from a field called `name` unless otherwise specified.
  42 
  43     Parameters:
  44         slug_source: Name of the form field from which the slug value will be derived
  45     """
  46     widget = widgets.SlugWidget
  47     label = _('Slug')
  48     help_text = _("URL-friendly unique shorthand")
  49 
  50     def __init__(self, *, slug_source='name', label=label, help_text=help_text, **kwargs):
  51         super().__init__(label=label, help_text=help_text, **kwargs)
  52 
  53         self.widget.attrs['slug-source'] = slug_source
  54 
  55 
  56 class ColorField(forms.CharField):
  57     """
  58     A field which represents a color value in hexadecimal `RRGGBB` format. Utilizes NetBox's `ColorSelect` widget to
  59     render choices.
  60     """
  61     widget = widgets.ColorSelect
  62 
  63 
  64 class TagFilterField(forms.MultipleChoiceField):
  65     """
  66     A filter field for the tags of a model. Only the tags used by a model are displayed.
  67 
  68     :param model: The model of the filter
  69     """
  70 
  71     def __init__(self, model, *args, **kwargs):
  72         def get_choices():
  73             tags = model.tags.annotate(
  74                 count=Count('extras_taggeditem_items')
  75             ).order_by('name')
  76             return [
  77                 (str(tag.slug), '{} ({})'.format(tag.name, tag.count)) for tag in tags
  78             ]
  79 
  80         # Choices are fetched each time the form is initialized
  81         super().__init__(label=_('Tags'), choices=get_choices, required=False, *args, **kwargs)
  82 
  83 
  84 class LaxURLField(forms.URLField):
  85     """
  86     Modifies Django's built-in URLField to remove the requirement for fully-qualified domain names
  87     (e.g. http://myserver/ is valid)
  88     """
  89     default_validators = [EnhancedURLValidator()]
  90 
  91 
  92 class JSONField(_JSONField):
  93     """
  94     Custom wrapper around Django's built-in JSONField to avoid presenting "null" as the default text.
  95     """
  96     def __init__(self, *args, **kwargs):
  97         super().__init__(*args, **kwargs)
  98         if not self.help_text:
  99             self.help_text = _('Enter context data in <a href="https://json.org/">JSON</a> format.')
  100             self.widget.attrs['placeholder'] = ''
  101             self.widget.attrs['class'] = 'font-monospace'
  102 
  103     def prepare_value(self, value):
  104         if isinstance(value, InvalidJSONInput):
  105             return value
  106         if value in ('', None):
  107             return ''
  108         return json.dumps(value, sort_keys=True, indent=4)
  109 
  110 
  111 class MACAddressField(forms.Field):
  112     """
  113     Validates a 48-bit MAC address.
  114     """
  115     widget = forms.CharField
  116     default_error_messages = {
  117         'invalid': _('MAC address must be in EUI-48 format'),
  118     }
  119 
  120     def to_python(self, value):
  121         value = super().to_python(value)
  122 
  123         # Validate MAC address format
  124         try:
  125             value = EUI(value.strip())
  126         except AddrFormatError:
  127             raise forms.ValidationError(self.error_messages['invalid'], code='invalid')
  128 
  129         return value
  130
  ```
  --- 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/netbox/utilities/forms/fields/fields.py b/netbox/utilities/forms/fields/fields.py
  --- a/netbox/utilities/forms/fields/fields.py
  +++ b/netbox/utilities/forms/fields/fields.py
  @@ -105,7 +105,12 @@
               return value
           if value in ('', None):
               return ''
  -        return json.dumps(value, sort_keys=True, indent=4)
  +        if type(value) is str:
  +            try:
  +                value = json.loads(value, cls=self.decoder)
  +            except json.decoder.JSONDecodeError:
  +                return value
  +        return json.dumps(value, sort_keys=True, indent=4, ensure_ascii=False, cls=self.encoder)
   
   
   class MACAddressField(forms.Field):
  
verification_info: {"golden_diff": "diff --git a/netbox/utilities/forms/fields/fields.py b/netbox/utilities/forms/fields/fields.py\n--- a/netbox/utilities/forms/fields/fields.py\n+++ b/netbox/utilities/forms/fields/fields.py\n@@ -105,7 +105,12 @@\n             return value\n         if value in ('', None):\n             return ''\n-        return json.dumps(value, sort_keys=True, indent=4)\n+        if type(value) is str:\n+            try:\n+                value = json.loads(value, cls=self.decoder)\n+            except json.decoder.JSONDecodeError:\n+                return value\n+        return json.dumps(value, sort_keys=True, indent=4, ensure_ascii=False, cls=self.encoder)\n \n \n class MACAddressField(forms.Field):\n", "issue": "Changing event rule action type messes up Conditions field\n### Deployment Type\r\n\r\nSelf-hosted\r\n\r\n### NetBox Version\r\n\r\nv3.7.1\r\n\r\n### Python Version\r\n\r\n3.11\r\n\r\n### Steps to Reproduce\r\n\r\n1. Go to **Operations - Event Rules - Add**\r\n2. Set Conditions = `{ \"and\": [{\"attr\": \"status.value\", \"value\": \"deprecated\"}]}`\r\n3. Select Action type = Script\r\n\r\n### Expected Behavior\r\n\r\nConditions field stays as-is\r\n\r\n### Observed Behavior\r\n\r\nConditions field is rewritten as invalid JSON:\r\n\r\n`\"{ \\\"and\\\": [{\\\"attr\\\": \\\"status.value\\\", \\\"value\\\": \\\"deprecated\\\"}]}\"`\r\n\r\nThe quoting is added with every change of the Action type.\r\n\r\nThis also happens if editing an existing event rule and changing the Action type.\r\n\r\n### Workaround\r\n\r\n- Copy the Conditions field somewhere\r\n- Change the Action type\r\n- Paste the Conditions field back\n", "before_files": [{"content": "import json\n\nfrom django import forms\nfrom django.db.models import Count\nfrom django.forms.fields import JSONField as _JSONField, InvalidJSONInput\nfrom django.templatetags.static import static\nfrom django.utils.translation import gettext_lazy as _\nfrom netaddr import AddrFormatError, EUI\n\nfrom utilities.forms import widgets\nfrom utilities.validators import EnhancedURLValidator\n\n__all__ = (\n    'ColorField',\n    'CommentField',\n    'JSONField',\n    'LaxURLField',\n    'MACAddressField',\n    'SlugField',\n    'TagFilterField',\n)\n\n\nclass CommentField(forms.CharField):\n    \"\"\"\n    A textarea with support for Markdown rendering. Exists mostly just to add a standard `help_text`.\n    \"\"\"\n    widget = widgets.MarkdownWidget\n    label = _('Comments')\n    help_text = _(\n        '<i class=\"mdi mdi-information-outline\"></i> '\n        '<a href=\"{url}\" target=\"_blank\" tabindex=\"-1\">Markdown</a> syntax is supported'\n    ).format(url=static('docs/reference/markdown/'))\n\n    def __init__(self, *, label=label, help_text=help_text, required=False, **kwargs):\n        super().__init__(label=label, help_text=help_text, required=required, **kwargs)\n\n\nclass SlugField(forms.SlugField):\n    \"\"\"\n    Extend Django's built-in SlugField to automatically populate from a field called `name` unless otherwise specified.\n\n    Parameters:\n        slug_source: Name of the form field from which the slug value will be derived\n    \"\"\"\n    widget = widgets.SlugWidget\n    label = _('Slug')\n    help_text = _(\"URL-friendly unique shorthand\")\n\n    def __init__(self, *, slug_source='name', label=label, help_text=help_text, **kwargs):\n        super().__init__(label=label, help_text=help_text, **kwargs)\n\n        self.widget.attrs['slug-source'] = slug_source\n\n\nclass ColorField(forms.CharField):\n    \"\"\"\n    A field which represents a color value in hexadecimal `RRGGBB` format. Utilizes NetBox's `ColorSelect` widget to\n    render choices.\n    \"\"\"\n    widget = widgets.ColorSelect\n\n\nclass TagFilterField(forms.MultipleChoiceField):\n    \"\"\"\n    A filter field for the tags of a model. Only the tags used by a model are displayed.\n\n    :param model: The model of the filter\n    \"\"\"\n\n    def __init__(self, model, *args, **kwargs):\n        def get_choices():\n            tags = model.tags.annotate(\n                count=Count('extras_taggeditem_items')\n            ).order_by('name')\n            return [\n                (str(tag.slug), '{} ({})'.format(tag.name, tag.count)) for tag in tags\n            ]\n\n        # Choices are fetched each time the form is initialized\n        super().__init__(label=_('Tags'), choices=get_choices, required=False, *args, **kwargs)\n\n\nclass LaxURLField(forms.URLField):\n    \"\"\"\n    Modifies Django's built-in URLField to remove the requirement for fully-qualified domain names\n    (e.g. http://myserver/ is valid)\n    \"\"\"\n    default_validators = [EnhancedURLValidator()]\n\n\nclass JSONField(_JSONField):\n    \"\"\"\n    Custom wrapper around Django's built-in JSONField to avoid presenting \"null\" as the default text.\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        if not self.help_text:\n            self.help_text = _('Enter context data in <a href=\"https://json.org/\">JSON</a> format.')\n            self.widget.attrs['placeholder'] = ''\n            self.widget.attrs['class'] = 'font-monospace'\n\n    def prepare_value(self, value):\n        if isinstance(value, InvalidJSONInput):\n            return value\n        if value in ('', None):\n            return ''\n        return json.dumps(value, sort_keys=True, indent=4)\n\n\nclass MACAddressField(forms.Field):\n    \"\"\"\n    Validates a 48-bit MAC address.\n    \"\"\"\n    widget = forms.CharField\n    default_error_messages = {\n        'invalid': _('MAC address must be in EUI-48 format'),\n    }\n\n    def to_python(self, value):\n        value = super().to_python(value)\n\n        # Validate MAC address format\n        try:\n            value = EUI(value.strip())\n        except AddrFormatError:\n            raise forms.ValidationError(self.error_messages['invalid'], code='invalid')\n\n        return value\n", "path": "netbox/utilities/forms/fields/fields.py"}], "after_files": [{"content": "import json\n\nfrom django import forms\nfrom django.db.models import Count\nfrom django.forms.fields import JSONField as _JSONField, InvalidJSONInput\nfrom django.templatetags.static import static\nfrom django.utils.translation import gettext_lazy as _\nfrom netaddr import AddrFormatError, EUI\n\nfrom utilities.forms import widgets\nfrom utilities.validators import EnhancedURLValidator\n\n__all__ = (\n    'ColorField',\n    'CommentField',\n    'JSONField',\n    'LaxURLField',\n    'MACAddressField',\n    'SlugField',\n    'TagFilterField',\n)\n\n\nclass CommentField(forms.CharField):\n    \"\"\"\n    A textarea with support for Markdown rendering. Exists mostly just to add a standard `help_text`.\n    \"\"\"\n    widget = widgets.MarkdownWidget\n    label = _('Comments')\n    help_text = _(\n        '<i class=\"mdi mdi-information-outline\"></i> '\n        '<a href=\"{url}\" target=\"_blank\" tabindex=\"-1\">Markdown</a> syntax is supported'\n    ).format(url=static('docs/reference/markdown/'))\n\n    def __init__(self, *, label=label, help_text=help_text, required=False, **kwargs):\n        super().__init__(label=label, help_text=help_text, required=required, **kwargs)\n\n\nclass SlugField(forms.SlugField):\n    \"\"\"\n    Extend Django's built-in SlugField to automatically populate from a field called `name` unless otherwise specified.\n\n    Parameters:\n        slug_source: Name of the form field from which the slug value will be derived\n    \"\"\"\n    widget = widgets.SlugWidget\n    label = _('Slug')\n    help_text = _(\"URL-friendly unique shorthand\")\n\n    def __init__(self, *, slug_source='name', label=label, help_text=help_text, **kwargs):\n        super().__init__(label=label, help_text=help_text, **kwargs)\n\n        self.widget.attrs['slug-source'] = slug_source\n\n\nclass ColorField(forms.CharField):\n    \"\"\"\n    A field which represents a color value in hexadecimal `RRGGBB` format. Utilizes NetBox's `ColorSelect` widget to\n    render choices.\n    \"\"\"\n    widget = widgets.ColorSelect\n\n\nclass TagFilterField(forms.MultipleChoiceField):\n    \"\"\"\n    A filter field for the tags of a model. Only the tags used by a model are displayed.\n\n    :param model: The model of the filter\n    \"\"\"\n\n    def __init__(self, model, *args, **kwargs):\n        def get_choices():\n            tags = model.tags.annotate(\n                count=Count('extras_taggeditem_items')\n            ).order_by('name')\n            return [\n                (str(tag.slug), '{} ({})'.format(tag.name, tag.count)) for tag in tags\n            ]\n\n        # Choices are fetched each time the form is initialized\n        super().__init__(label=_('Tags'), choices=get_choices, required=False, *args, **kwargs)\n\n\nclass LaxURLField(forms.URLField):\n    \"\"\"\n    Modifies Django's built-in URLField to remove the requirement for fully-qualified domain names\n    (e.g. http://myserver/ is valid)\n    \"\"\"\n    default_validators = [EnhancedURLValidator()]\n\n\nclass JSONField(_JSONField):\n    \"\"\"\n    Custom wrapper around Django's built-in JSONField to avoid presenting \"null\" as the default text.\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        if not self.help_text:\n            self.help_text = _('Enter context data in <a href=\"https://json.org/\">JSON</a> format.')\n            self.widget.attrs['placeholder'] = ''\n            self.widget.attrs['class'] = 'font-monospace'\n\n    def prepare_value(self, value):\n        if isinstance(value, InvalidJSONInput):\n            return value\n        if value in ('', None):\n            return ''\n        if type(value) is str:\n            try:\n                value = json.loads(value, cls=self.decoder)\n            except json.decoder.JSONDecodeError:\n                return value\n        return json.dumps(value, sort_keys=True, indent=4, ensure_ascii=False, cls=self.encoder)\n\n\nclass MACAddressField(forms.Field):\n    \"\"\"\n    Validates a 48-bit MAC address.\n    \"\"\"\n    widget = forms.CharField\n    default_error_messages = {\n        'invalid': _('MAC address must be in EUI-48 format'),\n    }\n\n    def to_python(self, value):\n        value = super().to_python(value)\n\n        # Validate MAC address format\n        try:\n            value = EUI(value.strip())\n        except AddrFormatError:\n            raise forms.ValidationError(self.error_messages['invalid'], code='invalid')\n\n        return value\n", "path": "netbox/utilities/forms/fields/fields.py"}]}