python mysql django django-models unique-constraint

python - set title matplotlib



Nombre Ășnico de clave personalizada (5)

Tengo un modelo con un unique_together definido para que 3 campos sean únicos juntos:

class MyModel(models.Model): clid = models.AutoField(primary_key=True, db_column=''CLID'') csid = models.IntegerField(db_column=''CSID'') cid = models.IntegerField(db_column=''CID'') uuid = models.CharField(max_length=96, db_column=''UUID'', blank=True) class Meta(models.Meta): unique_together = [ ["csid", "cid", "uuid"], ]

Ahora, si intento guardar una instancia de MyModel con una combinación existente de csid + cid + uuid, obtendría:

IntegrityError: (1062, "Duplicate entry ''1-1-1'' for key ''CSID''")

Cual es correcta. Pero, ¿hay una manera de personalizar ese nombre de clave? ( CSID en este caso)

En otras palabras, ¿puedo proporcionar un nombre para una restricción enumerada en unique_together ?

Por lo que entiendo, esto no está cubierto en la documentación.


Cambiar el nombre del índice en ./manage.py sqlall output.

Puede ejecutar ./manage.py sqlall usted mismo y agregar el nombre de la restricción usted mismo y aplicar manualmente en lugar de syncdb .

$ ./manage.py sqlall test BEGIN; CREATE TABLE `test_mymodel` ( `CLID` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `CSID` integer NOT NULL, `CID` integer NOT NULL, `UUID` varchar(96) NOT NULL, UNIQUE (`CSID`, `CID`, `UUID`) ) ; COMMIT;

p.ej

$ ./manage.py sqlall test BEGIN; CREATE TABLE `test_mymodel` ( `CLID` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `CSID` integer NOT NULL, `CID` integer NOT NULL, `UUID` varchar(96) NOT NULL, UNIQUE constraint_name (`CSID`, `CID`, `UUID`) ) ; COMMIT;

Anulando BaseDatabaseSchemaEditor._create_index_name

La solución señalada por @danihp está incompleta, solo funciona para actualizaciones de campo ( BaseDatabaseSchemaEditor._alter_field )

El sql que obtengo al reemplazar _create_index_name es:

BEGIN; CREATE TABLE "testapp_mymodel" ( "CLID" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "CSID" integer NOT NULL, "CID" integer NOT NULL, "UUID" varchar(96) NOT NULL, UNIQUE ("CSID", "CID", "UUID") ) ; COMMIT;

Anulando BaseDatabaseSchemaEditor.create_model

basado en https://github.com/django/django/blob/master/django/db/backends/schema.py

class BaseDatabaseSchemaEditor(object): # Overrideable SQL templates sql_create_table_unique = "UNIQUE (%(columns)s)" sql_create_unique = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s UNIQUE (%(columns)s)" sql_delete_unique = "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s"

y esta es la pieza en create_model que es de interés:

# Add any unique_togethers for fields in model._meta.unique_together: columns = [model._meta.get_field_by_name(field)[0].column for field in fields] column_sqls.append(self.sql_create_table_unique % { "columns": ", ".join(self.quote_name(column) for column in columns), })

Conclusión

Tú podrías:

  • reemplaza a create_model para usar _create_index_name para las unique_together de unique_together .
  • modifique la plantilla sql_create_table_unique para incluir un parámetro de name .

También puede comprobar una posible solución en este ticket:

https://code.djangoproject.com/ticket/24102


Creo que tienes que hacer eso en tu base de datos;

MySQL:

ALTER TABLE `votes` ADD UNIQUE `unique_index`(`user`, `email`, `address`);

Creo que entonces diría ... para clave ''unique_index''


El error de integridad se genera desde la base de datos pero desde django:

create table t ( a int, b int , c int); alter table t add constraint u unique ( a,b,c); <-- ''u'' insert into t values ( 1,2,3); insert into t values ( 1,2,3); Duplicate entry ''1-2-3'' for key ''u'' <---- ''u''

Eso significa que necesita crear una restricción con el nombre deseado en la base de datos. Pero es django en las migraciones quien restringe los nombres. Mira en _create_unique_sql:

def _create_unique_sql(self, model, columns): return self.sql_create_unique % { "table": self.quote_name(model._meta.db_table), "name": self.quote_name(self._create_index_name(model, columns, suffix="_uniq")), "columns": ", ".join(self.quote_name(column) for column in columns), }

Es _create_index_name quien tiene el algoritmo para nombrar restricciones:

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, self._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

Para la versión actual de django (1.7), el nombre de la restricción para una restricción única compuesta se ve así:

>>> _create_index_name( ''people'', [ ''c1'', ''c2'', ''c3''], ''_uniq'' ) ''myapp_people_c1_d22a1efbe4793fd_uniq''

Debería sobrescribir _create_index_name de alguna manera para cambiar el algoritmo. Una forma, tal vez, escribiendo su propio backend de DatabaseSchemaEditor de DatabaseSchemaEditor _create_index_name de mysql y sobrescribiendo _create_index_name en su DatabaseSchemaEditor en su schema.py (no probado)


No está bien documentado, pero dependiendo de si está utilizando Django 1.6 o 1.7, hay dos formas de hacerlo:

En Django 1.6, puedes anular el unique_error_message , de esta forma :

class MyModel(models.Model): clid = models.AutoField(primary_key=True, db_column=''CLID'') csid = models.IntegerField(db_column=''CSID'') cid = models.IntegerField(db_column=''CID'') # .... def unique_error_message(self, model_class, unique_check): if model_class == type(self) and unique_check == ("csid", "cid", "uuid"): return _(''Your custom error'') else: return super(MyModel, self).unique_error_message(model_class, unique_check)

O en Django 1.7 :

class MyModel(models.Model): clid = models.AutoField(primary_key=True, db_column=''CLID'') csid = models.IntegerField(db_column=''CSID'') cid = models.IntegerField(db_column=''CID'') uuid = models.CharField(max_length=96, db_column=''UUID'', blank=True) class Meta(models.Meta): unique_together = [ ["csid", "cid", "uuid"], ] error_messages = { NON_FIELD_ERRORS: { ''unique_together'': "%(model_name)s''s %(field_labels)s are not unique.", } }


Una solución es que puede capturar IntegrityError en save () y luego hacer un mensaje de error personalizado como desee, como se muestra a continuación.

try: obj = MyModel() obj.csid=1 obj.cid=1 obj.uuid=1 obj.save() except IntegrityError: message = "IntegrityError: Duplicate entry ''1-1-1'' for key ''CSID'', ''cid'', ''uuid'' "

Ahora puede usar este mensaje para mostrarlo como mensaje de error.