diff --git a/AUTHORS b/AUTHORS
a
|
b
|
|
535 | 535 | ye7cakf02@sneakemail.com |
536 | 536 | ymasuda@ethercube.com |
537 | 537 | Jesse Young <adunar@gmail.com> |
538 | 538 | Mykola Zamkovoi <nickzam@gmail.com> |
539 | 539 | zegor |
540 | 540 | Gasper Zejn <zejn@kiberpipa.org> |
541 | 541 | Jarek Zgoda <jarek.zgoda@gmail.com> |
542 | 542 | Cheng Zhang |
| 543 | Jeffrey Gelens <jeffrey@gelens.org> |
543 | 544 | |
544 | 545 | A big THANK YOU goes to: |
545 | 546 | |
546 | 547 | Rob Curley and Ralph Gage for letting us open-source Django. |
547 | 548 | |
548 | 549 | Frank Wiles for making excellent arguments for open-sourcing, and for |
549 | 550 | his sage sysadmin advice. |
550 | 551 | |
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
a
|
b
|
|
336 | 336 | |
337 | 337 | # Features that need to be confirmed at runtime |
338 | 338 | # Cache whether the confirmation has been performed. |
339 | 339 | _confirmed = False |
340 | 340 | supports_transactions = None |
341 | 341 | supports_stddev = None |
342 | 342 | can_introspect_foreign_keys = None |
343 | 343 | |
| 344 | # Support for the DISTINCT ON clause |
| 345 | can_distinct_on_fields = False |
| 346 | |
344 | 347 | def __init__(self, connection): |
345 | 348 | self.connection = connection |
346 | 349 | |
347 | 350 | def confirm(self): |
348 | 351 | "Perform manual checks of any database features that might vary between installs" |
349 | 352 | self._confirmed = True |
350 | 353 | self.supports_transactions = self._supports_transactions() |
351 | 354 | self.supports_stddev = self._supports_stddev() |
… |
… |
|
489 | 492 | def fulltext_search_sql(self, field_name): |
490 | 493 | """ |
491 | 494 | Returns the SQL WHERE clause to use in order to perform a full-text |
492 | 495 | search of the given field_name. Note that the resulting string should |
493 | 496 | contain a '%s' placeholder for the value being searched against. |
494 | 497 | """ |
495 | 498 | raise NotImplementedError('Full-text search is not implemented for this database backend') |
496 | 499 | |
| 500 | def distinct(self, db_table, fields): |
| 501 | """ |
| 502 | Returns an SQL DISTINCT clause which removes duplicate rows from the |
| 503 | result set. If any fields are given, only the given fields are being |
| 504 | checked for duplicates. |
| 505 | """ |
| 506 | if fields: |
| 507 | raise NotImplementedError('DISTINCT ON fields is not supported by this database backend') |
| 508 | else: |
| 509 | return 'DISTINCT' |
| 510 | |
497 | 511 | def last_executed_query(self, cursor, sql, params): |
498 | 512 | """ |
499 | 513 | Returns a string of the query last executed by the given cursor, with |
500 | 514 | placeholders replaced with actual values. |
501 | 515 | |
502 | 516 | `sql` is the raw query containing placeholders, and `params` is the |
503 | 517 | sequence of parameters. These are used by default, but this method |
504 | 518 | exists for database backends to provide a better implementation |
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
a
|
b
|
|
66 | 66 | class DatabaseFeatures(BaseDatabaseFeatures): |
67 | 67 | needs_datetime_string_cast = False |
68 | 68 | can_return_id_from_insert = True |
69 | 69 | requires_rollback_on_dirty_transaction = True |
70 | 70 | has_real_datatype = True |
71 | 71 | can_defer_constraint_checks = True |
72 | 72 | has_select_for_update = True |
73 | 73 | has_select_for_update_nowait = True |
| 74 | can_distinct_on_fields = True |
74 | 75 | |
75 | 76 | |
76 | 77 | class DatabaseWrapper(BaseDatabaseWrapper): |
77 | 78 | vendor = 'postgresql' |
78 | 79 | operators = { |
79 | 80 | 'exact': '= %s', |
80 | 81 | 'iexact': '= UPPER(%s)', |
81 | 82 | 'contains': 'LIKE %s', |
diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py
a
|
b
|
|
168 | 168 | macro in src/include/pg_config_manual.h . |
169 | 169 | |
170 | 170 | This implementation simply returns 63, but can easily be overridden by a |
171 | 171 | custom database backend that inherits most of its behavior from this one. |
172 | 172 | """ |
173 | 173 | |
174 | 174 | return 63 |
175 | 175 | |
| 176 | def distinct(self, db_table, fields): |
| 177 | if fields: |
| 178 | table_name = self.quote_name(db_table) |
| 179 | fields = [table_name + "." + self.quote_name(field) for field in fields] |
| 180 | return 'DISTINCT ON (%s)' % ', '.join(fields) |
| 181 | else: |
| 182 | return 'DISTINCT' |
| 183 | |
176 | 184 | def last_executed_query(self, cursor, sql, params): |
177 | 185 | # http://initd.org/psycopg/docs/cursor.html#cursor.query |
178 | 186 | # The query attribute is a Psycopg extension to the DB API 2.0. |
179 | 187 | return cursor.query |
180 | 188 | |
181 | 189 | def return_insert_id(self): |
182 | 190 | return "RETURNING %s", () |
diff --git a/django/db/models/query.py b/django/db/models/query.py
a
|
b
|
|
660 | 660 | """ |
661 | 661 | assert self.query.can_filter(), \ |
662 | 662 | "Cannot reorder a query once a slice has been taken." |
663 | 663 | obj = self._clone() |
664 | 664 | obj.query.clear_ordering() |
665 | 665 | obj.query.add_ordering(*field_names) |
666 | 666 | return obj |
667 | 667 | |
668 | | def distinct(self, true_or_false=True): |
| 668 | def distinct(self, *field_names): |
669 | 669 | """ |
670 | 670 | Returns a new QuerySet instance that will select only distinct results. |
671 | 671 | """ |
672 | 672 | obj = self._clone() |
673 | | obj.query.distinct = true_or_false |
| 673 | obj.query.add_distinct_fields(field_names) |
| 674 | obj.query.distinct = True |
| 675 | |
674 | 676 | return obj |
675 | 677 | |
676 | 678 | def extra(self, select=None, where=None, params=None, tables=None, |
677 | 679 | order_by=None, select_params=None): |
678 | 680 | """ |
679 | 681 | Adds extra SQL fragments to the query. |
680 | 682 | """ |
681 | 683 | assert self.query.can_filter(), \ |
… |
… |
|
1085 | 1087 | return self |
1086 | 1088 | |
1087 | 1089 | def order_by(self, *field_names): |
1088 | 1090 | """ |
1089 | 1091 | Always returns EmptyQuerySet. |
1090 | 1092 | """ |
1091 | 1093 | return self |
1092 | 1094 | |
1093 | | def distinct(self, true_or_false=True): |
| 1095 | def distinct(self, fields=None): |
1094 | 1096 | """ |
1095 | 1097 | Always returns EmptyQuerySet. |
1096 | 1098 | """ |
1097 | 1099 | return self |
1098 | 1100 | |
1099 | 1101 | def extra(self, select=None, where=None, params=None, tables=None, |
1100 | 1102 | order_by=None, select_params=None): |
1101 | 1103 | """ |
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
a
|
b
|
|
69 | 69 | |
70 | 70 | where, w_params = self.query.where.as_sql(qn=qn, connection=self.connection) |
71 | 71 | having, h_params = self.query.having.as_sql(qn=qn, connection=self.connection) |
72 | 72 | params = [] |
73 | 73 | for val in self.query.extra_select.itervalues(): |
74 | 74 | params.extend(val[1]) |
75 | 75 | |
76 | 76 | result = ['SELECT'] |
| 77 | |
77 | 78 | if self.query.distinct: |
78 | | result.append('DISTINCT') |
| 79 | distinct_sql = self.connection.ops.distinct( |
| 80 | self.query.model._meta.db_table, self.query.distinct_fields) |
| 81 | result.append(distinct_sql) |
| 82 | |
79 | 83 | result.append(', '.join(out_cols + self.query.ordering_aliases)) |
80 | 84 | |
81 | 85 | result.append('FROM') |
82 | 86 | result.extend(from_) |
83 | 87 | params.extend(f_params) |
84 | 88 | |
85 | 89 | if where: |
86 | 90 | result.append('WHERE %s' % where) |
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
a
|
b
|
|
120 | 120 | self.tables = [] # Aliases in the order they are created. |
121 | 121 | self.where = where() |
122 | 122 | self.where_class = where |
123 | 123 | self.group_by = None |
124 | 124 | self.having = where() |
125 | 125 | self.order_by = [] |
126 | 126 | self.low_mark, self.high_mark = 0, None # Used for offset/limit |
127 | 127 | self.distinct = False |
| 128 | self.distinct_fields = None |
128 | 129 | self.select_for_update = False |
129 | 130 | self.select_for_update_nowait = False |
130 | 131 | self.select_related = False |
131 | 132 | self.related_select_cols = [] |
132 | 133 | |
133 | 134 | # SQL aggregate-related attributes |
134 | 135 | self.aggregates = SortedDict() # Maps alias -> SQL aggregate function |
135 | 136 | self.aggregate_select_mask = None |
… |
… |
|
251 | 252 | if self.group_by is None: |
252 | 253 | obj.group_by = None |
253 | 254 | else: |
254 | 255 | obj.group_by = self.group_by[:] |
255 | 256 | obj.having = copy.deepcopy(self.having, memo=memo) |
256 | 257 | obj.order_by = self.order_by[:] |
257 | 258 | obj.low_mark, obj.high_mark = self.low_mark, self.high_mark |
258 | 259 | obj.distinct = self.distinct |
| 260 | obj.distinct_fields = self.distinct_fields |
259 | 261 | obj.select_for_update = self.select_for_update |
260 | 262 | obj.select_for_update_nowait = self.select_for_update_nowait |
261 | 263 | obj.select_related = self.select_related |
262 | 264 | obj.related_select_cols = [] |
263 | 265 | obj.aggregates = copy.deepcopy(self.aggregates, memo=memo) |
264 | 266 | if self.aggregate_select_mask is None: |
265 | 267 | obj.aggregate_select_mask = None |
266 | 268 | else: |
… |
… |
|
379 | 381 | in zip(query.aggregate_select.items(), result) |
380 | 382 | ]) |
381 | 383 | |
382 | 384 | def get_count(self, using): |
383 | 385 | """ |
384 | 386 | Performs a COUNT() query using the current filter constraints. |
385 | 387 | """ |
386 | 388 | obj = self.clone() |
387 | | if len(self.select) > 1 or self.aggregate_select: |
| 389 | if len(self.select) > 1 or self.aggregate_select or (self.distinct and self.distinct_fields): |
388 | 390 | # If a select clause exists, then the query has already started to |
389 | 391 | # specify the columns that are to be returned. |
390 | 392 | # In this case, we need to use a subquery to evaluate the count. |
391 | 393 | from django.db.models.sql.subqueries import AggregateQuery |
392 | 394 | subquery = obj |
393 | 395 | subquery.clear_ordering(True) |
394 | 396 | subquery.clear_limits() |
395 | 397 | |
… |
… |
|
1552 | 1554 | """ |
1553 | 1555 | Clears the list of fields to select (but not extra_select columns). |
1554 | 1556 | Some queryset types completely replace any existing list of select |
1555 | 1557 | columns. |
1556 | 1558 | """ |
1557 | 1559 | self.select = [] |
1558 | 1560 | self.select_fields = [] |
1559 | 1561 | |
| 1562 | def add_distinct_fields(self, field_names): |
| 1563 | self.distinct_fields = [] |
| 1564 | options = self.get_meta() |
| 1565 | |
| 1566 | for name in field_names: |
| 1567 | field, source, opts, join_list, last, _ = self.setup_joins( |
| 1568 | name.split(LOOKUP_SEP), options, self.get_initial_alias(), False) |
| 1569 | self.distinct_fields.append(field.column) |
| 1570 | |
1560 | 1571 | def add_fields(self, field_names, allow_m2m=True): |
1561 | 1572 | """ |
1562 | 1573 | Adds the given (model) fields to the select set. The field names are |
1563 | 1574 | added in the order specified. |
1564 | 1575 | """ |
1565 | 1576 | alias = self.get_initial_alias() |
1566 | 1577 | opts = self.get_meta() |
1567 | 1578 | |
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
a
|
b
|
|
134 | 134 | introspection: |
135 | 135 | |
136 | 136 | .. attribute:: ordered |
137 | 137 | |
138 | 138 | ``True`` if the ``QuerySet`` is ordered -- i.e. has an order_by() |
139 | 139 | clause or a default ordering on the model. ``False`` otherwise. |
140 | 140 | |
141 | 141 | .. attribute:: db |
142 | | |
| 142 | |
143 | 143 | The database that will be used if this query is executed now. |
144 | 144 | |
145 | 145 | .. note:: |
146 | 146 | |
147 | 147 | The ``query`` parameter to :class:`QuerySet` exists so that specialized |
148 | 148 | query subclasses such as |
149 | 149 | :class:`~django.contrib.gis.db.models.GeoQuerySet` can reconstruct |
150 | 150 | internal query state. The value of the parameter is an opaque |
… |
… |
|
340 | 340 | ``order_by()``). If no such ordering is defined for a given |
341 | 341 | ``QuerySet``, calling ``reverse()`` on it has no real effect (the |
342 | 342 | ordering was undefined prior to calling ``reverse()``, and will remain |
343 | 343 | undefined afterward). |
344 | 344 | |
345 | 345 | distinct |
346 | 346 | ~~~~~~~~ |
347 | 347 | |
348 | | .. method:: distinct() |
| 348 | .. method:: distinct(*fields) |
349 | 349 | |
350 | 350 | Returns a new ``QuerySet`` that uses ``SELECT DISTINCT`` in its SQL query. This |
351 | 351 | eliminates duplicate rows from the query results. |
352 | 352 | |
353 | 353 | By default, a ``QuerySet`` will not eliminate duplicate rows. In practice, this |
354 | 354 | is rarely a problem, because simple queries such as ``Blog.objects.all()`` |
355 | 355 | don't introduce the possibility of duplicate result rows. However, if your |
356 | 356 | query spans multiple tables, it's possible to get duplicate results when a |
357 | 357 | ``QuerySet`` is evaluated. That's when you'd use ``distinct()``. |
358 | 358 | |
| 359 | .. versionadded:: 1.4 |
| 360 | ``distinct()`` takes optional positional arguments ``*fields``, which specify |
| 361 | field names to which the ``DISTINCT`` should be limited. This translates to |
| 362 | a ``SELECT DISTINCT ON`` SQL query. Note that this ``DISTINCT ON`` query is |
| 363 | only available in PostgreSQL. |
| 364 | |
| 365 | .. note:: |
| 366 | When optional ``*fields`` are given, you will have to add an :meth:`order_by` |
| 367 | call with the same field names as the leftmost arguments. |
| 368 | |
359 | 369 | .. note:: |
360 | 370 | Any fields used in an :meth:`order_by` call are included in the SQL |
361 | 371 | ``SELECT`` columns. This can sometimes lead to unexpected results when |
362 | 372 | used in conjunction with ``distinct()``. If you order by fields from a |
363 | 373 | related model, those fields will be added to the selected columns and they |
364 | 374 | may make otherwise duplicate rows appear to be distinct. Since the extra |
365 | 375 | columns don't appear in the returned results (they are only there to |
366 | 376 | support ordering), it sometimes looks like non-distinct results are being |