Repository: wagtail/wagtail

Issue:
SnippetChooserPanel cannot take model string in model argument
We should update `SnippetChooserPanel` to use `resolve_model_string` (https://github.com/torchbox/wagtail/blob/master/wagtail/wagtailcore/utils.py#L13-L37). This will make it behave more like `PageChooserPanel`.



Discussion:

Resolving patch:
diff --git a/wagtail/wagtailsnippets/edit_handlers.py b/wagtail/wagtailsnippets/edit_handlers.py
--- a/wagtail/wagtailsnippets/edit_handlers.py
+++ b/wagtail/wagtailsnippets/edit_handlers.py
@@ -4,28 +4,42 @@
 from django.contrib.contenttypes.models import ContentType
 from django.utils.safestring import mark_safe
 from django.utils.encoding import force_text
+from django.core.exceptions import ImproperlyConfigured
 
 from wagtail.wagtailadmin.edit_handlers import BaseChooserPanel
+from wagtail.wagtailcore.utils import resolve_model_string
 from .widgets import AdminSnippetChooser
 
 
 class BaseSnippetChooserPanel(BaseChooserPanel):
     object_type_name = 'item'
 
-    _content_type = None
+    _target_content_type = None
 
     @classmethod
     def widget_overrides(cls):
         return {cls.field_name: AdminSnippetChooser(
-            content_type=cls.content_type(), snippet_type_name=cls.snippet_type_name)}
+            content_type=cls.target_content_type(), snippet_type_name=cls.snippet_type_name)}
 
     @classmethod
-    def content_type(cls):
-        if cls._content_type is None:
-            # TODO: infer the content type by introspection on the foreign key rather than having to pass it explicitly
-            cls._content_type = ContentType.objects.get_for_model(cls.snippet_type)
+    def target_content_type(cls):
+        if cls._target_content_type is None:
+            if cls.snippet_type:
+                try:
+                    model = resolve_model_string(cls.snippet_type)
+                except LookupError:
+                    raise ImproperlyConfigured("{0}.snippet_type must be of the form 'app_label.model_name', given {1!r}".format(
+                        cls.__name__, cls.snippet_type))
+                except ValueError:
+                    raise ImproperlyConfigured("{0}.snippet_type refers to model {1!r} that has not been installed".format(
+                        cls.__name__, cls.snippet_type))
 
-        return cls._content_type
+                cls._target_content_type = ContentType.objects.get_for_model(model)
+            else:
+                target_model = cls.model._meta.get_field(cls.field_name).rel.to
+                cls._target_content_type = ContentType.objects.get_for_model(target_model)
+
+        return cls._target_content_type
 
     def render_as_field(self):
         instance_obj = self.get_chosen_item()
@@ -35,6 +49,10 @@ def render_as_field(self):
             'snippet_type_name': self.snippet_type_name,
         }))
 
+    @property
+    def snippet_type_name(self):
+        return force_text(self.target_content_type()._meta.verbose_name)
+
 
 class SnippetChooserPanel(object):
     def __init__(self, field_name, snippet_type):
@@ -45,6 +63,5 @@ def bind_to_model(self, model):
         return type(str('_SnippetChooserPanel'), (BaseSnippetChooserPanel,), {
             'model': model,
             'field_name': self.field_name,
-            'snippet_type_name': force_text(self.snippet_type._meta.verbose_name),
             'snippet_type': self.snippet_type,
         })


Test patch:
diff --git a/wagtail/wagtailsnippets/tests.py b/wagtail/wagtailsnippets/tests.py
--- a/wagtail/wagtailsnippets/tests.py
+++ b/wagtail/wagtailsnippets/tests.py
@@ -1,11 +1,13 @@
 from django.test import TestCase
 from django.core.urlresolvers import reverse
 from django.test.utils import override_settings
+from django.core.exceptions import ImproperlyConfigured
 
 from wagtail.tests.utils import WagtailTestUtils
 from wagtail.tests.testapp.models import Advert, SnippetChooserModel
 from wagtail.tests.snippets.models import AlphaSnippet, ZuluSnippet, RegisterDecorator, RegisterFunction
 from wagtail.wagtailsnippets.models import register_snippet, SNIPPET_MODELS
+from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
 
 from wagtail.wagtailsnippets.views.snippets import (
     get_snippet_edit_handler
@@ -185,6 +187,31 @@ def test_render_js(self):
         self.assertIn('createSnippetChooser("id_advert", "tests/advert");',
                       self.snippet_chooser_panel.render_as_field())
 
+    def test_target_content_type(self):
+        result = SnippetChooserPanel(
+            'barbecue',
+            'wagtailcore.site'
+        ).bind_to_model(SnippetChooserModel).target_content_type()
+        self.assertEqual(result.name, 'Site')
+
+    def test_target_content_type_malformed_type(self):
+        result = SnippetChooserPanel(
+            'barbecue',
+            'snowman'
+        ).bind_to_model(SnippetChooserModel)
+        self.assertRaises(ImproperlyConfigured,
+                          result.target_content_type)
+
+    def test_target_content_type_nonexistent_type(self):
+        result = SnippetChooserPanel(
+            'barbecue',
+            'snowman.lorry'
+        ).bind_to_model(SnippetChooserModel)
+        self.assertRaises(ImproperlyConfigured,
+                          result.target_content_type)
+
+
+
 
 class TestSnippetRegistering(TestCase):
     def test_register_function(self):