Dan Swain
Dan Swain

Reputation: 3098

KeyError generated when editing site setting with foreign key set

Django Version: 2.1.5

Python Version: 3.6.8

Wagtail Version: 2.4

I have a template with four columns of links in the footer. I have set up the following models which consist of a BaseSetting object and footer link objects for each column of links. The footer link objects each ForeignKey to the TemplateItems object.

@register_setting
class TemplateItems(BaseSetting):
    page_banner = models.OneToOneField('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+',  help_text='Banner image that shows below menu on pages other than home page')
    footer_link_col1_header = models.CharField(max_length=25, default='', verbose_name='Footer Link Column 1 Header')
    footer_link_col2_header = models.CharField(max_length=25, blank=True, default='', verbose_name='Footer Link Column 2 Header')
    footer_link_col3_header = models.CharField(max_length=25, blank=True, default='', verbose_name='Footer Link Column 3 Header')
    footer_link_col4_header = models.CharField(max_length=25, blank=True, default='', verbose_name='Footer Link Column 4 Header')

    panels = [
        ImageChooserPanel('page_banner'),
        MultiFieldPanel([
            FieldPanel('footer_link_col1_header'),
            InlinePanel('footer_links_col_1', label='Column 1 Links'),
            FieldPanel('footer_link_col2_header'),
            InlinePanel('footer_links_col_2', label='Column 2 Links'),
            FieldPanel('footer_link_col3_header'),
            InlinePanel('footer_links_col_3', label='Column 3 Links'),
            FieldPanel('footer_link_col4_header'),
            InlinePanel('footer_links_col_4', label='Column 4 Links'),
        ], heading='Footer Links'),
        InlinePanel('social_media_links', label="Social Media Links"),
    ]


class FooterLink(Orderable):
    name = models.CharField(max_length=60, default='')
    url = models.CharField(max_length=200, default='')

    panels = [
        FieldRowPanel([
            FieldPanel('name'),
            FieldPanel('url'),
        ])
    ]

    class Meta:
        abstract = True

    def __str__(self):
        return f'{self.name}'


class FooterLinkCol1(FooterLink):
    template_items = ForeignKey('TemplateItems', related_name='footer_links_col_1', null=True, on_delete=models.SET_NULL)


class FooterLinkCol2(FooterLink):
    template_items = ForeignKey('TemplateItems', related_name='footer_links_col_2', null=True, on_delete=models.SET_NULL)


class FooterLinkCol3(FooterLink):
    template_items = ForeignKey('TemplateItems', related_name='footer_links_col_3', null=True, on_delete=models.SET_NULL)


class FooterLinkCol4(FooterLink):
    template_items = ForeignKey('TemplateItems', related_name='footer_links_col_4', null=True, on_delete=models.SET_NULL)

Migrations are created and migrated successfully, but when I go to the TemplateItems settings object in the Wagtail admin in order to add footer links, I receive the following error:

KeyError at /admin/settings/main/templateitems/2/

'footer_links_col_1'

If I comment out any of the footer_links_col_X items, then I receive the error for the first one that is not commented out. There are no existing footer links in the database for any of the columns. I wondered if the problem was coming because the ForeignKey is to a BaseSetting object, but when I declare these models in the Django admin (including the inlines for each of the column links), it displays and allows me to add links just fine.

Traceback:

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner 34. response = get_response(request)

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response 126. response = self.process_exception_by_middleware(e, request)

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response 124. response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/django/views/decorators/cache.py" in _wrapped_view_func 44. response = view_func(request, *args, **kwargs)

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/wagtail/admin/urls/init.py" in wrapper 102. return view_func(request, *args, **kwargs)

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/wagtail/admin/decorators.py" in decorated_view 34. return view_func(request, *args, **kwargs)

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/wagtail/contrib/settings/views.py" in edit 83. instance=instance, form=form, request=request)

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py" in bind_to_instance 153. new.on_instance_bound()

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py" in on_instance_bound 295. request=self.request))

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py" in bind_to_instance 153. new.on_instance_bound()

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py" in on_instance_bound 295. request=self.request))

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py" in bind_to_instance 153. new.on_instance_bound()

File "/opt/virtualenvs/MY_SITE-a0hNfZxl/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py" in on_instance_bound 692. self.formset = self.form.formsets[self.relation_name]

Exception Type: KeyError at /admin/settings/main/templateitems/2/ Exception Value: 'footer_links_col_1'

Upvotes: 2

Views: 1026

Answers (1)

gasman
gasman

Reputation: 25227

InlinePanel requires the corresponding foreign key to be a ParentalKey:

from modelcluster.fields import ParentalKey

class FooterLinkCol1(FooterLink):
    template_items = ParentalKey('TemplateItems', related_name='footer_links_col_1', null=True, on_delete=models.SET_NULL)

In turn, ParentalKey requires the parent model to inherit from ClusterableModel (which is automatically true for Wagtail Page models):

from modelcluster.models import ClusterableModel

class TemplateItems(BaseSetting, ClusterableModel):

(There's some explanation of the motivation for ClusterableModel / ParentalKey in the readme for django-modelcluster.)

Upvotes: 3

Related Questions