django-clickhouse-backend icon indicating copy to clipboard operation
django-clickhouse-backend copied to clipboard

[QUESTION] Django 5.2 Support

Open dorian-v opened this issue 7 months ago • 1 comments

Hello !

I've been using django-clickhouse-backend on one of my projects and it's been working great so far, so first of all thank you for your work :)

I was just wondering if there were any plans to support Django 5.2 in the near future ?

After updating it, I'm getting the following error with the ClickHouse version of the SQLInsertCompiler:

        (
>           self.field_as_sql(field, get_placeholder, value)
            for field, get_placeholder, value in zip(fields, get_placeholders, row)
        )
        for row in value_rows
    )
E   TypeError: SQLInsertCompiler.field_as_sql() takes 3 positional arguments but 4 were given

It seems the issue comes from this Django 5.2 commit: https://github.com/django/django/commit/2638b75554d2624dca3062a8da113a47f855f2a2 where they've changed the assemble_as_sql / field_as_sql to send/receive an additional get_placeholder parameter.

If this is the only issue, I can maybe look into suggesting a fix. However, any additional changes required might be difficult, hence my question :)

Thanks !

dorian-v avatar Apr 17 '25 15:04 dorian-v

Temporary workaround while a fix is on the way.

# project/patches/clickhouse.py

"""
Django 5.2 compatibility patch for Clickhouse backend.

This patch updates the Clickhouse backend's SQL compiler classes to be compatible with
Django 5.2's changes to the SQLCompiler classes.
"""

import logging
import types

logger = logging.getLogger(__name__)


def patched_sql_insert_compiler_field_as_sql(self, field, get_placeholder, val):
    """
    Take a field and a value intended to be saved on that field, and
    return placeholder SQL and accompanying params. Check for raw values,
    expressions, and fields with get_placeholder() defined in that order.

    When field is None, consider the value raw and use it as the
    placeholder, with no corresponding parameters returned.
    """
    if field is None:
        # A field value of None means the value is raw.
        sql, params = val, []
    elif hasattr(val, "as_sql"):
        # This is an expression, let's compile it.
        sql, params = self.compile(val)
    elif get_placeholder is not None:
        # Some fields (e.g. geo fields) need special munging before
        # they can be inserted.
        sql, params = get_placeholder(val, self, self.connection), [val]
    else:
        # Return the common case for the placeholder
        sql, params = "%s", [val]

    return sql, params


def apply_patch():
    """
    Apply the Django 5.2 compatibility patch to Clickhouse backend.
    """
    try:
        import clickhouse_backend.models.sql.compiler as ch_compiler
    except ImportError:
        logger.warning("Clickhouse backend not found, skipping patch")
        return

    # Check if the patch is needed by looking at the Django version
    import django

    if django.VERSION < (5, 2):
        logger.info("Django version is less than 5.2, no patch needed")
        return

    logger.info("Applying Django 5.2 compatibility patch to Clickhouse backend")

    # Patch SQLInsertCompiler
    insert_compiler = ch_compiler.SQLInsertCompiler
    insert_compiler.field_as_sql = types.MethodType(
        patched_sql_insert_compiler_field_as_sql, insert_compiler
    )

    logger.info("Django 5.2 compatibility patch applied to Clickhouse backend")

suquant avatar Apr 23 '25 12:04 suquant