Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for filter expression in GroupConcat #948

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
12 changes: 11 additions & 1 deletion src/django_mysql/models/aggregates.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class GroupConcat(Aggregate):
def __init__(
self,
expression: Expression,
filter=None,
caramdache marked this conversation as resolved.
Show resolved Hide resolved
distinct: bool = False,
separator: str | None = None,
ordering: str | None = None,
Expand All @@ -38,7 +39,7 @@ def __init__(
# This can/will be improved to SetTextField or ListTextField
extra["output_field"] = CharField()

super().__init__(expression, **extra)
super().__init__(expression, filter=filter, **extra)

self.distinct = distinct
self.separator = separator
Expand All @@ -53,6 +54,15 @@ def as_sql(
connection: BaseDatabaseWrapper,
**extra_context: Any,
) -> tuple[str, tuple[Any, ...]]:
if self.filter:
extra_context["distinct"] = "DISTINCT " if self.distinct else ""
copy = self.copy()
copy.filter = None
source_expressions = copy.get_source_expressions()
condition = When(self.filter, then=source_expressions[0])
copy.set_source_expressions([Case(condition)] + source_expressions[1:])
return super(Aggregate, copy).as_sql(compiler, connection, **extra_context)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked into it, but it's a bit odd that you're skipping the existing implementation and calling super(). The implemetnation below covers the SQL oddities in GROUP_CONCAT, I think we'd still want to use it, just tweak adding the filter there.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we need keep the existing pathway in the long term. It's probably better to let Aggregate.as_sql() perform the work based on the template.

If you look at the code for Count, you'll see there are 2 paths, one when filter is set, the other when it's not. I think we should have a similar structure here, but avoid manual handcrafting as much as possible because this is likely fragile.


connection.ops.check_expression_support(self)
sql = ["GROUP_CONCAT("]
if self.distinct:
Expand Down