django-cms / djangocms-bootstrap4

django CMS Bootstrap 4 is a plugin bundle for django CMS providing several components from the popular Bootstrap 4 framework.
https://www.django-cms.org/
Other
82 stars 57 forks source link

Extending carousel causes plugin fail to copy #91

Closed weijiangan closed 5 years ago

weijiangan commented 5 years ago

I extended the carousel slide plugin so it can autofill the slide content from another internal model of my application. However, doing so seemingly causes the cms copy operation to fail when I publish and bug out when I try to add new slides after I publish.

class ArtCarouselSlide(Bootstrap4CarouselSlide):
    artwork = models.ForeignKey(
        Artwork,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
    )

    def save(self):
        self.carousel_image = self.artwork.picture
        self.carousel_content = (
            f"<p><em><strong>{self.artwork.title}</strong></em></p>"
            f"<p>{self.artwork.artist.first_name} {self.artwork.artist.last_name}, {self.artwork.year}</p>"
        )
        super().save()

Here's the plugin code:

@plugin_pool.register_plugin
class ArtCarouselSlidePlugin(Bootstrap4CarouselSlidePlugin):
    model = ArtCarouselSlide
    name = _('Art carousel slide')
    module = _('Gallery')
    parent_classes = ['ArtCarouselPlugin']

    fieldsets = [
        (None, {
            'fields': ('artwork', )
        }),
        (_('Link settings'), {
            'classes': ('collapse', ),
            'fields': (
                ('external_link', 'internal_link'),
                ('mailto', 'phone'),
                ('anchor', 'target'),
            )
        }),
        (_('Advanced settings'), {
            'classes': ('collapse', ),
            'fields': (
                'tag_type',
                'attributes',
            )
        }),
    ]

I also subclassed Carousel plugin so it can accept my new child plugin:

@plugin_pool.register_plugin
class ArtCarouselPlugin(Bootstrap4CarouselPlugin):
    name = _('Art carousel')
    module = _('Gallery')
    child_classes = ['Bootstrap4CarouselSlidePlugin', 'ArtCarouselSlidePlugin']

I have a hard time finding out why because it doesn't throw any error and my only clue so far is when I looked into the cms_cmsplugin table in postgres the numchild property indicates a wrong value, which is the value it should have if it copied successfully. If I set the value to 0, which accurately reflects the number of children (since it failed to make copies of its children), I can then add new slides again. Nonetheless, if I press publish, the same problem will repeat again.

This is the error it throws when I try to add new slides after publishing:

Traceback (most recent call last):
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/contrib/staticfiles/handlers.py", line 63, in __call__
    return self.application(environ, start_response)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/core/handlers/wsgi.py", line 157, in __call__
    response = self.get_response(request)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/core/handlers/base.py", line 124, in get_response
    response = self._middleware_chain(request)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/core/handlers/exception.py", line 43, in inner
    response = response_for_exception(request, exc)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/core/handlers/exception.py", line 93, in response_for_exception
    response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/core/handlers/exception.py", line 139, in handle_uncaught_exception
    return debug.technical_500_response(request, *exc_info)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django_extensions/management/technical_response.py", line 37, in null_technical_500_response
    six.reraise(exc_type, exc_value, tb)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/six.py", line 692, in reraise
    raise value.with_traceback(tb)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/utils/decorators.py", line 149, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/views/decorators/cache.py", line 57, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/contrib/admin/sites.py", line 224, in inner
    return view(request, *args, **kwargs)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/views/decorators/clickjacking.py", line 39, in wrapped_view
    resp = view_func(*args, **kwargs)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/cms/admin/placeholderadmin.py", line 342, in add_plugin
    response = plugin_instance.add_view(request)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/contrib/admin/options.py", line 1509, in add_view
    return self.changeform_view(request, None, form_url, extra_context)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/utils/decorators.py", line 67, in _wrapper
    return bound_func(*args, **kwargs)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/utils/decorators.py", line 149, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/utils/decorators.py", line 63, in bound_func
    return func.__get__(self, type(self))(*args2, **kwargs2)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/contrib/admin/options.py", line 1409, in changeform_view
    return self._changeform_view(request, object_id, form_url, extra_context)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/contrib/admin/options.py", line 1449, in _changeform_view
    self.save_model(request, new_object, form, not add)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/cms/plugin_base.py", line 338, in save_model
    return super(CMSPluginBase, self).save_model(request, obj, form, change)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/django/contrib/admin/options.py", line 980, in save_model
    obj.save()
  File "/Users/user01/Work/abc/website/abc/gallery/models/carousel.py", line 24, in save
    super().save()
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/cms/models/pluginmodel.py", line 262, in save
    self.parent.add_child(instance=self)
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/treebeard/mp_tree.py", line 1013, in add_child
    return MP_AddChildHandler(self, **kwargs).process()
  File "/Users/user01/Work/abc/website/env/lib/python3.7/site-packages/treebeard/mp_tree.py", line 387, in process
    newobj.path = self.node.get_last_child()._inc_path()
AttributeError: 'NoneType' object has no attribute '_inc_path'
FinalAngel commented 5 years ago

@chaosk do you know if we can even extend an existing plugin? AFAIK you'd need to do an abstract model and then extend from there similar to djangocms-picture or djangocms-link.

chaosk commented 5 years ago

There's a few tickets that I could find that relate to that issue:

You're right, extending concrete plugin models does not work, you can only do that with abstract models (or if the model you're creating is a proxy model)

FinalAngel commented 5 years ago

Closing this than, it's not intended here to make these plugins extendable similar to picture or link unless there is a great demand for it :)