Repository: nautobot/nautobot

Issue:
get_filterset_for_model is not compatible with plugins
### Environment
* Python version: 3.7
* Nautobot version: 1.0.0

### Steps to Reproduce
1. Create a plugin with a model `MyModel` and a filterset `MyModelFilter` defined respectively in `nautobot_myplugin.models` and `nautobot_myplugin.filters`
2. Call `nautobot.utilities.utils.get_filterset_for_model` with an instance of `MyModel`

### Expected Behavior
`get_filterset_for_model` should return `MyModelFilter` as the filterset associated with `MyModel`

### Observed Behavior
None is returned because `get_filterset_for_model` is searching for the filterset at `nautobot.nautobot_myplugin.filters` instead of `nautobot_myplugin.filters`

As a result all features based on `get_filterset_for_model` will not work properly with models coming from plugins.


Discussion:

Resolving patch:
diff --git a/nautobot/utilities/api.py b/nautobot/utilities/api.py
--- a/nautobot/utilities/api.py
+++ b/nautobot/utilities/api.py
@@ -19,7 +19,9 @@ def get_serializer_for_model(model, prefix=""):
     # Serializers for Django's auth models are in the users app
     if app_name == "auth":
         app_name = "users"
-    serializer_name = f"nautobot.{app_name}.api.serializers.{prefix}{model_name}Serializer"
+    serializer_name = f"{app_name}.api.serializers.{prefix}{model_name}Serializer"
+    if app_name not in settings.PLUGINS:
+        serializer_name = f"nautobot.{serializer_name}"
     try:
         return dynamic_import(serializer_name)
     except AttributeError:
diff --git a/nautobot/utilities/utils.py b/nautobot/utilities/utils.py
--- a/nautobot/utilities/utils.py
+++ b/nautobot/utilities/utils.py
@@ -6,6 +6,7 @@
 from itertools import count, groupby
 from distutils.util import strtobool
 
+from django.conf import settings
 from django.core.serializers import serialize
 from django.db.models import Count, OuterRef, Subquery, Model
 from django.db.models.functions import Coalesce
@@ -375,7 +376,10 @@ def get_filterset_for_model(model):
 
     try:
         filterset_name = f"{model.__name__}FilterSet"
-        return getattr(import_module(f"nautobot.{model._meta.app_label}.filters"), filterset_name)
+        if model._meta.app_label in settings.PLUGINS:
+            return getattr(import_module(f"{model._meta.app_label}.filters"), filterset_name)
+        else:
+            return getattr(import_module(f"nautobot.{model._meta.app_label}.filters"), filterset_name)
     except ModuleNotFoundError:
         # The name of the module is not correct
         pass


Test patch:
diff --git a/nautobot/utilities/testing/api.py b/nautobot/utilities/testing/api.py
--- a/nautobot/utilities/testing/api.py
+++ b/nautobot/utilities/testing/api.py
@@ -48,7 +48,11 @@ def setUp(self):
         self.header = {"HTTP_AUTHORIZATION": "Token {}".format(self.token.key)}
 
     def _get_view_namespace(self):
-        return f"{self.view_namespace or self.model._meta.app_label}-api"
+        if self.view_namespace:
+            return f"{self.view_namespace}-api"
+        if self.model._meta.app_label in settings.PLUGINS:
+            return f"plugins-api:{self.model._meta.app_label}-api"
+        return f"{self.model._meta.app_label}-api"
 
     def _get_detail_url(self, instance):
         viewname = f"{self._get_view_namespace()}:{instance._meta.model_name}-detail"
diff --git a/nautobot/utilities/testing/views.py b/nautobot/utilities/testing/views.py
--- a/nautobot/utilities/testing/views.py
+++ b/nautobot/utilities/testing/views.py
@@ -1,6 +1,7 @@
 import json
 import uuid
 
+from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
@@ -226,6 +227,8 @@ def _get_base_url(self):
         Return the base format for a URL for the test's model. Override this to test for a model which belongs
         to a different app (e.g. testing Interfaces within the virtualization app).
         """
+        if self.model._meta.app_label in settings.PLUGINS:
+            return "plugins:{}:{}_{{}}".format(self.model._meta.app_label, self.model._meta.model_name)
         return "{}:{}_{{}}".format(self.model._meta.app_label, self.model._meta.model_name)
 
     def _get_url(self, action, instance=None):