question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Custom models using jQuery doesn't work

See original GitHub issue

Found a bug? Please fill out the sections below. 👍

Issue Summary

I’m trying to use a custom modelfield inside Wagtail. In my case, Django-Recurrence.

I then get greeted with the following error, which I believe is caused by the jQuery dependency, colliding with Wagtail’s React use.

Screenshot 2022-03-04 at 14 00 19

Here’s my models.py

from django.db import models
from recurrence.fields import RecurrenceField
from modelcluster.fields import ParentalKey
from wagtail.core import hooks
from wagtail.core.models import Page, Orderable
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel, MultiFieldPanel, InlinePanel
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.search import index

class PartnerPage(Page):
    logo = models.ForeignKey(
        'wagtailimages.Image', related_name='partner_logo', null=True, blank=True, on_delete=models.SET_NULL
    )
    banner = models.ForeignKey(
        'wagtailimages.Image', related_name='partner_banner', null=True, blank=True, on_delete=models.SET_NULL
    )
    body = RichTextField(null=True, blank=True)
    feed_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='feed_image'
    )
    

    search_fields = Page.search_fields + [
        index.SearchField('body'),
    ]

    content_panels = Page.content_panels + [
        ImageChooserPanel('logo'),
        ImageChooserPanel('banner'),
        FieldPanel('body', classname="full"),
        InlinePanel('gallery', label="Partner Gallery"),
    ]

    promote_panels = [
        MultiFieldPanel(Page.promote_panels, "Common page configuration"),
        ImageChooserPanel('feed_image'),
    ]

    settings_panels = [
        MultiFieldPanel(Page.settings_panels, "Common page configuration"),
        InlinePanel('events', label="Events", heading="Sorry this doesn't work yet in Wagtail, use Django-admin instead"),
        
    ]


class PartnerPageRelatedLink(Orderable):
    page = ParentalKey(PartnerPage, on_delete=models.CASCADE, related_name='related_links')
    name = models.CharField(max_length=255)
    url = models.URLField()

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


class PartnerGallery(models.Model):
    page = ParentalKey(PartnerPage, on_delete=models.CASCADE, related_name='gallery')
    image = models.ForeignKey(
        'wagtailimages.Image', on_delete=models.CASCADE, related_name='+'
    )
    caption = models.CharField(blank=True, max_length=250)

    panels = [
        ImageChooserPanel('image'),
        FieldPanel('caption'),
    ]

class PartnerEvents(models.Model):
    partner = ParentalKey(PartnerPage, on_delete=models.CASCADE, related_name='events')
    name = models.CharField(max_length=255, null=True, blank=True)
    start_time = models.TimeField(null=True, blank=True)
    end_time = models.TimeField(null=True, blank=True)
    recurrences = RecurrenceField(null=True, blank=True)

    panels = [
        FieldPanel('name'),
        FieldPanel('start_time'),
        FieldPanel('end_time'),
        FieldPanel('recurrences'),
    ]
    
    def __str__(self):
        return self.name


@hooks.register('insert_editor_js')
def editor_js():
    return '<script src="/django-admin/jsi18n/"></script>'

Steps to Reproduce

  1. On an existing or new Django / Wagtail entity run pip install django-recurrence
  2. Setup Django Recurrence using these instructions.
  3. Add a hook to the Wagtail model where you want to use Django Recurrence. (as seen in this issue https://github.com/jazzband/django-recurrence/issues/99)
  4. There’s where it stops.

Any other relevant information. For example, why do you consider this a bug and what did you expect to happen instead?

  • I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: yes

Technical details

  • Python version: 3.10
  • Django version: 4.0.3.
  • Wagtail version: Look at the bottom of the Settings menu in the Wagtail admin, or run pip show wagtail | grep Version: 2.16.1 .
  • Browser version: Safari 15
  • Django-recurrence version: 1.11.1

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
lb-commented, Mar 5, 2022

@ronaldlangeveld as per above, I do not think this is a bug with InlinePanel or Wagtail but rather a factor of a few things

  1. Documentation for InlinePanel - it could be clearer how this functionality works with JavaScript initialisation, but work is already underway to address common init script issues https://github.com/wagtail/rfcs/pull/33 and how we can refactor our existing JS https://github.com/wagtail/wagtail/discussions/7689#discussioncomment-2037913

  2. Django Recurrence JavaScript issues - it appears that the code as built in Django recurrence does not handle text nodes being added dynamically either side of added elements. This could be improved on that library but I understand why that has probably not come up yet.

    • If you want to maybe revise https://github.com/jazzband/django-recurrence/issues/198 to be focused on the text node issue that would be a good idea I think - the bug raised on that repo in its current state is unlikely to get looked at as it is a bit vague.
    • I also recommend you raise a new issue with Django Recurrence that asks for the #container requirement to be documented, it probably works as is in Django’s modeladmin but maybe could be clearer in the docs or better yet, configurable somehow.
1reaction
lb-commented, Mar 5, 2022

@ronaldlangeveld ok, there are more problems with the recurrence code, it looks like it has been set up to work with dynamic content in a #container element, however it does not filter out text nodes.

Here is a really rough way to get this working, I have just done a minimal version of your model, created a ContainerFieldRowPanel that will wrap the InlinePanel and uses it to add a script and an id of container on the wrapper.

https://github.com/lb-/bakerydemo/commit/ccdd48ee520f40301453eb4e0bd3ca5ffd984b78

models.py

from django.db import models

from modelcluster.fields import ParentalKey
from recurrence.fields import RecurrenceField

from wagtail.admin.edit_handlers import (FieldPanel, FieldRowPanel, InlinePanel)
from wagtail.core.models import Page, Orderable


class ContainerFieldRowPanel(FieldRowPanel):
    template = "events/edit_handlers/container_field_row_panel.html"


class ConferencePage(Page):
    content_panels = Page.content_panels + [
        ContainerFieldRowPanel([InlinePanel('events', min_num=1)]),
    ]


class Event(Orderable, models.Model):
    page = ParentalKey(
        'events.ConferencePage',
        related_name='events',
        on_delete=models.CASCADE
    )
    name = models.CharField(max_length=255, null=True, blank=True)
    recurrences = RecurrenceField(null=True, blank=True)

    panels = [
        FieldPanel('name'),
        FieldPanel('recurrences'),
    ]
    
    def __str__(self):
        return self.name

templates/events/edit_handlers/container_field_row_panel.html

<div id="container">
  <script>
		// IMPORTANT: this script should be moved to an isolated file so it does not load every time this panel is used
    document.addEventListener("DOMContentLoaded", function () {
      const targetNode = document.getElementById("container");
      const config = { attributes: false, childList: true, subtree: true };
      const callback = function (mutationsList, observer2) {
        for (const mutation of mutationsList) {
          if (mutation.type === "childList") {
            // Check if nodes were added
            // ADDED - also ignore nodes that are only text nodes
            const addedNodes = Array.from(mutation.addedNodes).filter(function (
              element
            ) {
              return element.nodeName !== "#text";
            });
            if (addedNodes.length > 0) {
              const $addedRecurrenceField = django
                .jQuery(addedNodes[0])
                .find(".recurrence-widget");
              console.log("$addedRecurrenceField", $addedRecurrenceField);
              // Length has to be 1, to prevent cases when draging inlines returns length 0 or more than 1.
              if ($addedRecurrenceField.length === 1) {
                initRecurrenceWidget($addedRecurrenceField);
              }
            }
          }
        }
      };
      // cannot use observer as the recurrence code adds a global
      const observer2 = new MutationObserver(callback);
      observer2.observe(targetNode, config);
    });
  </script>
  {% include "wagtailadmin/edit_handlers/field_row_panel.html" %}
</div>
Read more comments on GitHub >

github_iconTop Results From Across the Web

Jquery doesn't work on the bottom, but works in the head?
Jquery library needs to be loaded before you can load your custom js file which has jquery code in it. For example if...
Read more >
Backbone.js
Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable ...
Read more >
.hide() | jQuery API Documentation
When a custom queue name is used the animation does not automatically start; you must call .dequeue("queuename") to start it. specialEasing.
Read more >
JavaScript - Bootstrap
JavaScript. Bring Bootstrap's components to life with over a dozen custom jQuery plugins. Easily include them all, or one by one.
Read more >
Handling common JavaScript problems - MDN Web Docs
jQuery (or whatever library you are using) will then handle the ... in their code, and find that such features don't work in...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found