Ticket #5805: index_together.diff

File index_together.diff, 7.1 KB (added by Jeffrey Gelens, 12 years ago)

Updated patch to match trunk

  • django/db/backends/creation.py

    diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py
    a b  
    133133                    (qn(r_table), qn(truncate_name(r_name, self.connection.ops.max_name_length())),
    134134                    qn(r_col), qn(table), qn(col),
    135135                    self.connection.ops.deferrable_sql()))
    136136            del pending_references[model]
    137137        return final_output
    138138
    139139    def sql_indexes_for_model(self, model, style):
    140140        "Returns the CREATE INDEX SQL statements for a single model"
     141        from django.db.backends.util import truncate_name
     142
    141143        if not model._meta.managed or model._meta.proxy:
    142144            return []
     145
     146        opts = model._meta
     147        qn = self.connection.ops.quote_name
    143148        output = []
     149
    144150        for f in model._meta.local_fields:
    145151            output.extend(self.sql_indexes_for_field(model, f, style))
     152
     153        for field_constraints in opts.index_together:
     154            i_name = '%s_%s' % (opts.db_table, self._digest(field_constraints))
     155
     156            output.append(
     157                style.SQL_KEYWORD('CREATE INDEX') + ' ' +
     158                style.SQL_TABLE(qn(truncate_name(i_name, self.connection.ops.max_name_length()))) + ' ' +
     159                style.SQL_KEYWORD('ON') + ' ' +
     160                style.SQL_TABLE(qn(opts.db_table)) + ' ' +
     161                "(%s);" % ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints])
     162            )
     163
    146164        return output
    147165
    148166    def sql_indexes_for_field(self, model, f, style):
    149167        "Return the CREATE INDEX SQL statements for a single model field"
    150168        from django.db.backends.util import truncate_name
    151169
    152170        if f.db_index and not f.unique:
    153171            qn = self.connection.ops.quote_name
  • django/db/models/options.py

    diff --git a/django/db/models/options.py b/django/db/models/options.py
    a b  
    1212from django.utils.datastructures import SortedDict
    1313
    1414# Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
    1515get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip()
    1616
    1717DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering',
    1818                 'unique_together', 'permissions', 'get_latest_by',
    1919                 'order_with_respect_to', 'app_label', 'db_tablespace',
    20                  'abstract', 'managed', 'proxy', 'auto_created')
     20                 'abstract', 'managed', 'proxy', 'auto_created', 'index_together')
    2121
    2222class Options(object):
    2323    def __init__(self, meta, app_label=None):
    2424        self.local_fields, self.local_many_to_many = [], []
    2525        self.virtual_fields = []
    2626        self.module_name, self.verbose_name = None, None
    2727        self.verbose_name_plural = None
    2828        self.db_table = ''
    2929        self.ordering = []
    3030        self.unique_together =  []
     31        self.index_together =  []
    3132        self.permissions =  []
    3233        self.object_name, self.app_label = None, app_label
    3334        self.get_latest_by = None
    3435        self.order_with_respect_to = None
    3536        self.db_tablespace = settings.DEFAULT_TABLESPACE
    3637        self.admin = None
    3738        self.meta = meta
    3839        self.pk = None
     
    7576                if name.startswith('_'):
    7677                    del meta_attrs[name]
    7778            for attr_name in DEFAULT_NAMES:
    7879                if attr_name in meta_attrs:
    7980                    setattr(self, attr_name, meta_attrs.pop(attr_name))
    8081                elif hasattr(self.meta, attr_name):
    8182                    setattr(self, attr_name, getattr(self.meta, attr_name))
    8283
    83             # unique_together can be either a tuple of tuples, or a single
     84            # unique_together and index_together can be either a tuple of tuples, or a single
    8485            # tuple of two strings. Normalize it to a tuple of tuples, so that
    8586            # calling code can uniformly expect that.
    8687            ut = meta_attrs.pop('unique_together', self.unique_together)
    8788            if ut and not isinstance(ut[0], (tuple, list)):
    8889                ut = (ut,)
    8990            self.unique_together = ut
    9091
     92            it = meta_attrs.pop('index_together', self.index_together)
     93            if it and not isinstance(it[0], (tuple, list)):
     94                it = (it,)
     95            self.index_together = it
     96
    9197            # verbose_name_plural is a special case because it uses a 's'
    9298            # by default.
    9399            if self.verbose_name_plural is None:
    94100                self.verbose_name_plural = string_concat(self.verbose_name, 's')
    95101
    96102            # Any leftover attributes must be invalid.
    97103            if meta_attrs != {}:
    98104                raise TypeError("'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys()))
  • docs/ref/models/options.txt

    diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt
    a b  
    242242    appropriate ``UNIQUE`` statements are included in the ``CREATE TABLE``
    243243    statement).
    244244
    245245    For convenience, unique_together can be a single list when dealing with a single
    246246    set of fields::
    247247
    248248        unique_together = ("driver", "restaurant")
    249249
     250``index_together``
     251-------------------
     252
     253.. versionadded:: 1.4
     254
     255.. attribute:: Options.index_together
     256
     257    Sets of field names that, taken together, will be indexed::
     258
     259        index_together = (("driver", "restaurant"),)
     260
     261    This is a list of lists of fields that will indexed together. (i.e., the
     262    appropriate ``CREATE INDEX`` statements will be created for this table.
     263
     264    For convenience, index_together can be a single list when dealing with a single
     265    set of fields::
     266
     267        index_together = ("driver", "restaurant")
     268
    250269``verbose_name``
    251270----------------
    252271
    253272.. attribute:: Options.verbose_name
    254273
    255274    A human-readable name for the object, singular::
    256275
    257276        verbose_name = "pizza"
  • tests/modeltests/basic/models.py

    diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py
    a b  
    88
    99
    1010class Article(models.Model):
    1111    headline = models.CharField(max_length=100, default='Default headline')
    1212    pub_date = models.DateTimeField()
    1313
    1414    class Meta:
    1515        ordering = ('pub_date','headline')
     16        index_together = ('headline', 'pub_date')
    1617
    1718    def __unicode__(self):
    1819        return self.headline
  • tests/modeltests/get_or_create/models.py

    diff --git a/tests/modeltests/get_or_create/models.py b/tests/modeltests/get_or_create/models.py
    a b  
    99from django.db import models
    1010
    1111
    1212class Person(models.Model):
    1313    first_name = models.CharField(max_length=100)
    1414    last_name = models.CharField(max_length=100)
    1515    birthday = models.DateField()
    1616
     17    class Meta:
     18        index_together = (('first_name', 'last_name'), ('first_name', 'birthday'))
     19
    1720    def __unicode__(self):
    1821        return u'%s %s' % (self.first_name, self.last_name)
    1922
    2023class ManualPrimaryKeyTest(models.Model):
    2124    id = models.IntegerField(primary_key=True)
    2225    data = models.CharField(max_length=100)
Back to Top