Description
Description
If a plugin model inherits from another non-abstract plugin model (which is not CMSPlugin
itself), its plugin instances cannot be copied on CMS pages. Instead, an error is raised.
Steps to reproduce
- Define a plugin model structure like this
from cms.models import CMSPlugin
class Direct(CMSPlugin): # non-abstract
...
class Subclass(Direct): # non-abstract
...
- Define a CMS Plugin that uses the
Subclass
model - Use an instance of said plugin on a page
- Try to copy the instance in the structure panel
- See error
Expected behaviour
The plugin should be copied to the clipboard and should be able to be pasted somewhere else in the structure panel.
Actual behaviour
An exception is raised while trying to copy, which originates here:
AttributeError: 'CMSPlugin' object has no attribute 'cmsplugin_ptr_id'
Additional information (CMS/Python/Django versions)
Python 3.8
Django==3.1.3
django-cms==3.8.0
Regarding the line that raises the error:
plugin_instance.cmsplugin_ptr = new_plugin
It appears that, even though the attribute is called cmsplugin_ptr
, Django doesn't necessarily expect a CMSPlugin
instance here (which new_plugin
always is). Instead, Django seems to expect an instance of the closest ancestor (Direct
in my example above), which would then also have cmsplugin_ptr_id
attribute itself (hence the AttributeError
above). This is also why the code works for models that inherit directly from CMSPlugin
(or an abstract model), since CMSPlugin
is the closest ancestor in this case.
A simple fix may be to set the cmsplugin_ptr_id
instead - when dealing with the raw ID, the type of new_plugin
doesn't matter. With the line above changed to
plugin_instance.cmsplugin_ptr_id = new_plugin.pk
copying plugins seems to work in all inheritance scenarios in a short test. I may be able to provide a PR utilizing that fix if the issue is accepted.