prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <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`.
  
  
  </issue>
  <code>
  [start of wagtail/wagtailsnippets/edit_handlers.py]
  1 from __future__ import absolute_import, unicode_literals
  2 
  3 from django.template.loader import render_to_string
  4 from django.contrib.contenttypes.models import ContentType
  5 from django.utils.safestring import mark_safe
  6 from django.utils.encoding import force_text
  7 
  8 from wagtail.wagtailadmin.edit_handlers import BaseChooserPanel
  9 from .widgets import AdminSnippetChooser
  10 
  11 
  12 class BaseSnippetChooserPanel(BaseChooserPanel):
  13     object_type_name = 'item'
  14 
  15     _content_type = None
  16 
  17     @classmethod
  18     def widget_overrides(cls):
  19         return {cls.field_name: AdminSnippetChooser(
  20             content_type=cls.content_type(), snippet_type_name=cls.snippet_type_name)}
  21 
  22     @classmethod
  23     def content_type(cls):
  24         if cls._content_type is None:
  25             # TODO: infer the content type by introspection on the foreign key rather than having to pass it explicitly
  26             cls._content_type = ContentType.objects.get_for_model(cls.snippet_type)
  27 
  28         return cls._content_type
  29 
  30     def render_as_field(self):
  31         instance_obj = self.get_chosen_item()
  32         return mark_safe(render_to_string(self.field_template, {
  33             'field': self.bound_field,
  34             self.object_type_name: instance_obj,
  35             'snippet_type_name': self.snippet_type_name,
  36         }))
  37 
  38 
  39 class SnippetChooserPanel(object):
  40     def __init__(self, field_name, snippet_type):
  41         self.field_name = field_name
  42         self.snippet_type = snippet_type
  43 
  44     def bind_to_model(self, model):
  45         return type(str('_SnippetChooserPanel'), (BaseSnippetChooserPanel,), {
  46             'model': model,
  47             'field_name': self.field_name,
  48             'snippet_type_name': force_text(self.snippet_type._meta.verbose_name),
  49             'snippet_type': self.snippet_type,
  50         })
  51
  [end of wagtail/wagtailsnippets/edit_handlers.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: 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 @@
               '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 @@
           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,
           })
  
verification_info: {"golden_diff": "diff --git a/wagtail/wagtailsnippets/edit_handlers.py b/wagtail/wagtailsnippets/edit_handlers.py\n--- a/wagtail/wagtailsnippets/edit_handlers.py\n+++ b/wagtail/wagtailsnippets/edit_handlers.py\n@@ -4,28 +4,42 @@\n from django.contrib.contenttypes.models import ContentType\n from django.utils.safestring import mark_safe\n from django.utils.encoding import force_text\n+from django.core.exceptions import ImproperlyConfigured\n \n from wagtail.wagtailadmin.edit_handlers import BaseChooserPanel\n+from wagtail.wagtailcore.utils import resolve_model_string\n from .widgets import AdminSnippetChooser\n \n \n class BaseSnippetChooserPanel(BaseChooserPanel):\n     object_type_name = 'item'\n \n-    _content_type = None\n+    _target_content_type = None\n \n     @classmethod\n     def widget_overrides(cls):\n         return {cls.field_name: AdminSnippetChooser(\n-            content_type=cls.content_type(), snippet_type_name=cls.snippet_type_name)}\n+            content_type=cls.target_content_type(), snippet_type_name=cls.snippet_type_name)}\n \n     @classmethod\n-    def content_type(cls):\n-        if cls._content_type is None:\n-            # TODO: infer the content type by introspection on the foreign key rather than having to pass it explicitly\n-            cls._content_type = ContentType.objects.get_for_model(cls.snippet_type)\n+    def target_content_type(cls):\n+        if cls._target_content_type is None:\n+            if cls.snippet_type:\n+                try:\n+                    model = resolve_model_string(cls.snippet_type)\n+                except LookupError:\n+                    raise ImproperlyConfigured(\"{0}.snippet_type must be of the form 'app_label.model_name', given {1!r}\".format(\n+                        cls.__name__, cls.snippet_type))\n+                except ValueError:\n+                    raise ImproperlyConfigured(\"{0}.snippet_type refers to model {1!r} that has not been installed\".format(\n+                        cls.__name__, cls.snippet_type))\n \n-        return cls._content_type\n+                cls._target_content_type = ContentType.objects.get_for_model(model)\n+            else:\n+                target_model = cls.model._meta.get_field(cls.field_name).rel.to\n+                cls._target_content_type = ContentType.objects.get_for_model(target_model)\n+\n+        return cls._target_content_type\n \n     def render_as_field(self):\n         instance_obj = self.get_chosen_item()\n@@ -35,6 +49,10 @@\n             'snippet_type_name': self.snippet_type_name,\n         }))\n \n+    @property\n+    def snippet_type_name(self):\n+        return force_text(self.target_content_type()._meta.verbose_name)\n+\n \n class SnippetChooserPanel(object):\n     def __init__(self, field_name, snippet_type):\n@@ -45,6 +63,5 @@\n         return type(str('_SnippetChooserPanel'), (BaseSnippetChooserPanel,), {\n             'model': model,\n             'field_name': self.field_name,\n-            'snippet_type_name': force_text(self.snippet_type._meta.verbose_name),\n             'snippet_type': self.snippet_type,\n         })\n", "issue": "SnippetChooserPanel cannot take model string in model argument\nWe 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`.\n\n", "before_files": [{"content": "from __future__ import absolute_import, unicode_literals\n\nfrom django.template.loader import render_to_string\nfrom django.contrib.contenttypes.models import ContentType\nfrom django.utils.safestring import mark_safe\nfrom django.utils.encoding import force_text\n\nfrom wagtail.wagtailadmin.edit_handlers import BaseChooserPanel\nfrom .widgets import AdminSnippetChooser\n\n\nclass BaseSnippetChooserPanel(BaseChooserPanel):\n    object_type_name = 'item'\n\n    _content_type = None\n\n    @classmethod\n    def widget_overrides(cls):\n        return {cls.field_name: AdminSnippetChooser(\n            content_type=cls.content_type(), snippet_type_name=cls.snippet_type_name)}\n\n    @classmethod\n    def content_type(cls):\n        if cls._content_type is None:\n            # TODO: infer the content type by introspection on the foreign key rather than having to pass it explicitly\n            cls._content_type = ContentType.objects.get_for_model(cls.snippet_type)\n\n        return cls._content_type\n\n    def render_as_field(self):\n        instance_obj = self.get_chosen_item()\n        return mark_safe(render_to_string(self.field_template, {\n            'field': self.bound_field,\n            self.object_type_name: instance_obj,\n            'snippet_type_name': self.snippet_type_name,\n        }))\n\n\nclass SnippetChooserPanel(object):\n    def __init__(self, field_name, snippet_type):\n        self.field_name = field_name\n        self.snippet_type = snippet_type\n\n    def bind_to_model(self, model):\n        return type(str('_SnippetChooserPanel'), (BaseSnippetChooserPanel,), {\n            'model': model,\n            'field_name': self.field_name,\n            'snippet_type_name': force_text(self.snippet_type._meta.verbose_name),\n            'snippet_type': self.snippet_type,\n        })\n", "path": "wagtail/wagtailsnippets/edit_handlers.py"}]}