Skip to content

Commit

Permalink
Provide MySQL and SQLite compat for Refactored plugin tree (#6437)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Kamycki committed Jul 27, 2018
1 parent 83d38db commit f5db7b0
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 25 deletions.
64 changes: 53 additions & 11 deletions cms/models/placeholdermodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -748,14 +748,56 @@ def _shift_plugin_positions(self, language, start, offset=None):
def _recalculate_plugin_positions(self, language):
from cms.models import CMSPlugin
cursor = CMSPlugin._get_database_cursor('write')
sql = (
'UPDATE {0} '
'SET position = RowNbrs.RowNbr '
'FROM ('
'SELECT ID, ROW_NUMBER() OVER (ORDER BY position) AS RowNbr '
'FROM {0} WHERE placeholder_id=%s AND language=%s '
') RowNbrs '
'WHERE {0}.id=RowNbrs.id'
)
sql = sql.format(connection.ops.quote_name(CMSPlugin._meta.db_table))
cursor.execute(sql, [self.pk, language])
db_vendor = CMSPlugin.get_database_vendor('write')

if db_vendor == 'sqlite':
sql = (
'CREATE TEMPORARY TABLE temp AS '
'SELECT ID, ('
'SELECT COUNT(*)+1 FROM {0} t WHERE '
'placeholder_id={0}.placeholder_id AND language={0}.language '
'AND {0}.position > t.position'
') AS new_position '
'FROM {0} WHERE placeholder_id=%s AND language=%s'
)
sql = sql.format(connection.ops.quote_name(CMSPlugin._meta.db_table))
cursor.execute(sql, [self.pk, language])

sql = (
'UPDATE {0} '
'SET position = (SELECT new_position FROM temp WHERE id={0}.id) '
'WHERE placeholder_id=%s AND language=%s'
)
sql = sql.format(connection.ops.quote_name(CMSPlugin._meta.db_table))
cursor.execute(sql, [self.pk, language])

sql = 'DROP TABLE temp'
sql = sql.format(connection.ops.quote_name(CMSPlugin._meta.db_table))
cursor.execute(sql)
elif db_vendor == 'postgresql':
sql = (
'UPDATE {0} '
'SET position = RowNbrs.RowNbr '
'FROM ('
'SELECT ID, ROW_NUMBER() OVER (ORDER BY position) AS RowNbr '
'FROM {0} WHERE placeholder_id=%s AND language=%s '
') RowNbrs '
'WHERE {0}.id=RowNbrs.id'
)
sql = sql.format(connection.ops.quote_name(CMSPlugin._meta.db_table))
cursor.execute(sql, [self.pk, language])
elif db_vendor == 'mysql':
sql = (
'UPDATE {0} '
'SET position = ('
'SELECT COUNT(*)+1 FROM (SELECT * FROM {0}) t '
'WHERE placeholder_id={0}.placeholder_id AND language={0}.language '
'AND {0}.position > t.position'
') WHERE placeholder_id=%s AND language=%s'
)
sql = sql.format(connection.ops.quote_name(CMSPlugin._meta.db_table))
cursor.execute(sql, [self.pk, language])
else:
raise RuntimeError(
'{} is not supported by django-cms'.format(connection.vendor)
)
50 changes: 38 additions & 12 deletions cms/models/pluginmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,18 @@ def get_database_vendor(cls, action):
def _get_database_cursor(cls, action):
return cls._get_database_connection(action).cursor()

@classmethod
def NO_CTE_SUPPORT(cls):
# This has to be as function because when it's a var it evaluates before
# db is connected and we get OperationalError. MySQL version is retrived
# from db, and it's cached_property.
connection = cls._get_database_connection('read')
db_vendor = cls.get_database_vendor('read')
return (
db_vendor == 'sqlite' and
connection.Database.sqlite_version_info < (3, 8, 3)
) or db_vendor == 'mysql' and connection.mysql_version < (8, 0)

def get_plugin_name(self):
from cms.plugin_pool import plugin_pool

Expand Down Expand Up @@ -290,20 +302,34 @@ def reload(self):
return CMSPlugin.objects.get(pk=self.pk)

def _get_descendants_count(self):
cursor = CMSPlugin._get_database_cursor('write')
sql = _get_descendants_cte() + '\n'
sql += 'SELECT COUNT(*) FROM descendants;'
sql = sql.format(connection.ops.quote_name(CMSPlugin._meta.db_table))
cursor.execute(sql, [self.pk])
return cursor.fetchall()[0][0]
if self.NO_CTE_SUPPORT():
return len(self._get_descendants_ids())
else:
cursor = CMSPlugin._get_database_cursor('write')
sql = _get_descendants_cte() + '\n'
sql += 'SELECT COUNT(*) FROM descendants;'
sql = sql.format(connection.ops.quote_name(CMSPlugin._meta.db_table))
cursor.execute(sql, [self.pk])
return cursor.fetchall()[0][0]

def _get_descendants_ids(self):
cursor = CMSPlugin._get_database_cursor('write')
sql = _get_descendants_cte() + '\n'
sql += 'SELECT id FROM descendants;'
sql = sql.format(connection.ops.quote_name(CMSPlugin._meta.db_table))
cursor.execute(sql, [self.pk])
return [item[0] for item in cursor.fetchall()]
if self.NO_CTE_SUPPORT():
descendants = []
childrens = self.get_children().values_list('pk', flat=True)
descendants.extend(childrens)
while childrens:
childrens = CMSPlugin.objects.filter(
parent__in=childrens,
).values_list('pk', flat=True)
descendants.extend(childrens)
return descendants
else:
cursor = CMSPlugin._get_database_cursor('write')
sql = _get_descendants_cte() + '\n'
sql += 'SELECT id FROM descendants;'
sql = sql.format(connection.ops.quote_name(CMSPlugin._meta.db_table))
cursor.execute(sql, [self.pk])
return [item[0] for item in cursor.fetchall()]

def get_children(self):
return self.cmsplugin_set.all()
Expand Down
4 changes: 2 additions & 2 deletions cms/tests/test_rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from cms import plugin_rendering
from cms.api import create_page, add_plugin
from cms.cache.placeholder import get_placeholder_cache
from cms.models import Page, Placeholder, CMSPlugin
from cms.models import Page, Placeholder
from cms.plugin_rendering import PluginContext
from cms.test_utils.project.placeholderapp.models import Example1
from cms.test_utils.testcases import CMSTestCase
Expand Down Expand Up @@ -258,7 +258,7 @@ def test_processors(self):
from djangocms_text_ckeditor.cms_plugins import TextPlugin
from cms.plugin_pool import plugin_pool

instance = CMSPlugin.objects.all()[0].get_plugin_instance()[0]
instance = self.test_placeholders['main'].get_plugins('en').first().get_bound_plugin()

load_from_string = self.load_template_from_string

Expand Down

0 comments on commit f5db7b0

Please sign in to comment.