def test_automatic_m2m_column_names(self): """ Regression for #12386 - field names on the autogenerated intermediate class that are specified as dotted strings don't retain any path component for the field or column name. """ self.assertEqual( Article.publications.through._meta.fields[1].name, 'article' ) self.assertEqual( Article.publications.through._meta.fields[1].get_attname_column(), ('article_id', 'article_id') ) self.assertEqual( Article.publications.through._meta.fields[2].name, 'publication' ) self.assertEqual( Article.publications.through._meta.fields[2].get_attname_column(), ('publication_id', 'publication_id') ) self.assertEqual( Article._meta.get_field('publications').m2m_db_table(), truncate_name('model_package_article_publications', connection.ops.max_name_length()), ) self.assertEqual( Article._meta.get_field('publications').m2m_column_name(), 'article_id' ) self.assertEqual( Article._meta.get_field('publications').m2m_reverse_name(), 'publication_id' )
def sql_destroy_indexes_for_fields(self, model, fields, style): if len(fields) == 1 and fields[0].db_tablespace: tablespace_sql = self.connection.ops.tablespace_sql( fields[0].db_tablespace) elif model._meta.db_tablespace: tablespace_sql = self.connection.ops.tablespace_sql( model._meta.db_tablespace) else: tablespace_sql = "" if tablespace_sql: tablespace_sql = " " + tablespace_sql field_names = [] qn = self.connection.ops.quote_name for f in fields: field_names.append(style.SQL_FIELD(qn(f.column))) index_name = "{0}_{1}".format(model._meta.db_table, self._digest([f.name for f in fields])) return [ style.SQL_KEYWORD("DROP INDEX") + " " + style.SQL_TABLE(qn(truncate_name(index_name, self.connection.ops.max_name_length()))) + " " + style.SQL_KEYWORD("ON") + " " + style.SQL_TABLE(qn(model._meta.db_table)) + ";", ]
def sql_for_pending_references(self, model, style, pending_references): """ Returns any ALTER TABLE statements to add constraints after the fact. """ opts = model._meta if not opts.managed or opts.swapped: return [] qn = self.connection.ops.quote_name final_output = [] if model in pending_references: for rel_class, f in pending_references[model]: rel_opts = rel_class._meta r_table = rel_opts.db_table r_col = f.column table = opts.db_table col = opts.get_field(f.rel.field_name).column # For MySQL, r_name must be unique in the first 64 characters. # So we are careful with character usage here. r_name = '%s_refs_%s_%s' % ( r_col, col, self._digest(r_table, table)) final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % (qn(r_table), qn(truncate_name( r_name, self.connection.ops.max_name_length())), qn(r_col), qn(table), qn(col), self.connection.ops.deferrable_sql())) del pending_references[model] return final_output
def sql_indexes_for_field(self, model, f, style): """ Return the CREATE INDEX SQL statements for a single model field. """ from django.db.backends.utils import truncate_name if f.db_index and not f.unique: qn = self.connection.ops.quote_name tablespace = f.db_tablespace or model._meta.db_tablespace if tablespace: tablespace_sql = self.connection.ops.tablespace_sql(tablespace) if tablespace_sql: tablespace_sql = " " + tablespace_sql else: tablespace_sql = "" i_name = "%s_%s" % (model._meta.db_table, self._digest(f.column)) # HANA complains with semicolon at the end output = [ style.SQL_KEYWORD("CREATE INDEX") + " " + style.SQL_TABLE(qn(truncate_name(i_name, self.connection.ops.max_name_length()))) + " " + style.SQL_KEYWORD("ON") + " " + style.SQL_TABLE(qn(model._meta.db_table)) + " " + "(%s)" % style.SQL_FIELD(qn(f.column)) + "%s" % tablespace_sql ] else: output = [] return output
def get_index_sql(index_name, opclass=''): return (style.SQL_KEYWORD('CREATE INDEX') + ' ' + style.SQL_TABLE(qn(truncate_name(index_name, self.connection.ops.max_name_length()))) + ' ' + style.SQL_KEYWORD('ON') + ' ' + style.SQL_TABLE(qn(db_table)) + ' ' + "(%s%s)" % (style.SQL_FIELD(qn(f.column)), opclass) + "%s;" % tablespace_sql)
def deferred_class_factory(model, attrs): """ Returns a class object that is a copy of "model" with the specified "attrs" being replaced with DeferredAttribute objects. The "pk_value" ties the deferred attributes to a particular instance of the model. """ if not attrs: return model # Never create deferred models based on deferred model if model._deferred: # Deferred models are proxies for the non-deferred model. We never # create chains of defers => proxy_for_model is the non-deferred # model. model = model._meta.proxy_for_model # The app registry wants a unique name for each model, otherwise the new # class won't be created (we get an exception). Therefore, we generate # the name using the passed in attrs. It's OK to reuse an existing class # object if the attrs are identical. name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(attrs))) name = utils.truncate_name(name, 80, 32) try: return apps.get_model(model._meta.app_label, name) except LookupError: class Meta: proxy = True app_label = model._meta.app_label overrides = {attr: DeferredAttribute(attr, model) for attr in attrs} overrides["Meta"] = Meta overrides["__module__"] = model.__module__ overrides["_deferred"] = True return type(str(name), (model,), overrides)
def _get_no_autofield_sequence_name(self, table): """ Manually created sequence name to keep backward compatibility for AutoFields that aren't Oracle identity columns. """ name_length = self.max_name_length() - 3 return '%s_SQ' % truncate_name(strip_quotes(table), name_length).upper()
def __new__(mcs, name, bases, attrs): if django.VERSION < (2, 0): # Silence polymorphic messages for wrong manager inheritance, # let everything work just like before. try: attrs['Meta'].manager_inheritance_from_future = True except KeyError: pass new_class = super(ContentItemMetaClass, mcs).__new__(mcs, name, bases, attrs) db_table = new_class._meta.db_table app_label = new_class._meta.app_label if name != 'ContentItem': if db_table.startswith(app_label + '_'): model_name = db_table[len(app_label) + 1:] new_class._meta.db_table = truncate_name("contentitem_%s_%s" % (app_label, model_name), connection.ops.max_name_length()) if hasattr(new_class._meta, 'original_attrs'): # Make sure that the Django migrations also pick up this change! # Changing the db_table beforehand might be cleaner, # but also requires duplicating the whole algorithm that Django uses. new_class._meta.original_attrs['db_table'] = new_class._meta.db_table # Enforce good manners. The name is often not visible, except for the delete page. if not new_class._meta.abstract: if not hasattr(new_class, '__str__') or new_class.__str__ == ContentItem.__str__: if PY3: raise TypeError("The {0} class should implement a __str__() function.".format(name)) else: # The first check is for python_2_unicode_compatible tricks, also check for __unicode__ only. if not hasattr(new_class, '__unicode__') or new_class.__unicode__ == ContentItem.__unicode__: raise TypeError("The {0} class should implement a __unicode__() or __str__() function.".format(name)) return new_class
def sql_indexes_for_fields(self, model, fields, style): if len(fields) == 1 and fields[0].db_tablespace: tablespace_sql = self.connection.ops.tablespace_sql(fields[0].db_tablespace) elif model._meta.db_tablespace: tablespace_sql = self.connection.ops.tablespace_sql(model._meta.db_tablespace) else: tablespace_sql = "" if tablespace_sql: tablespace_sql = " " + tablespace_sql field_names = [] qn = self.connection.ops.quote_name for f in fields: field_names.append(style.SQL_FIELD(qn(f.column))) index_name = "%s_%s" % (model._meta.db_table, self._digest([f.name for f in fields])) return [ style.SQL_KEYWORD("CREATE INDEX") + " " + style.SQL_TABLE(qn(truncate_name(index_name, self.connection.ops.max_name_length()))) + " " + style.SQL_KEYWORD("ON") + " " + style.SQL_TABLE(qn(model._meta.db_table)) + " " + "(%s)" % style.SQL_FIELD(", ".join(field_names)) + "%s;" % tablespace_sql, ]
def _create_index_name(self, model, column_names, suffix=""): """ Generates a unique name for an index/unique constraint. """ # If there is just one column in the index, use a default algorithm from Django if len(column_names) == 1 and not suffix: return truncate_name( '%s_%s' % (model._meta.db_table, BaseDatabaseCreation._digest(column_names[0])), self.connection.ops.max_name_length() ) # Else generate the name for the index using a different algorithm table_name = model._meta.db_table.replace('"', '').replace('.', '_') index_unique_name = '_%x' % abs(hash((table_name, ','.join(column_names)))) max_length = self.connection.ops.max_name_length() or 200 # If the index name is too long, truncate it index_name = ('%s_%s%s%s' % ( table_name, column_names[0], index_unique_name, suffix, )).replace('"', '').replace('.', '_') if len(index_name) > max_length: part = ('_%s%s%s' % (column_names[0], index_unique_name, suffix)) index_name = '%s%s' % (table_name[:(max_length - len(part))], part) # It shouldn't start with an underscore (Oracle hates this) if index_name[0] == "_": index_name = index_name[1:] # If it's STILL too long, just hash it down if len(index_name) > max_length: index_name = hashlib.md5(force_bytes(index_name)).hexdigest()[:max_length] # It can't start with a number on Oracle, so prepend D if we need to if index_name[0].isdigit(): index_name = "D%s" % index_name[:-1] return index_name
def contribute_to_class(self, cls, name): from django.db import connection from django.db.backends.utils import truncate_name cls._meta = self self.model = cls # First, construct the default values for these options. self.object_name = cls.__name__ self.model_name = self.object_name.lower() self.verbose_name = camel_case_to_spaces(self.object_name) # Store the original user-defined values for each option, # for use when serializing the model definition self.original_attrs = {} # Next, apply any overridden values from 'class Meta'. if self.meta: meta_attrs = self.meta.__dict__.copy() for name in self.meta.__dict__: # Ignore any private attributes that Django doesn't care about. # NOTE: We can't modify a dictionary's contents while looping # over it, so we loop over the *original* dictionary instead. if name.startswith('_'): del meta_attrs[name] for attr_name in DEFAULT_NAMES: if attr_name in meta_attrs: setattr(self, attr_name, meta_attrs.pop(attr_name)) self.original_attrs[attr_name] = getattr(self, attr_name) elif hasattr(self.meta, attr_name): setattr(self, attr_name, getattr(self.meta, attr_name)) self.original_attrs[attr_name] = getattr(self, attr_name) self.unique_together = normalize_together(self.unique_together) self.index_together = normalize_together(self.index_together) # verbose_name_plural is a special case because it uses a 's' # by default. if self.verbose_name_plural is None: self.verbose_name_plural = format_lazy('{}s', self.verbose_name) # order_with_respect_and ordering are mutually exclusive. self._ordering_clash = bool(self.ordering and self.order_with_respect_to) # Any leftover attributes must be invalid. if meta_attrs != {}: raise TypeError("'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs)) else: self.verbose_name_plural = format_lazy('{}s', self.verbose_name) del self.meta # If the db_table wasn't provided, use the app_label + model_name. if not self.db_table: self.db_table = "%s_%s" % (self.app_label, self.model_name) self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
def create_gm2m_intermediary_model(field, klass): """ Creates a generic M2M model for the GM2M field 'field' on model 'klass' """ from django.db import models managed = klass._meta.managed name = "%s_%s" % (klass._meta.object_name, field.name) model_name = klass._meta.model_name db_table = db_backends_utils.truncate_name( "%s_%s" % (klass._meta.db_table, field.name), connection.ops.max_name_length() ) meta_kwargs = { "db_table": db_table, "managed": managed, "auto_created": klass, "app_label": klass._meta.app_label, "db_tablespace": klass._meta.db_tablespace, "unique_together": (SRC_ATTNAME, CT_ATTNAME, FK_ATTNAME), "verbose_name": "%s-generic relationship" % model_name, "verbose_name_plural": "%s-generic relationships" % model_name, "apps": field.model._meta.apps, } meta = type("Meta", (object,), meta_kwargs) fk_maxlength = 16 # default value if field.pk_maxlength is not False: fk_maxlength = field.pk_maxlength model = type( str(name), (models.Model,), { "Meta": meta, "__module__": klass.__module__, SRC_ATTNAME: models.ForeignKey( klass, on_delete=field.rel.on_delete_src, db_constraint=field.rel.db_constraint ), CT_ATTNAME: models.ForeignKey(ct.ContentType, db_constraint=field.rel.db_constraint), FK_ATTNAME: models.CharField(max_length=fk_maxlength), TGT_ATTNAME: ct.GenericForeignKey( ct_field=CT_ATTNAME, fk_field=FK_ATTNAME, for_concrete_model=field.rel.for_concrete_model ), }, ) if is_fake_model(klass): # if we are building a fake model for migrations purposes, create a # ModelState from the model and render it (see issues #3 and #5) return ModelState.from_model(model).render(klass._meta.apps) return model
def database_name(self): """ The database name used for external databases. Escape all non-ascii characters and truncate to 64 chars, the maximum for mysql """ name = self.domain.replace('.', '_') allowed = string.ascii_letters + string.digits + '_' escaped = ''.join(char for char in name if char in allowed) return truncate_name(escaped, length=64)
def _get_operation_name(self, prefix, table): """ Get the operation (trigger, sequence, etc) name by replacing the table name prefix to a configured prefix """ name_length = self.max_name_length() - 3 return string.replace_prefix( truncate_name(table, name_length).upper(), settings.PREFIX.get('TABLE'), prefix.upper())
def create_gm2m_intermediary_model(field, klass): """ Creates a generic M2M model for the GM2M field 'field' on model 'klass' """ from django.db import models managed = klass._meta.managed name = '%s_%s' % (klass._meta.object_name, field.name) model_name = klass._meta.model_name db_table = db_backends_utils.truncate_name( '%s_%s' % (klass._meta.db_table, field.name), connection.ops.max_name_length()) meta_kwargs = { 'db_table': db_table, 'managed': managed, 'auto_created': klass, 'app_label': klass._meta.app_label, 'db_tablespace': klass._meta.db_tablespace, 'unique_together': (SRC_ATTNAME, CT_ATTNAME, FK_ATTNAME), 'verbose_name': '%s-generic relationship' % model_name, 'verbose_name_plural': '%s-generic relationships' % model_name, 'apps': field.model._meta.apps, } meta = type('Meta', (object,), meta_kwargs) fk_maxlength = 16 # default value if field.pk_maxlength is not False: fk_maxlength = field.pk_maxlength model = type(str(name), (models.Model,), { 'Meta': meta, '__module__': klass.__module__, SRC_ATTNAME: models.ForeignKey(klass, auto_created=True, on_delete=field.remote_field.on_delete_src, db_constraint=field.remote_field.db_constraint), CT_ATTNAME: models.ForeignKey(ct.ContentType, db_constraint=field.remote_field.db_constraint), FK_ATTNAME: models.CharField(max_length=fk_maxlength), TGT_ATTNAME: ct.GenericForeignKey( ct_field=CT_ATTNAME, fk_field=FK_ATTNAME, for_concrete_model=field.remote_field.for_concrete_model, ), }) if is_fake_model(klass): # if we are building a fake model for migrations purposes, create a # ModelState from the model and render it (see issues #3 and #5) return ModelState.from_model(model).render(klass._meta.apps) return model
def test_shallow_factory(): shallow_class = shallow_class_factory(SimpleObject) assert issubclass(shallow_class, SimpleObject) name = "{}_ElasticSearchResult".format(SimpleObject.__name__) name = utils.truncate_name(name, 80, 32) # try to get the model from the django registry cached_shallow_class = apps.get_model(SimpleObject._meta.app_label, name) assert cached_shallow_class == shallow_class
def m2m_db_table(self): # self.db_table will be None if if self.rel.through is not None: return self.rel.through._meta.db_table elif self.db_table: return self.db_table else: return db_backends_utils.truncate_name( '%s_%s' % (self.model._meta.db_table, self.name), connection.ops.max_name_length())
def m2m_db_table(self): # self.db_table will be None if if self.remote_field.through is not None: return self.remote_field.through._meta.db_table elif self.db_table: return self.db_table else: return db_backends_utils.truncate_name( '%s_%s' % (self.model._meta.db_table, self.name), connection.ops.max_name_length())
def test_truncate_name(self): self.assertEqual(truncate_name('some_table', 10), 'some_table') self.assertEqual(truncate_name('some_long_table', 10), 'some_la38a') self.assertEqual(truncate_name('some_long_table', 10, 3), 'some_loa38') self.assertEqual(truncate_name('some_long_table'), 'some_long_table') # "user"."table" syntax self.assertEqual(truncate_name('username"."some_table', 10), 'username"."some_table') self.assertEqual(truncate_name('username"."some_long_table', 10), 'username"."some_la38a') self.assertEqual(truncate_name('username"."some_long_table', 10, 3), 'username"."some_loa38')
def get_index_sql(index_name, opclass=''): return (style.SQL_KEYWORD('CREATE INDEX') + ' ' + style.SQL_TABLE( qn( truncate_name( index_name, self.connection.ops.max_name_length()))) + ' ' + style.SQL_KEYWORD('ON') + ' ' + style.SQL_TABLE(qn(db_table)) + ' ' + "(%s%s)" % (style.SQL_FIELD(qn(f.column)), opclass) + "%s;" % tablespace_sql)
def _create_constraint_name(self, model, column_names, constraint_type='', suffix=""): """ Generates a unique name for a constraint. """ column = '_'.join(column_names) if isinstance(column_names, (list, tuple)) else column_names name = '%s%s_%s%s' % ( self.constraint_type_prefix_map.get(constraint_type.upper(), ''), model._meta.db_table, column, suffix, ) return truncate_name(name, length=self.connection.ops.max_name_length(), hash_len=8)
def quote_name(self, name): # SQL92 requires delimited (quoted) names to be case-sensitive. When # not quoted, Oracle has case-insensitive behavior for identifiers, but # always defaults to uppercase. # We simplify things by making Oracle identifiers always uppercase. if not name.startswith('"') and not name.endswith('"'): name = '"%s"' % truncate_name(name.upper(), self.max_name_length()) # Oracle puts the query text into a (query % args) construct, so % signs # in names need to be escaped. The '%%' will be collapsed back to '%' at # that stage so we aren't really making the name longer here. name = name.replace("%", "%%") return name.upper()
def database_name(self): """ The database name used for external databases/storages, if any. """ name = self.internal_lms_domain.replace('.', '_') # Escape all non-ascii characters and truncate to 50 chars. # The maximum length for the name of a MySQL database is 64 characters. # But since we add suffixes to database_name to generate unique database names # for different services (e.g. xqueue) we don't want to use the maximum length here. allowed = string.ascii_letters + string.digits + '_' escaped = ''.join(char for char in name if char in allowed) return truncate_name(escaped, length=50)
def quote_name(self, name): # SQL92 requires delimited (quoted) names to be case-sensitive. When # not quoted, Oracle has case-insensitive behavior for identifiers, but # always defaults to uppercase. # We simplify things by making Oracle identifiers always uppercase. if not name.startswith('"') and not name.endswith('"'): name = '"%s"' % truncate_name(name.upper(), self.max_name_length()) # Oracle puts the query text into a (query % args) construct, so % signs # in names need to be escaped. The '%%' will be collapsed back to '%' at # that stage so we aren't really making the name longer here. name = name.replace('%', '%%') return name.upper()
def contribute_to_class(self, cls, name): from django.db import connection from django.db.backends.utils import truncate_name cls._meta = self self.model = cls self.installed = re.sub("\.models$", "", cls.__module__) in settings.INSTALLED_APPS # First, construct the default values for these options. self.object_name = cls.__name__ self.model_name = self.object_name.lower() self.verbose_name = get_verbose_name(self.object_name) # Store the original user-defined values for each option, # for use when serializing the model definition self.original_attrs = {} # Next, apply any overridden values from 'class Meta'. if self.meta: meta_attrs = self.meta.__dict__.copy() for name in self.meta.__dict__: # Ignore any private attributes that Django doesn't care about. # NOTE: We can't modify a dictionary's contents while looping # over it, so we loop over the *original* dictionary instead. if name.startswith("_"): del meta_attrs[name] for attr_name in DEFAULT_NAMES: if attr_name in meta_attrs: setattr(self, attr_name, meta_attrs.pop(attr_name)) self.original_attrs[attr_name] = getattr(self, attr_name) elif hasattr(self.meta, attr_name): setattr(self, attr_name, getattr(self.meta, attr_name)) self.original_attrs[attr_name] = getattr(self, attr_name) ut = meta_attrs.pop("unique_together", self.unique_together) self.unique_together = normalize_unique_together(ut) # verbose_name_plural is a special case because it uses a 's' # by default. if self.verbose_name_plural is None: self.verbose_name_plural = string_concat(self.verbose_name, "s") # Any leftover attributes must be invalid. if meta_attrs != {}: raise TypeError("'class Meta' got invalid attribute(s): %s" % ",".join(meta_attrs.keys())) else: self.verbose_name_plural = string_concat(self.verbose_name, "s") del self.meta # If the db_table wasn't provided, use the app_label + model_name. if not self.db_table: self.db_table = "%s_%s" % (self.app_label, self.model_name) self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
def shallow_class_factory(model): if model._deferred: model = model._meta.proxy_for_model name = "{}_ElasticSearchResult".format(model.__name__) name = utils.truncate_name(name, 80, 32) # try to get the model from the django registry try: return apps.get_model(model._meta.app_label, name) # get the object's type - hopefully a django model except LookupError: class Meta(object): proxy = True app_label = model._meta.app_label class ElasticMapping: class Meta: elastic_abstract = True doc_type = model.search_objects.mapping.doc_type overrides = { "save": None, "Meta": Meta, "Mapping": ElasticMapping, "search_objects": model.search_objects.__class__(), "__module__": model.__module__, "_deferred": True, } for attname, es_field in iteritems( model.search_objects.mapping.properties._params["properties"]): if type(es_field) == field.Nested: # This is a nested object! dj_field = model._meta.get_field(attname) if isinstance(dj_field, ManyToOneRel): overrides[attname] = ElasticSearchManyField( attname, dj_field.related_model) elif isinstance(dj_field, ForeignObjectRel): overrides[attname] = ElasticSearchForeignKey( attname, dj_field.related_model) if isinstance(dj_field, models.ManyToManyField): overrides[attname] = ElasticSearchManyField( attname, dj_field.rel.to) if isinstance(dj_field, models.ForeignKey): # Let's add a fake foreignkey attribute overrides[attname] = ElasticSearchForeignKey( attname, dj_field.rel.to) return type(str(name), (model, ), overrides)
def sql_indexes_for_field( self, model, f, style ): """Return the CREATE INDEX SQL statements for a single model field""" output = [] qn = self.connection.ops.quote_name max_name_length = self.connection.ops.max_name_length() # ignore tablespace information tablespace_sql = '' i = 0 if( djangoVersion[0:2] >= ( 1, 8 ) and 'DB2' not in getattr(self.connection.connection, dbms_name)) or getattr(self.connection.connection, dbms_name) != 'DB2': if len( model._meta.unique_together_index ) != 0: for unique_together_index in model._meta.unique_together_index: i = i + 1 column_list = [] for column in unique_together_index: for local_field in model._meta.local_fields: if column == local_field.name: column_list.extend( [ local_field.column ] ) self.__add_psudokey_column( style, self.connection.cursor(), model._meta.db_table, model._meta.pk.attname, column_list ) column_list.extend( [ truncate_name( "%s%s" % ( self.psudo_column_prefix, "_".join( column_list ) ), max_name_length ) ] ) output.extend( [style.SQL_KEYWORD( 'CREATE UNIQUE INDEX' ) + ' ' + \ style.SQL_TABLE( qn( 'db2_%s_%s' % ( model._meta.db_table, i ) ) ) + ' ' + \ style.SQL_KEYWORD( 'ON' ) + ' ' + \ style.SQL_TABLE( qn( model._meta.db_table ) ) + ' ' + \ '( %s )' % ", ".join( column_list ) + ' ' + \ '%s;' % tablespace_sql] ) model._meta.unique_together_index = [] if f.unique_index: column_list = [] column_list.extend( [f.column] ) self.__add_psudokey_column( style, self.connection.cursor(), model._meta.db_table, model._meta.pk.attname, column_list ) cisql = 'CREATE UNIQUE INDEX' output.extend( [style.SQL_KEYWORD( cisql ) + ' ' + style.SQL_TABLE( qn( '%s_%s' % ( model._meta.db_table, f.column ) ) ) + ' ' + style.SQL_KEYWORD( 'ON' ) + ' ' + style.SQL_TABLE( qn( model._meta.db_table ) ) + ' ' + "(%s, %s )" % ( style.SQL_FIELD( qn( f.column ) ), style.SQL_FIELD( qn( truncate_name( ( self.psudo_column_prefix + f.column ), max_name_length ) ) ) ) + "%s;" % tablespace_sql] ) return output if f.db_index and not f.unique: cisql = 'CREATE INDEX' output.extend( [style.SQL_KEYWORD( cisql ) + ' ' + style.SQL_TABLE( qn( '%s_%s' % ( model._meta.db_table, f.column ) ) ) + ' ' + style.SQL_KEYWORD( 'ON' ) + ' ' + style.SQL_TABLE( qn( model._meta.db_table ) ) + ' ' + "(%s)" % style.SQL_FIELD( qn( f.column ) ) + "%s;" % tablespace_sql] ) return output
def alterFieldDataTypeByRemaking(self, model, old_field, new_field, strict): tmp_new_field = copy.deepcopy(new_field) tmp_new_field.column = truncate_name( "%s%s" % ( self.psudo_column_prefix, tmp_new_field.column ), self.connection.ops.max_name_length() ) self.add_field(model, tmp_new_field) #Transfer data from old field to new tmp field self.execute("UPDATE %s set %s=%s" % ( self.quote_name(model._meta.db_table), self.quote_name(tmp_new_field.column), self.quote_name(old_field.column) ) ) self.remove_field(model, old_field) return tmp_new_field, new_field
def alterFieldDataTypeByRemaking(self, model, old_field, new_field, strict): tmp_new_field = copy.deepcopy(new_field) tmp_new_field.column = truncate_name( "%s%s" % (self.psudo_column_prefix, tmp_new_field.column), self.connection.ops.max_name_length()) self.add_field(model, tmp_new_field) #Transfer data from old field to new tmp field self.execute("UPDATE %s set %s=%s" % (self.quote_name( model._meta.db_table), self.quote_name( tmp_new_field.column), self.quote_name(old_field.column))) self.remove_field(model, old_field) return tmp_new_field, new_field
def handle(self, old_app_name, new_app_name, *args, **options): with connection.cursor() as cursor: old_app_name = old_app_name[0] new_app_name = new_app_name[0] cursor.execute("SELECT * FROM django_content_type " f"where app_label='{new_app_name}'") has_already_been_ran = cursor.fetchone() if has_already_been_ran: logger.info('Rename has already been done, exiting without ' 'making any changes') return None cursor.execute( f"UPDATE django_content_type SET app_label='{new_app_name}' " f"WHERE app_label='{old_app_name}'") cursor.execute( f"UPDATE django_migrations SET app='{new_app_name}' " f"WHERE app='{old_app_name}'") models = apps.all_models[new_app_name] models.update(apps.all_models[old_app_name]) for model_name in models: old_table_name = truncate_name( f"{old_app_name}_{model_name}", connection.ops.max_name_length()) new_table_name = truncate_name( f"{new_app_name}_{model_name}", connection.ops.max_name_length()) query = (f"ALTER TABLE {old_table_name} " f"RENAME TO {new_table_name}") try: cursor.execute(query) except ProgrammingError: logger.warning('Rename query failed: "%s"', query, exc_info=True)
def __add_psudokey_column( self, style, cursor, table_name, pk_name, column_list ): qn = self.connection.ops.quote_name max_name_length = self.connection.ops.max_name_length() sql = style.SQL_KEYWORD( 'ALTER TABLE ' ) + \ style.SQL_TABLE( qn( table_name ) ) + \ style.SQL_KEYWORD( ' ADD COLUMN ' ) + \ style.SQL_FIELD( qn( truncate_name( "%s%s" % ( self.psudo_column_prefix, "_".join( column_list ) ), max_name_length ) ) ) + \ style.SQL_KEYWORD( ' GENERATED ALWAYS AS( CASE WHEN ' ) + \ style.SQL_FIELD( "%s %s" % ( " IS NULL OR ".join( column_list ), 'IS NULL THEN ' ) ) + \ style.SQL_FIELD( qn( pk_name ) ) + \ style.SQL_KEYWORD( ' END ) ;' ) cursor.execute( 'SET INTEGRITY FOR ' + style.SQL_TABLE( qn( table_name ) ) + ' OFF CASCADE DEFERRED;' ) cursor.execute( sql ) cursor.execute( 'SET INTEGRITY FOR ' + style.SQL_TABLE( table_name ) + ' IMMEDIATE CHECKED;' ) cursor.close()
def __new__(mcs, name, bases, attrs): if django.VERSION < (2, 0): # Silence polymorphic messages for wrong manager inheritance, # let everything work just like before. try: attrs["Meta"].manager_inheritance_from_future = True except KeyError: pass new_class = super(ContentItemMetaClass, mcs).__new__(mcs, name, bases, attrs) db_table = new_class._meta.db_table app_label = new_class._meta.app_label if name != "ContentItem": if db_table.startswith(app_label + "_"): model_name = db_table[len(app_label) + 1:] new_class._meta.db_table = truncate_name( "contentitem_%s_%s" % (app_label, model_name), connection.ops.max_name_length(), ) if hasattr(new_class._meta, "original_attrs"): # Make sure that the Django migrations also pick up this change! # Changing the db_table beforehand might be cleaner, # but also requires duplicating the whole algorithm that Django uses. new_class._meta.original_attrs[ "db_table"] = new_class._meta.db_table # Enforce good manners. The name is often not visible, except for the delete page. if not new_class._meta.abstract: if (not hasattr(new_class, "__str__") or new_class.__str__ == ContentItem.__str__): if PY3: raise TypeError( "The {0} class should implement a __str__() function." .format(name)) else: # The first check is for python_2_unicode_compatible tricks, also check for __unicode__ only. if (not hasattr(new_class, "__unicode__") or new_class.__unicode__ == ContentItem.__unicode__): raise TypeError( "The {0} class should implement a __unicode__() or __str__() function." .format(name)) return new_class
def _create_managed_state(self, tenant_model, connection, state): managed = Managed("%s.%s" % (tenant_model._meta.app_label, tenant_model._meta.object_name)) state.__dict__.pop('apps', None) project_state = state.clone() managed_models = [] for model_key, model_state in iteritems(project_state.models): options = model_state.options if options.get('managed') == managed: db_table = options.get('db_table') if not db_table: db_table = truncate_name("%s_%s" % model_key, connection.ops.max_name_length()) options.update( managed=True, db_table=db_table, ) managed_models.append(model_key) return project_state, managed_models
def _create_constraint_name(self, model, column_names, constraint_type='', suffix=""): """ Generates a unique name for a constraint. """ column = '_'.join(column_names) if isinstance( column_names, (list, tuple)) else column_names name = '%s%s_%s%s' % ( self.constraint_type_prefix_map.get(constraint_type.upper(), ''), model._meta.db_table, column, suffix, ) return truncate_name(name, length=self.connection.ops.max_name_length(), hash_len=8)
def sql_indexes_for_field(self, model, f, style): "Return any spatial index creation SQL for the field." from django.contrib.gis.db.models.fields import GeometryField output = super(OracleCreation, self).sql_indexes_for_field(model, f, style) if isinstance(f, GeometryField): gqn = self.connection.ops.geo_quote_name qn = self.connection.ops.quote_name db_table = model._meta.db_table output.append( style.SQL_KEYWORD('INSERT INTO ') + style.SQL_TABLE('USER_SDO_GEOM_METADATA') + ' (%s, %s, %s, %s)\n ' % tuple(map(qn, ['TABLE_NAME', 'COLUMN_NAME', 'DIMINFO', 'SRID']) ) + style.SQL_KEYWORD(' VALUES ') + '(\n ' + style.SQL_TABLE(gqn(db_table)) + ',\n ' + style.SQL_FIELD(gqn(f.column)) + ',\n ' + style.SQL_KEYWORD("MDSYS.SDO_DIM_ARRAY") + '(\n ' + style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") + ("('LONG', %s, %s, %s),\n " % (f._extent[0], f._extent[2], f._tolerance)) + style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") + ("('LAT', %s, %s, %s)\n ),\n" % (f._extent[1], f._extent[3], f._tolerance)) + ' %s\n );' % f.srid) if f.spatial_index: # Getting the index name, Oracle doesn't allow object # names > 30 characters. idx_name = truncate_name('%s_%s_id' % (db_table, f.column), 30) output.append( style.SQL_KEYWORD('CREATE INDEX ') + style.SQL_TABLE(qn(idx_name)) + style.SQL_KEYWORD(' ON ') + style.SQL_TABLE(qn(db_table)) + '(' + style.SQL_FIELD(qn(f.column)) + ') ' + style.SQL_KEYWORD('INDEXTYPE IS ') + style.SQL_TABLE('MDSYS.SPATIAL_INDEX') + ';') return output
def __new__(mcs, name, bases, attrs): new_class = super(URLNodeMetaClass, mcs).__new__(mcs, name, bases, attrs) # Update the table name. # Inspired by from Django-CMS, (c) , BSD licensed. if name not in ['UrlNode', 'Page', 'HtmlPage']: meta = new_class._meta # Make sure only values are updated if there is no manual edit, or a proxy model for UrlNode (e.g. HtmlPage) if meta.db_table.startswith(meta.app_label + '_') and meta.db_table != 'fluent_pages_urlnode': model_name = meta.db_table[len(meta.app_label) + 1:] meta.db_table = truncate_name("pagetype_{0}_{1}".format(meta.app_label, model_name), connection.ops.max_name_length()) if hasattr(meta, 'original_attrs'): # Make sure that the Django 1.7 migrations also pick up this change! # Changing the db_table beforehand might be cleaner, # but also requires duplicating the whole algorithm that Django uses. meta.original_attrs['db_table'] = meta.db_table return new_class
def _create_managed_state(self, tenant_model, connection, state): managed = Managed( "%s.%s" % (tenant_model._meta.app_label, tenant_model._meta.object_name)) state.__dict__.pop('apps', None) project_state = state.clone() managed_models = [] for model_key, model_state in iteritems(project_state.models): options = model_state.options if options.get('managed') == managed: db_table = options.get('db_table') if not db_table: db_table = truncate_name("%s_%s" % model_key, connection.ops.max_name_length()) options.update( managed=True, db_table=db_table, ) managed_models.append(model_key) return project_state, managed_models
def _foreign_key_sql(self, from_table_name, from_column_name, to_table_name, to_column_name): """ Generates a full SQL statement to add a foreign key constraint """ constraint_name = "%s_refs_%s_%x" % ( from_column_name, to_column_name, abs(hash((from_table_name, to_table_name))), ) return "ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;" % ( self.quote_name(from_table_name), self.quote_name( truncate_name(constraint_name, connection.ops.max_name_length())), self.quote_name(from_column_name), self.quote_name(to_table_name), self.quote_name(to_column_name), connection.ops.deferrable_sql(), # Django knows this )
def create_constraint_name(connection, r_col, col, r_table, table): """Return the name of a constraint. This provides compatibility with all supported versions of Django. Args: connection (object): The database connection. r_col (str): The column name for the source of the relation. col (str): The column name for the "to" end of the relation. r_table (str): The table name for the source of the relation. table (str): The table name for the "to" end of the relation. Returns: str: The generated constraint name for this version of Django. """ if BaseDatabaseSchemaEditor: suffix = '_fk_%(to_table)s_%(to_column)s' % { 'to_table': table, 'to_column': col, } # No need to truncate here, since create_index_name() will do it for # us. return create_index_name(connection, r_table, col_names=[r_col], suffix=suffix) else: return truncate_name( '%s_refs_%s_%s' % (r_col, col, digest(connection, r_table, table)), connection.ops.max_name_length())
def sql_remove_table_constraints(self, model, references_to_delete, style): if not model._meta.managed or model._meta.proxy or model._meta.swapped: return [] output = [] qn = self.connection.ops.quote_name for rel_class, f in references_to_delete[model]: table = rel_class._meta.db_table col = f.column r_table = model._meta.db_table r_col = model._meta.get_field(f.rel.field_name).column r_name = '%s_refs_%s_%s' % ( col, r_col, self._digest(table, r_table)) output.append('%s %s %s %s;' % ( style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(table)), style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()), style.SQL_FIELD(qn(truncate_name( r_name, self.connection.ops.max_name_length()))) )) del references_to_delete[model] return output
def create_index_together_name(connection, table_name, field_names): """Return the name of an index for an index_together. This provides compatibility with all supported versions of Django >= 1.5. Prior versions don't support index_together. Args: connection (object): The database connection. table_name (str): The name of the table. field_names (list of str): The list of field names indexed together. Returns: str: The generated index name for this version of Django. """ if BaseDatabaseSchemaEditor: # Django >= 1.7 # # Starting in 1.7, the index_together indexes were given a "_idx" # suffix. return create_index_name(connection, table_name, field_names, field_names, suffix='_idx') else: # Django < 1.7 # # index_together was introduced in Django 1.5, and prior to 1.7, the # format was identical to that of normal indexes. assert django.VERSION[:2] >= (1, 5) index_name = '%s_%s' % (table_name, digest(connection, field_names)) return truncate_name(index_name, connection.ops.max_name_length())
def sql_indexes_for_field(self, model, f, style): kwargs = VERSION[:2] >= (1, 3) and {'connection': self.connection} or {} if f.db_type(**kwargs) == 'hstore': if not f.db_index: return [] qn = self.connection.ops.quote_name tablespace = f.db_tablespace or model._meta.db_tablespace tablespace_sql = '' if tablespace: sql = self.connection.ops.tablespace_sql(tablespace) if sql: tablespace_sql = ' ' + sql index_name = '%s_%s_gist' % (model._meta.db_table, f.column) clauses = [style.SQL_KEYWORD('CREATE INDEX'), style.SQL_TABLE(qn(truncate_name(index_name, self.connection.ops.max_name_length()))), style.SQL_KEYWORD('ON'), style.SQL_TABLE(qn(model._meta.db_table)), style.SQL_KEYWORD('USING GIST'), '(%s)' % style.SQL_FIELD(qn(f.column))] return ['%s%s;' % (' '.join(clauses), tablespace_sql)] else: return super(DatabaseCreation, self).sql_indexes_for_field(model, f, style)
def sql_destroy_indexes_for_fields(self, model, fields, style): if len(fields) == 1 and fields[0].db_tablespace: tablespace_sql = self.connection.ops.tablespace_sql(fields[0].db_tablespace) elif model._meta.db_tablespace: tablespace_sql = self.connection.ops.tablespace_sql(model._meta.db_tablespace) else: tablespace_sql = "" if tablespace_sql: tablespace_sql = " " + tablespace_sql field_names = [] qn = self.connection.ops.quote_name for f in fields: field_names.append(style.SQL_FIELD(qn(f.column))) index_name = "%s_%s" % (model._meta.db_table, self._digest([f.name for f in fields])) return [ style.SQL_KEYWORD("DROP INDEX") + " " + style.SQL_TABLE(qn(truncate_name(index_name, self.connection.ops.max_name_length()))) + " " + ";", ]
def shallow_class_factory(model): if model._deferred: model = model._meta.proxy_for_model name = "{}_ElasticSearchResult".format(model.__name__) name = utils.truncate_name(name, 80, 32) # try to get the model from the django registry try: return apps.get_model(model._meta.app_label, name) # get the object's type - hopefully a django model except LookupError: class Meta(object): proxy = True app_label = model._meta.app_label overrides = { "save": None, "Meta": Meta, "__module__": model.__module__, "_deferred": True, } for attname, es_field in iteritems(model.search_objects.mapping.properties._params["properties"]): if type(es_field) == field.Nested: # This is a nested object! dj_field = model._meta.get_field(attname) if isinstance(dj_field, ManyToOneRel): overrides[attname] = ElasticSearchManyField(attname, dj_field.related_model) elif isinstance(dj_field, ForeignObjectRel): overrides[attname] = ElasticSearchForeignKey(attname, dj_field.related_model) if isinstance(dj_field, models.ManyToManyField): overrides[attname] = ElasticSearchManyField(attname, dj_field.rel.to) if isinstance(dj_field, models.ForeignKey): # Let's add a fake foreignkey attribute overrides[attname] = ElasticSearchForeignKey(attname, dj_field.rel.to) return type(str(name), (model,), overrides)
def deferred_class_factory(model, attrs): """ Returns a class object that is a copy of "model" with the specified "attrs" being replaced with DeferredAttribute objects. The "pk_value" ties the deferred attributes to a particular instance of the model. """ class Meta: proxy = True app_label = model._meta.app_label # The app_cache wants a unique name for each model, otherwise the new class # won't be created (we get an old one back). Therefore, we generate the # name using the passed in attrs. It's OK to reuse an existing class # object if the attrs are identical. name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs)))) name = utils.truncate_name(name, 80, 32) overrides = dict((attr, DeferredAttribute(attr, model)) for attr in attrs) overrides["Meta"] = Meta overrides["__module__"] = model.__module__ overrides["_deferred"] = True return type(str(name), (model,), overrides)
def deferred_class_factory(model, attrs): """ Returns a class object that is a copy of "model" with the specified "attrs" being replaced with DeferredAttribute objects. The "pk_value" ties the deferred attributes to a particular instance of the model. """ class Meta: proxy = True app_label = model._meta.app_label # The app_cache wants a unique name for each model, otherwise the new class # won't be created (we get an old one back). Therefore, we generate the # name using the passed in attrs. It's OK to reuse an existing class # object if the attrs are identical. name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs)))) name = utils.truncate_name(name, 80, 32) overrides = dict((attr, DeferredAttribute(attr, model)) for attr in attrs) overrides["Meta"] = Meta overrides["__module__"] = model.__module__ overrides["_deferred"] = True return type(str(name), (model, ), overrides)
def deferred_class_factory(model, attrs): """ Returns a class object that is a copy of "model" with the specified "attrs" being replaced with DeferredAttribute objects. The "pk_value" ties the deferred attributes to a particular instance of the model. """ if not attrs: return model opts = model._meta # Never create deferred models based on deferred model if model._deferred: # Deferred models are proxies for the non-deferred model. We never # create chains of defers => proxy_for_model is the non-deferred # model. model = opts.proxy_for_model # The app registry wants a unique name for each model, otherwise the new # class won't be created (we get an exception). Therefore, we generate # the name using the passed in attrs. It's OK to reuse an existing class # object if the attrs are identical. name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(attrs))) name = utils.truncate_name(name, 80, 32) try: return opts.apps.get_model(model._meta.app_label, name) except LookupError: class Meta: proxy = True apps = opts.apps app_label = opts.app_label overrides = {attr: DeferredAttribute(attr, model) for attr in attrs} overrides["Meta"] = Meta overrides["__module__"] = model.__module__ overrides["_deferred"] = True return type(str(name), (model, ), overrides)
def _create_index_name(self, model, column_names, suffix=""): """ Generates a unique name for an index/unique constraint. """ # If there is just one column in the index, use a default algorithm from Django if len(column_names) == 1 and not suffix: return truncate_name( '%s_%s' % (model._meta.db_table, BaseDatabaseCreation._digest(column_names[0])), self.connection.ops.max_name_length()) # Else generate the name for the index using a different algorithm table_name = model._meta.db_table.replace('"', '').replace('.', '_') index_unique_name = '_%x' % abs( hash((table_name, ','.join(column_names)))) max_length = self.connection.ops.max_name_length() or 200 # If the index name is too long, truncate it index_name = ('%s_%s%s%s' % ( table_name, column_names[0], index_unique_name, suffix, )).replace('"', '').replace('.', '_') if len(index_name) > max_length: part = ('_%s%s%s' % (column_names[0], index_unique_name, suffix)) index_name = '%s%s' % (table_name[:(max_length - len(part))], part) # It shouldn't start with an underscore (Oracle hates this) if index_name[0] == "_": index_name = index_name[1:] # If it's STILL too long, just hash it down if len(index_name) > max_length: index_name = hashlib.md5( force_bytes(index_name)).hexdigest()[:max_length] # It can't start with a number on Oracle, so prepend D if we need to if index_name[0].isdigit(): index_name = "D%s" % index_name[:-1] return index_name
def _get_sequence_name(self, table): name_length = self.max_name_length() - 3 return '%s_SQ' % backend_utils.truncate_name(table, name_length).upper()
def _get_trigger_name(self, table): name_length = self.max_name_length() - 3 return '%s_TR' % backend_utils.truncate_name(table, name_length).upper()