|
3 | 3 |
|
4 | 4 | from django.conf import settings |
5 | 5 | from django.core.exceptions import ImproperlyConfigured, ValidationError |
6 | | -from django.db import models |
| 6 | +from django.db import connections, models |
7 | 7 | from django.db.models.aggregates import Aggregate |
8 | 8 | from django.db.models.expressions import F, Value |
9 | 9 | from django.db.models.fields import BooleanField, CharField |
10 | | -from django.db.models.functions import Lower |
| 10 | +from django.db.models.functions import Cast, Lower |
11 | 11 | from django.utils.functional import cached_property |
12 | 12 | from django.utils.translation import gettext_lazy as _ |
13 | 13 |
|
| 14 | +try: |
| 15 | + from django.contrib.postgres.aggregates import StringAgg |
| 16 | +except ImportError: # psycopg2 is not installed |
| 17 | + pass |
| 18 | + |
14 | 19 |
|
15 | 20 | class GroupConcat(Aggregate): |
| 21 | + """ |
| 22 | + To be used on SQLite, MySQL and MariaDB databases. For Postgres, use `StringAgg`. |
| 23 | + """ |
16 | 24 | function = 'GROUP_CONCAT' |
17 | 25 | template = '%(function)s(%(distinct)s %(expressions)s)' |
18 | | - # template = '%(function)s(%(distinct)s %(expressions)s%(separator)s)' |
19 | 26 | allow_distinct = True |
20 | 27 |
|
21 | | - def __init__(self, expression, distinct=False, ordering=None, separator=',', output_field=None, **extra): |
| 28 | + def __init__(self, expression, distinct=False, ordering=None, output_field=None, **extra): |
22 | 29 | super().__init__( |
23 | 30 | expression, |
24 | 31 | distinct='DISTINCT ' if distinct else '', |
25 | 32 | ordering=f' ORDER BY {ordering}' if ordering is not None else '', |
26 | | - separator=f",'{separator}'", |
27 | 33 | output_field=CharField() if output_field is None else output_field, |
28 | 34 | **extra |
29 | 35 | ) |
@@ -97,15 +103,21 @@ def get_queryset(model): |
97 | 103 | is_folder=Value(model.is_folder, output_field=BooleanField()), |
98 | 104 | **unified_annotations, |
99 | 105 | ) |
100 | | - expressions = {'label_ids': |
101 | | - Value(None, output_field=CharField()) if model.is_folder |
102 | | - else GroupConcat('labels__id', distinct=True) |
103 | | - } |
| 106 | + if model.is_folder: |
| 107 | + expressions = {'label_ids': Value('', output_field=CharField())} |
| 108 | + else: |
| 109 | + vendor = connections[model.objects.db].vendor |
| 110 | + if vendor == 'postgresql': |
| 111 | + concatenated = Cast('labels__id', output_field=CharField()) |
| 112 | + expressions = {'label_ids': StringAgg(concatenated, ',', distinct=True)} |
| 113 | + else: |
| 114 | + expressions = {'label_ids': GroupConcat('labels__id', distinct=True)} |
104 | 115 | for name, field in unified_fields.items(): |
105 | 116 | if name in annotations: |
106 | 117 | concrete_fields.remove(name) |
107 | 118 | elif name not in model_field_names: |
108 | | - expressions[name] = Value(None, output_field=field) |
| 119 | + value = None if field.default is models.NOT_PROVIDED else field.default |
| 120 | + expressions[name] = Value(value, output_field=field) |
109 | 121 | queryset = model.objects.values(*concrete_fields, **expressions).annotate(**annotations) |
110 | 122 | model_lookup = dict(lookup) |
111 | 123 | labels = model_lookup.pop('labels__in', None) |
|
0 commit comments