We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
image tag runs query on template load
Theres a database query in the `__init__` method of `ImageNode`:

https://github.com/torchbox/wagtail/blob/master/wagtail/wagtailimages/templatetags/wagtailimages_tags.py#L42

This causes a query to be generated every time the template is loaded. The filter is not actually needed until the node renders though.

This can cause crashes if you are trying to load a template without a database (such as doing offline compression of assets).


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `wagtail/wagtailcore/management/commands/fixtree.py`
Content:
```
1 import operator
2 import functools
3 from optparse import make_option
4 
5 from django.core.management.base import BaseCommand
6 from django.core.exceptions import ObjectDoesNotExist
7 from django.db import models
8 from django.db.models import Q
9 from django.utils import six
10 
11 from wagtail.wagtailcore.models import Page
12 
13 
14 class Command(BaseCommand):
15     help = "Checks for data integrity errors on the page tree, and fixes them where possible."
16     base_options = (
17         make_option('--noinput', action='store_false', dest='interactive', default=True,
18             help='If provided, any fixes requiring user interaction will be skipped.'
19         ),
20     )
21     option_list = BaseCommand.option_list + base_options
22 
23     def handle(self, **options):
24         any_problems_fixed = False
25 
26         for page in Page.objects.all():
27             try:
28                 page.specific
29             except ObjectDoesNotExist:
30                 self.stdout.write("Page %d (%s) is missing a subclass record; deleting." % (page.id, page.title))
31                 any_problems_fixed = True
32                 page.delete()
33 
34         (bad_alpha, bad_path, orphans, bad_depth, bad_numchild) = Page.find_problems()
35 
36         if bad_depth:
37             self.stdout.write("Incorrect depth value found for pages: %r" % bad_depth)
38         if bad_numchild:
39             self.stdout.write("Incorrect numchild value found for pages: %r" % bad_numchild)
40 
41         if bad_depth or bad_numchild:
42             Page.fix_tree(destructive=False)
43             any_problems_fixed = True
44 
45         if orphans:
46             # The 'orphans' list as returned by treebeard only includes pages that are
47             # missing an immediate parent; descendants of orphans are not included.
48             # Deleting only the *actual* orphans is a bit silly (since it'll just create
49             # more orphans), so generate a queryset that contains descendants as well.
50             orphan_paths = Page.objects.filter(id__in=orphans).values_list('path', flat=True)
51             filter_conditions = []
52             for path in orphan_paths:
53                 filter_conditions.append(Q(path__startswith=path))
54 
55             # combine filter_conditions into a single ORed condition
56             final_filter = functools.reduce(operator.or_, filter_conditions)
57 
58             # build a queryset of all pages to be removed; this must be a vanilla Django
59             # queryset rather than a treebeard MP_NodeQuerySet, so that we bypass treebeard's
60             # custom delete() logic that would trip up on the very same corruption that we're
61             # trying to fix here.
62             pages_to_delete = models.query.QuerySet(Page).filter(final_filter)
63 
64             self.stdout.write("Orphaned pages found:")
65             for page in pages_to_delete:
66                 self.stdout.write("ID %d: %s" % (page.id, page.title))
67             self.stdout.write('')
68 
69             if options.get('interactive', True):
70                 yes_or_no = six.moves.input("Delete these pages? [y/N] ")
71                 delete_orphans = yes_or_no.lower().startswith('y')
72                 self.stdout.write('')
73             else:
74                 # Running tests, check for the "delete_orphans" option
75                 delete_orphans = options.get('delete_orphans', False)
76 
77             if delete_orphans:
78                 deletion_count = len(pages_to_delete)
79                 pages_to_delete.delete()
80                 self.stdout.write(
81                     "%d orphaned page%s deleted." % (deletion_count, "s"[deletion_count==1:])
82                 )
83                 any_problems_fixed = True
84 
85         if any_problems_fixed:
86             # re-run find_problems to see if any new ones have surfaced
87             (bad_alpha, bad_path, orphans, bad_depth, bad_numchild) = Page.find_problems()
88 
89         if any((bad_alpha, bad_path, orphans, bad_depth, bad_numchild)):
90             self.stdout.write("Remaining problems (cannot fix automatically):")
91             if bad_alpha:
92                 self.stdout.write("Invalid characters found in path for pages: %r" % bad_alpha)
93             if bad_path:
94                 self.stdout.write("Invalid path length found for pages: %r" % bad_path)
95             if orphans:
96                 self.stdout.write("Orphaned pages found: %r" % orphans)
97             if bad_depth:
98                 self.stdout.write("Incorrect depth value found for pages: %r" % bad_depth)
99             if bad_numchild:
100                 self.stdout.write("Incorrect numchild value found for pages: %r" % bad_numchild)
101 
102         elif any_problems_fixed:
103             self.stdout.write("All problems fixed.")
104         else:
105             self.stdout.write("No problems found.")
106
```

Path: `wagtail/wagtailimages/templatetags/wagtailimages_tags.py`
Content:
```
1 from django import template
2 
3 from wagtail.wagtailimages.models import Filter, SourceImageIOError
4 
5 register = template.Library()
6 
7 # Local cache of filters, avoid hitting the DB
8 filters = {}
9 
10 
11 @register.tag(name="image")
12 def image(parser, token):
13     bits = token.split_contents()[1:]
14     image_var = bits[0]
15     filter_spec = bits[1]
16     bits = bits[2:]
17 
18     if len(bits) == 2 and bits[0] == 'as':
19         # token is of the form {% image self.photo max-320x200 as img %}
20         return ImageNode(image_var, filter_spec, output_var_name=bits[1])
21     else:
22         # token is of the form {% image self.photo max-320x200 %} - all additional tokens
23         # should be kwargs, which become attributes
24         attrs = {}
25         for bit in bits:
26             try:
27                 name, value = bit.split('=')
28             except ValueError:
29                 raise template.TemplateSyntaxError("'image' tag should be of the form {% image self.photo max-320x200 [ custom-attr=\"value\" ... ] %} or {% image self.photo max-320x200 as img %}")
30             attrs[name] = parser.compile_filter(value) # setup to resolve context variables as value
31 
32         return ImageNode(image_var, filter_spec, attrs=attrs)
33 
34 
35 class ImageNode(template.Node):
36     def __init__(self, image_var_name, filter_spec, output_var_name=None, attrs={}):
37         self.image_var = template.Variable(image_var_name)
38         self.output_var_name = output_var_name
39         self.attrs = attrs
40 
41         if filter_spec not in filters:
42             filters[filter_spec], _ = Filter.objects.get_or_create(spec=filter_spec)
43         self.filter = filters[filter_spec]
44 
45     def render(self, context):
46         try:
47             image = self.image_var.resolve(context)
48         except template.VariableDoesNotExist:
49             return ''
50 
51         if not image:
52             return ''
53 
54         try:
55             rendition = image.get_rendition(self.filter)
56         except SourceImageIOError:
57             # It's fairly routine for people to pull down remote databases to their
58             # local dev versions without retrieving the corresponding image files.
59             # In such a case, we would get a SourceImageIOError at the point where we try to
60             # create the resized version of a non-existent image. Since this is a
61             # bit catastrophic for a missing image, we'll substitute a dummy
62             # Rendition object so that we just output a broken link instead.
63             Rendition = image.renditions.model  # pick up any custom Image / Rendition classes that may be in use
64             rendition = Rendition(image=image, width=0, height=0)
65             rendition.file.name = 'not-found'
66 
67         if self.output_var_name:
68             # return the rendition object in the given variable
69             context[self.output_var_name] = rendition
70             return ''
71         else:
72             # render the rendition's image tag now
73             resolved_attrs = {}
74             for key in self.attrs:
75                 resolved_attrs[key] = self.attrs[key].resolve(context)
76             return rendition.img_tag(resolved_attrs)
77
```
--- 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()

```

diff --git a/wagtail/wagtailcore/management/commands/fixtree.py b/wagtail/wagtailcore/management/commands/fixtree.py
--- a/wagtail/wagtailcore/management/commands/fixtree.py
+++ b/wagtail/wagtailcore/management/commands/fixtree.py
@@ -20,6 +20,11 @@
     )
     option_list = BaseCommand.option_list + base_options
 
+    def numberlist_to_string(self, numberlist):
+        # Converts a list of numbers into a string
+        # Doesn't put "L" after longs
+        return '[' + ', '.join(map(str, numberlist)) + ']'
+
     def handle(self, **options):
         any_problems_fixed = False
 
@@ -34,9 +39,9 @@
         (bad_alpha, bad_path, orphans, bad_depth, bad_numchild) = Page.find_problems()
 
         if bad_depth:
-            self.stdout.write("Incorrect depth value found for pages: %r" % bad_depth)
+            self.stdout.write("Incorrect depth value found for pages: %s" % self.numberlist_to_string(bad_depth))
         if bad_numchild:
-            self.stdout.write("Incorrect numchild value found for pages: %r" % bad_numchild)
+            self.stdout.write("Incorrect numchild value found for pages: %s" % self.numberlist_to_string(bad_numchild))
 
         if bad_depth or bad_numchild:
             Page.fix_tree(destructive=False)
@@ -89,15 +94,15 @@
         if any((bad_alpha, bad_path, orphans, bad_depth, bad_numchild)):
             self.stdout.write("Remaining problems (cannot fix automatically):")
             if bad_alpha:
-                self.stdout.write("Invalid characters found in path for pages: %r" % bad_alpha)
+                self.stdout.write("Invalid characters found in path for pages: %s" % self.numberlist_to_string(bad_alpha))
             if bad_path:
-                self.stdout.write("Invalid path length found for pages: %r" % bad_path)
+                self.stdout.write("Invalid path length found for pages: %s" % self.numberlist_to_string(bad_path))
             if orphans:
-                self.stdout.write("Orphaned pages found: %r" % orphans)
+                self.stdout.write("Orphaned pages found: %s" % self.numberlist_to_string(orphans))
             if bad_depth:
-                self.stdout.write("Incorrect depth value found for pages: %r" % bad_depth)
+                self.stdout.write("Incorrect depth value found for pages: %s" % self.numberlist_to_string(bad_depth))
             if bad_numchild:
-                self.stdout.write("Incorrect numchild value found for pages: %r" % bad_numchild)
+                self.stdout.write("Incorrect numchild value found for pages: %s" % self.numberlist_to_string(bad_numchild))
 
         elif any_problems_fixed:
             self.stdout.write("All problems fixed.")
diff --git a/wagtail/wagtailimages/templatetags/wagtailimages_tags.py b/wagtail/wagtailimages/templatetags/wagtailimages_tags.py
--- a/wagtail/wagtailimages/templatetags/wagtailimages_tags.py
+++ b/wagtail/wagtailimages/templatetags/wagtailimages_tags.py
@@ -1,12 +1,10 @@
 from django import template
+from django.utils.functional import cached_property
 
 from wagtail.wagtailimages.models import Filter, SourceImageIOError
 
 register = template.Library()
 
-# Local cache of filters, avoid hitting the DB
-filters = {}
-
 
 @register.tag(name="image")
 def image(parser, token):
@@ -37,10 +35,12 @@
         self.image_var = template.Variable(image_var_name)
         self.output_var_name = output_var_name
         self.attrs = attrs
+        self.filter_spec = filter_spec
 
-        if filter_spec not in filters:
-            filters[filter_spec], _ = Filter.objects.get_or_create(spec=filter_spec)
-        self.filter = filters[filter_spec]
+    @cached_property
+    def filter(self):
+        _filter, _ = Filter.objects.get_or_create(spec=self.filter_spec)
+        return _filter
 
     def render(self, context):
         try:

{"golden_diff": "diff --git a/wagtail/wagtailcore/management/commands/fixtree.py b/wagtail/wagtailcore/management/commands/fixtree.py\n--- a/wagtail/wagtailcore/management/commands/fixtree.py\n+++ b/wagtail/wagtailcore/management/commands/fixtree.py\n@@ -20,6 +20,11 @@\n     )\n     option_list = BaseCommand.option_list + base_options\n \n+    def numberlist_to_string(self, numberlist):\n+        # Converts a list of numbers into a string\n+        # Doesn't put \"L\" after longs\n+        return '[' + ', '.join(map(str, numberlist)) + ']'\n+\n     def handle(self, **options):\n         any_problems_fixed = False\n \n@@ -34,9 +39,9 @@\n         (bad_alpha, bad_path, orphans, bad_depth, bad_numchild) = Page.find_problems()\n \n         if bad_depth:\n-            self.stdout.write(\"Incorrect depth value found for pages: %r\" % bad_depth)\n+            self.stdout.write(\"Incorrect depth value found for pages: %s\" % self.numberlist_to_string(bad_depth))\n         if bad_numchild:\n-            self.stdout.write(\"Incorrect numchild value found for pages: %r\" % bad_numchild)\n+            self.stdout.write(\"Incorrect numchild value found for pages: %s\" % self.numberlist_to_string(bad_numchild))\n \n         if bad_depth or bad_numchild:\n             Page.fix_tree(destructive=False)\n@@ -89,15 +94,15 @@\n         if any((bad_alpha, bad_path, orphans, bad_depth, bad_numchild)):\n             self.stdout.write(\"Remaining problems (cannot fix automatically):\")\n             if bad_alpha:\n-                self.stdout.write(\"Invalid characters found in path for pages: %r\" % bad_alpha)\n+                self.stdout.write(\"Invalid characters found in path for pages: %s\" % self.numberlist_to_string(bad_alpha))\n             if bad_path:\n-                self.stdout.write(\"Invalid path length found for pages: %r\" % bad_path)\n+                self.stdout.write(\"Invalid path length found for pages: %s\" % self.numberlist_to_string(bad_path))\n             if orphans:\n-                self.stdout.write(\"Orphaned pages found: %r\" % orphans)\n+                self.stdout.write(\"Orphaned pages found: %s\" % self.numberlist_to_string(orphans))\n             if bad_depth:\n-                self.stdout.write(\"Incorrect depth value found for pages: %r\" % bad_depth)\n+                self.stdout.write(\"Incorrect depth value found for pages: %s\" % self.numberlist_to_string(bad_depth))\n             if bad_numchild:\n-                self.stdout.write(\"Incorrect numchild value found for pages: %r\" % bad_numchild)\n+                self.stdout.write(\"Incorrect numchild value found for pages: %s\" % self.numberlist_to_string(bad_numchild))\n \n         elif any_problems_fixed:\n             self.stdout.write(\"All problems fixed.\")\ndiff --git a/wagtail/wagtailimages/templatetags/wagtailimages_tags.py b/wagtail/wagtailimages/templatetags/wagtailimages_tags.py\n--- a/wagtail/wagtailimages/templatetags/wagtailimages_tags.py\n+++ b/wagtail/wagtailimages/templatetags/wagtailimages_tags.py\n@@ -1,12 +1,10 @@\n from django import template\n+from django.utils.functional import cached_property\n \n from wagtail.wagtailimages.models import Filter, SourceImageIOError\n \n register = template.Library()\n \n-# Local cache of filters, avoid hitting the DB\n-filters = {}\n-\n \n @register.tag(name=\"image\")\n def image(parser, token):\n@@ -37,10 +35,12 @@\n         self.image_var = template.Variable(image_var_name)\n         self.output_var_name = output_var_name\n         self.attrs = attrs\n+        self.filter_spec = filter_spec\n \n-        if filter_spec not in filters:\n-            filters[filter_spec], _ = Filter.objects.get_or_create(spec=filter_spec)\n-        self.filter = filters[filter_spec]\n+    @cached_property\n+    def filter(self):\n+        _filter, _ = Filter.objects.get_or_create(spec=self.filter_spec)\n+        return _filter\n \n     def render(self, context):\n         try:\n", "issue": "image tag runs query on template load\nTheres a database query in the `__init__` method of `ImageNode`:\n\nhttps://github.com/torchbox/wagtail/blob/master/wagtail/wagtailimages/templatetags/wagtailimages_tags.py#L42\n\nThis causes a query to be generated every time the template is loaded. The filter is not actually needed until the node renders though.\n\nThis can cause crashes if you are trying to load a template without a database (such as doing offline compression of assets).\n\n", "before_files": [{"content": "import operator\nimport functools\nfrom optparse import make_option\n\nfrom django.core.management.base import BaseCommand\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.db import models\nfrom django.db.models import Q\nfrom django.utils import six\n\nfrom wagtail.wagtailcore.models import Page\n\n\nclass Command(BaseCommand):\n    help = \"Checks for data integrity errors on the page tree, and fixes them where possible.\"\n    base_options = (\n        make_option('--noinput', action='store_false', dest='interactive', default=True,\n            help='If provided, any fixes requiring user interaction will be skipped.'\n        ),\n    )\n    option_list = BaseCommand.option_list + base_options\n\n    def handle(self, **options):\n        any_problems_fixed = False\n\n        for page in Page.objects.all():\n            try:\n                page.specific\n            except ObjectDoesNotExist:\n                self.stdout.write(\"Page %d (%s) is missing a subclass record; deleting.\" % (page.id, page.title))\n                any_problems_fixed = True\n                page.delete()\n\n        (bad_alpha, bad_path, orphans, bad_depth, bad_numchild) = Page.find_problems()\n\n        if bad_depth:\n            self.stdout.write(\"Incorrect depth value found for pages: %r\" % bad_depth)\n        if bad_numchild:\n            self.stdout.write(\"Incorrect numchild value found for pages: %r\" % bad_numchild)\n\n        if bad_depth or bad_numchild:\n            Page.fix_tree(destructive=False)\n            any_problems_fixed = True\n\n        if orphans:\n            # The 'orphans' list as returned by treebeard only includes pages that are\n            # missing an immediate parent; descendants of orphans are not included.\n            # Deleting only the *actual* orphans is a bit silly (since it'll just create\n            # more orphans), so generate a queryset that contains descendants as well.\n            orphan_paths = Page.objects.filter(id__in=orphans).values_list('path', flat=True)\n            filter_conditions = []\n            for path in orphan_paths:\n                filter_conditions.append(Q(path__startswith=path))\n\n            # combine filter_conditions into a single ORed condition\n            final_filter = functools.reduce(operator.or_, filter_conditions)\n\n            # build a queryset of all pages to be removed; this must be a vanilla Django\n            # queryset rather than a treebeard MP_NodeQuerySet, so that we bypass treebeard's\n            # custom delete() logic that would trip up on the very same corruption that we're\n            # trying to fix here.\n            pages_to_delete = models.query.QuerySet(Page).filter(final_filter)\n\n            self.stdout.write(\"Orphaned pages found:\")\n            for page in pages_to_delete:\n                self.stdout.write(\"ID %d: %s\" % (page.id, page.title))\n            self.stdout.write('')\n\n            if options.get('interactive', True):\n                yes_or_no = six.moves.input(\"Delete these pages? [y/N] \")\n                delete_orphans = yes_or_no.lower().startswith('y')\n                self.stdout.write('')\n            else:\n                # Running tests, check for the \"delete_orphans\" option\n                delete_orphans = options.get('delete_orphans', False)\n\n            if delete_orphans:\n                deletion_count = len(pages_to_delete)\n                pages_to_delete.delete()\n                self.stdout.write(\n                    \"%d orphaned page%s deleted.\" % (deletion_count, \"s\"[deletion_count==1:])\n                )\n                any_problems_fixed = True\n\n        if any_problems_fixed:\n            # re-run find_problems to see if any new ones have surfaced\n            (bad_alpha, bad_path, orphans, bad_depth, bad_numchild) = Page.find_problems()\n\n        if any((bad_alpha, bad_path, orphans, bad_depth, bad_numchild)):\n            self.stdout.write(\"Remaining problems (cannot fix automatically):\")\n            if bad_alpha:\n                self.stdout.write(\"Invalid characters found in path for pages: %r\" % bad_alpha)\n            if bad_path:\n                self.stdout.write(\"Invalid path length found for pages: %r\" % bad_path)\n            if orphans:\n                self.stdout.write(\"Orphaned pages found: %r\" % orphans)\n            if bad_depth:\n                self.stdout.write(\"Incorrect depth value found for pages: %r\" % bad_depth)\n            if bad_numchild:\n                self.stdout.write(\"Incorrect numchild value found for pages: %r\" % bad_numchild)\n\n        elif any_problems_fixed:\n            self.stdout.write(\"All problems fixed.\")\n        else:\n            self.stdout.write(\"No problems found.\")\n", "path": "wagtail/wagtailcore/management/commands/fixtree.py"}, {"content": "from django import template\n\nfrom wagtail.wagtailimages.models import Filter, SourceImageIOError\n\nregister = template.Library()\n\n# Local cache of filters, avoid hitting the DB\nfilters = {}\n\n\n@register.tag(name=\"image\")\ndef image(parser, token):\n    bits = token.split_contents()[1:]\n    image_var = bits[0]\n    filter_spec = bits[1]\n    bits = bits[2:]\n\n    if len(bits) == 2 and bits[0] == 'as':\n        # token is of the form {% image self.photo max-320x200 as img %}\n        return ImageNode(image_var, filter_spec, output_var_name=bits[1])\n    else:\n        # token is of the form {% image self.photo max-320x200 %} - all additional tokens\n        # should be kwargs, which become attributes\n        attrs = {}\n        for bit in bits:\n            try:\n                name, value = bit.split('=')\n            except ValueError:\n                raise template.TemplateSyntaxError(\"'image' tag should be of the form {% image self.photo max-320x200 [ custom-attr=\\\"value\\\" ... ] %} or {% image self.photo max-320x200 as img %}\")\n            attrs[name] = parser.compile_filter(value) # setup to resolve context variables as value\n\n        return ImageNode(image_var, filter_spec, attrs=attrs)\n\n\nclass ImageNode(template.Node):\n    def __init__(self, image_var_name, filter_spec, output_var_name=None, attrs={}):\n        self.image_var = template.Variable(image_var_name)\n        self.output_var_name = output_var_name\n        self.attrs = attrs\n\n        if filter_spec not in filters:\n            filters[filter_spec], _ = Filter.objects.get_or_create(spec=filter_spec)\n        self.filter = filters[filter_spec]\n\n    def render(self, context):\n        try:\n            image = self.image_var.resolve(context)\n        except template.VariableDoesNotExist:\n            return ''\n\n        if not image:\n            return ''\n\n        try:\n            rendition = image.get_rendition(self.filter)\n        except SourceImageIOError:\n            # It's fairly routine for people to pull down remote databases to their\n            # local dev versions without retrieving the corresponding image files.\n            # In such a case, we would get a SourceImageIOError at the point where we try to\n            # create the resized version of a non-existent image. Since this is a\n            # bit catastrophic for a missing image, we'll substitute a dummy\n            # Rendition object so that we just output a broken link instead.\n            Rendition = image.renditions.model  # pick up any custom Image / Rendition classes that may be in use\n            rendition = Rendition(image=image, width=0, height=0)\n            rendition.file.name = 'not-found'\n\n        if self.output_var_name:\n            # return the rendition object in the given variable\n            context[self.output_var_name] = rendition\n            return ''\n        else:\n            # render the rendition's image tag now\n            resolved_attrs = {}\n            for key in self.attrs:\n                resolved_attrs[key] = self.attrs[key].resolve(context)\n            return rendition.img_tag(resolved_attrs)\n", "path": "wagtail/wagtailimages/templatetags/wagtailimages_tags.py"}], "after_files": [{"content": "import operator\nimport functools\nfrom optparse import make_option\n\nfrom django.core.management.base import BaseCommand\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.db import models\nfrom django.db.models import Q\nfrom django.utils import six\n\nfrom wagtail.wagtailcore.models import Page\n\n\nclass Command(BaseCommand):\n    help = \"Checks for data integrity errors on the page tree, and fixes them where possible.\"\n    base_options = (\n        make_option('--noinput', action='store_false', dest='interactive', default=True,\n            help='If provided, any fixes requiring user interaction will be skipped.'\n        ),\n    )\n    option_list = BaseCommand.option_list + base_options\n\n    def numberlist_to_string(self, numberlist):\n        # Converts a list of numbers into a string\n        # Doesn't put \"L\" after longs\n        return '[' + ', '.join(map(str, numberlist)) + ']'\n\n    def handle(self, **options):\n        any_problems_fixed = False\n\n        for page in Page.objects.all():\n            try:\n                page.specific\n            except ObjectDoesNotExist:\n                self.stdout.write(\"Page %d (%s) is missing a subclass record; deleting.\" % (page.id, page.title))\n                any_problems_fixed = True\n                page.delete()\n\n        (bad_alpha, bad_path, orphans, bad_depth, bad_numchild) = Page.find_problems()\n\n        if bad_depth:\n            self.stdout.write(\"Incorrect depth value found for pages: %s\" % self.numberlist_to_string(bad_depth))\n        if bad_numchild:\n            self.stdout.write(\"Incorrect numchild value found for pages: %s\" % self.numberlist_to_string(bad_numchild))\n\n        if bad_depth or bad_numchild:\n            Page.fix_tree(destructive=False)\n            any_problems_fixed = True\n\n        if orphans:\n            # The 'orphans' list as returned by treebeard only includes pages that are\n            # missing an immediate parent; descendants of orphans are not included.\n            # Deleting only the *actual* orphans is a bit silly (since it'll just create\n            # more orphans), so generate a queryset that contains descendants as well.\n            orphan_paths = Page.objects.filter(id__in=orphans).values_list('path', flat=True)\n            filter_conditions = []\n            for path in orphan_paths:\n                filter_conditions.append(Q(path__startswith=path))\n\n            # combine filter_conditions into a single ORed condition\n            final_filter = functools.reduce(operator.or_, filter_conditions)\n\n            # build a queryset of all pages to be removed; this must be a vanilla Django\n            # queryset rather than a treebeard MP_NodeQuerySet, so that we bypass treebeard's\n            # custom delete() logic that would trip up on the very same corruption that we're\n            # trying to fix here.\n            pages_to_delete = models.query.QuerySet(Page).filter(final_filter)\n\n            self.stdout.write(\"Orphaned pages found:\")\n            for page in pages_to_delete:\n                self.stdout.write(\"ID %d: %s\" % (page.id, page.title))\n            self.stdout.write('')\n\n            if options.get('interactive', True):\n                yes_or_no = six.moves.input(\"Delete these pages? [y/N] \")\n                delete_orphans = yes_or_no.lower().startswith('y')\n                self.stdout.write('')\n            else:\n                # Running tests, check for the \"delete_orphans\" option\n                delete_orphans = options.get('delete_orphans', False)\n\n            if delete_orphans:\n                deletion_count = len(pages_to_delete)\n                pages_to_delete.delete()\n                self.stdout.write(\n                    \"%d orphaned page%s deleted.\" % (deletion_count, \"s\"[deletion_count==1:])\n                )\n                any_problems_fixed = True\n\n        if any_problems_fixed:\n            # re-run find_problems to see if any new ones have surfaced\n            (bad_alpha, bad_path, orphans, bad_depth, bad_numchild) = Page.find_problems()\n\n        if any((bad_alpha, bad_path, orphans, bad_depth, bad_numchild)):\n            self.stdout.write(\"Remaining problems (cannot fix automatically):\")\n            if bad_alpha:\n                self.stdout.write(\"Invalid characters found in path for pages: %s\" % self.numberlist_to_string(bad_alpha))\n            if bad_path:\n                self.stdout.write(\"Invalid path length found for pages: %s\" % self.numberlist_to_string(bad_path))\n            if orphans:\n                self.stdout.write(\"Orphaned pages found: %s\" % self.numberlist_to_string(orphans))\n            if bad_depth:\n                self.stdout.write(\"Incorrect depth value found for pages: %s\" % self.numberlist_to_string(bad_depth))\n            if bad_numchild:\n                self.stdout.write(\"Incorrect numchild value found for pages: %s\" % self.numberlist_to_string(bad_numchild))\n\n        elif any_problems_fixed:\n            self.stdout.write(\"All problems fixed.\")\n        else:\n            self.stdout.write(\"No problems found.\")\n", "path": "wagtail/wagtailcore/management/commands/fixtree.py"}, {"content": "from django import template\nfrom django.utils.functional import cached_property\n\nfrom wagtail.wagtailimages.models import Filter, SourceImageIOError\n\nregister = template.Library()\n\n\n@register.tag(name=\"image\")\ndef image(parser, token):\n    bits = token.split_contents()[1:]\n    image_var = bits[0]\n    filter_spec = bits[1]\n    bits = bits[2:]\n\n    if len(bits) == 2 and bits[0] == 'as':\n        # token is of the form {% image self.photo max-320x200 as img %}\n        return ImageNode(image_var, filter_spec, output_var_name=bits[1])\n    else:\n        # token is of the form {% image self.photo max-320x200 %} - all additional tokens\n        # should be kwargs, which become attributes\n        attrs = {}\n        for bit in bits:\n            try:\n                name, value = bit.split('=')\n            except ValueError:\n                raise template.TemplateSyntaxError(\"'image' tag should be of the form {% image self.photo max-320x200 [ custom-attr=\\\"value\\\" ... ] %} or {% image self.photo max-320x200 as img %}\")\n            attrs[name] = parser.compile_filter(value) # setup to resolve context variables as value\n\n        return ImageNode(image_var, filter_spec, attrs=attrs)\n\n\nclass ImageNode(template.Node):\n    def __init__(self, image_var_name, filter_spec, output_var_name=None, attrs={}):\n        self.image_var = template.Variable(image_var_name)\n        self.output_var_name = output_var_name\n        self.attrs = attrs\n        self.filter_spec = filter_spec\n\n    @cached_property\n    def filter(self):\n        _filter, _ = Filter.objects.get_or_create(spec=self.filter_spec)\n        return _filter\n\n    def render(self, context):\n        try:\n            image = self.image_var.resolve(context)\n        except template.VariableDoesNotExist:\n            return ''\n\n        if not image:\n            return ''\n\n        try:\n            rendition = image.get_rendition(self.filter)\n        except SourceImageIOError:\n            # It's fairly routine for people to pull down remote databases to their\n            # local dev versions without retrieving the corresponding image files.\n            # In such a case, we would get a SourceImageIOError at the point where we try to\n            # create the resized version of a non-existent image. Since this is a\n            # bit catastrophic for a missing image, we'll substitute a dummy\n            # Rendition object so that we just output a broken link instead.\n            Rendition = image.renditions.model  # pick up any custom Image / Rendition classes that may be in use\n            rendition = Rendition(image=image, width=0, height=0)\n            rendition.file.name = 'not-found'\n\n        if self.output_var_name:\n            # return the rendition object in the given variable\n            context[self.output_var_name] = rendition\n            return ''\n        else:\n            # render the rendition's image tag now\n            resolved_attrs = {}\n            for key in self.attrs:\n                resolved_attrs[key] = self.attrs[key].resolve(context)\n            return rendition.img_tag(resolved_attrs)\n", "path": "wagtail/wagtailimages/templatetags/wagtailimages_tags.py"}]}