Repository: netbox-community/netbox
Pull request: Add dns_name filter on the IP Address page 
<!--
    Thank you for your interest in contributing to NetBox! Please note that
    our contribution policy requires that a feature request or bug report be
    approved and assigned prior to opening a pull request. This helps avoid
    waste time and effort on a proposed change that we might not be able to
    accept.

    IF YOUR PULL REQUEST DOES NOT REFERENCE AN ISSUE WHICH HAS BEEN ASSIGNED
    TO YOU, IT WILL BE CLOSED AUTOMATICALLY.

    Please specify your assigned issue number on the line below.
-->
### Fixes: #13957

<!--
    Please include a summary of the proposed changes below.
-->


Problem statement:
Add dns_name filter on IP Addresses page
### NetBox version

v.3.6.3

### Feature type

Change to existing functionality

### Proposed functionality

It is currently possible to filter on dns_name on the IP Addresses page.

The proposed functionality is to add the ability to filter on the dns_name field.

### Use case

1: A user may want to determine how many IP Addresses have dns_names that wall within a particular sub domain. It is currently only possibly to do this by creating an export filter and using Jinja to filter, and Jinja has limited filtering capabilities.

2: A user may want to check if there are dns_name duplicates for a specific fqdn.

### Database changes

None

### External dependencies

None

Hints from issue discussion:
Can you please assign this issue to me ?

Commit message: Add dns_name filter on the IP Address page #13957

Patch to review:
diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py
index bc918128651..332eb43577c 100644
--- a/netbox/ipam/filtersets.py
+++ b/netbox/ipam/filtersets.py
@@ -618,10 +618,14 @@ class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
     role = django_filters.MultipleChoiceFilter(
         choices=IPAddressRoleChoices
     )
+    dns_name = MultiValueCharFilter(
+        method='search_by_dns',
+        label=_('DNS Name'),
+    )
 
     class Meta:
         model = IPAddress
-        fields = ['id', 'dns_name', 'description']
+        fields = ['id', 'description']
 
     def search(self, queryset, name, value):
         if not value.strip():
@@ -738,6 +742,17 @@ def _assigned(self, queryset, name, value):
                 assigned_object_id__isnull=True
             )
 
+    def search_by_dns(self, queryset, name, value):
+        if not value:
+            return queryset
+
+        normalized_value = str(value[0]).lower()
+
+        if normalized_value in ('none', 'null'):
+            return queryset.filter(dns_name='')
+
+        return queryset.filter(dns_name__in=value)
+
 
 class FHRPGroupFilterSet(NetBoxModelFilterSet):
     protocol = django_filters.MultipleChoiceFilter(
diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py
index e4e967f8117..120044937ee 100644
--- a/netbox/ipam/forms/filtersets.py
+++ b/netbox/ipam/forms/filtersets.py
@@ -295,7 +295,7 @@ class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
     model = IPAddress
     fieldsets = (
         (None, ('q', 'filter_id', 'tag')),
-        (_('Attributes'), ('parent', 'family', 'status', 'role', 'mask_length', 'assigned_to_interface')),
+        (_('Attributes'), ('parent', 'family', 'status', 'role', 'mask_length', 'assigned_to_interface', 'dns_name')),
         (_('VRF'), ('vrf_id', 'present_in_vrf_id')),
         (_('Tenant'), ('tenant_group_id', 'tenant_id')),
         (_('Device/VM'), ('device_id', 'virtual_machine_id')),
@@ -357,6 +357,15 @@ class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
             choices=BOOLEAN_WITH_BLANK_CHOICES
         )
     )
+    dns_name = forms.CharField(
+        required=False,
+        widget=forms.TextInput(
+            attrs={
+                'placeholder': 'DNS Name',
+            }
+        ),
+        label='DNS Name'
+    )
     tag = TagFilterField(model)
 
 


Code review comments:
diff_hunk: @@ -738,6 +742,17 @@ def _assigned(self, queryset, name, value):
                 assigned_object_id__isnull=True
             )
 
+    def search_by_dns(self, queryset, name, value):
+        if not value:
+            return queryset
+
+        normalized_value = str(value[0]).lower()
+
+        if normalized_value in ('none', 'null'):
+            return queryset.filter(dns_name='')
+
+        return queryset.filter(dns_name__in=value)
+
path: netbox/ipam/filtersets.py
text: @user1:
The scope of FR #13957 is merely to add the `dns_name` filter to the IP addresses list filters. It does not entail modifying the filter's behavior, which would result in a breaking change. Please reverse these changes.

diff_hunk: @@ -357,6 +357,15 @@ class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
             choices=BOOLEAN_WITH_BLANK_CHOICES
         )
     )
+    dns_name = forms.CharField(
+        required=False,
+        widget=forms.TextInput(
+            attrs={
+                'placeholder': 'DNS Name',
+            }
+        ),
+        label='DNS Name'
path: netbox/ipam/forms/filtersets.py
text: @user1:
Please ensure human-readable labels are wrapped for translation.
```suggestion
        label=_('DNS Name')
```

diff_hunk: @@ -357,6 +357,15 @@ class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
             choices=BOOLEAN_WITH_BLANK_CHOICES
         )
     )
+    dns_name = forms.CharField(
+        required=False,
+        widget=forms.TextInput(
+            attrs={
+                'placeholder': 'DNS Name',
+            }
+        ),
path: netbox/ipam/forms/filtersets.py
text: @user1:
I know this is set on other fields, but the placeholder text is not needed.
```suggestion
```