django.db.utils.IntegrityError: (1062, “entrada duplicada ” para clave ‘slug'”)

Estoy tratando de seguir el libro de tangowithdjango y debo agregar una bala para actualizar la tabla de categorías. Sin embargo, estoy recibiendo un error después de intentar migrar las bases de datos.

http://www.tangowithdjango.com/book17/chapters/models_templates.html#creating-a-details-page

No proporcioné un valor predeterminado para la bala, por lo que Django me pidió que proporcionara uno y, tal como se indica en el libro, escribí “”.

Vale la pena notar que, en lugar de usar sqlite como en el libro original, estoy usando mysql.

models.py from django.db import models from django.template.defaultfilters import slugify # Create your models here. class Category(models.Model): name = models.CharField(max_length=128, unique=True) views = models.IntegerField(default=0) likes = models.IntegerField(default=0) slug = models.SlugField(unique=True) def save(self, *args, **kwargs): self.slug = slugify(self.name) super(Category, self).save(*args, **kwargs) class Meta: verbose_name_plural = "Categories" def __unicode__(self): return self.name class Page(models.Model): category = models.ForeignKey(Category) title = models.CharField(max_length=128) url = models.URLField() views = models.IntegerField(default=0) def __unicode__(self): return self.title 

El símbolo del sistema

 sudo python manage.py migrate Operations to perform: Apply all migrations: admin, rango, contenttypes, auth, sessions Running migrations: Applying rango.0003_category_slug...Traceback (most recent call last): File "manage.py", line 10, in  execute_from_command_line(sys.argv) File "/usr/local/lib/python2.7/dist-packages/django/core/management/__init__.py", line 385, in execute_from_command_line utility.execute() File "/usr/local/lib/python2.7/dist-packages/django/core/management/__init__.py", line 377, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 288, in run_from_argv self.execute(*args, **options.__dict__) File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 338, in execute output = self.handle(*args, **options) File "/usr/local/lib/python2.7/dist-packages/django/core/management/commands/migrate.py", line 160, in handle executor.migrate(targets, plan, fake=options.get("fake", False)) File "/usr/local/lib/python2.7/dist-packages/django/db/migrations/executor.py", line 63, in migrate self.apply_migration(migration, fake=fake) File "/usr/local/lib/python2.7/dist-packages/django/db/migrations/executor.py", line 97, in apply_migration migration.apply(project_state, schema_editor) File "/usr/local/lib/python2.7/dist-packages/django/db/migrations/migration.py", line 107, in apply operation.database_forwards(self.app_label, schema_editor, project_state, new_state) File "/usr/local/lib/python2.7/dist-packages/django/db/migrations/operations/fields.py", line 37, in database_forwards field, File "/usr/local/lib/python2.7/dist-packages/django/db/backends/mysql/schema.py", line 42, in add_field super(DatabaseSchemaEditor, self).add_field(model, field) File "/usr/local/lib/python2.7/dist-packages/django/db/backends/schema.py", line 411, in add_field self.execute(sql, params) File "/usr/local/lib/python2.7/dist-packages/django/db/backends/schema.py", line 98, in execute cursor.execute(sql, params) File "/usr/local/lib/python2.7/dist-packages/django/db/backends/utils.py", line 81, in execute return super(CursorDebugWrapper, self).execute(sql, params) File "/usr/local/lib/python2.7/dist-packages/django/db/backends/utils.py", line 65, in execute return self.cursor.execute(sql, params) File "/usr/local/lib/python2.7/dist-packages/django/db/utils.py", line 94, in __exit__ six.reraise(dj_exc_type, dj_exc_value, traceback) File "/usr/local/lib/python2.7/dist-packages/django/db/backends/utils.py", line 65, in execute return self.cursor.execute(sql, params) File "/usr/local/lib/python2.7/dist-packages/django/db/backends/mysql/base.py", line 128, in execute return self.cursor.execute(query, args) File "/usr/local/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 205, in execute self.errorhandler(self, exc, value) File "/usr/local/lib/python2.7/dist-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler raise errorclass, errorvalue django.db.utils.IntegrityError: (1062, "Duplicate entry '' for key 'slug'") 

Vamos a analizarlo paso a paso:

  1. Está agregando el campo slug con unique = True , eso significa que cada registro debe tener un valor diferente, no puede haber dos registros con el mismo valor en slug
  2. Está creando la migración: django le pide un valor predeterminado para los campos que ya existen en la base de datos, por lo que proporcionó ” (cadena vacía) como ese valor.
  3. Ahora django está intentando migrar tu base de datos. En la base de datos tenemos al menos 2 registros.
  4. El primer registro se migra, la columna slug se llena con una cadena vacía. Eso es bueno porque ningún otro registro tiene una cadena vacía en el campo slug
  5. El segundo registro se migra, la columna slug se llena con una cadena vacía. Eso falla, porque el primer registro ya tiene una cadena vacía en el campo slug . Se levanta la excepción y se aborta la migración.

Es por eso que tu migración falla. Todo lo que debe hacer es editar la migración, copiar las migrations.AlterField Operación migrations.AlterField dos veces, en la primera operación eliminar unique = True. Entre esas operaciones, debe poner migrations.RunPython operation y proporcionar 2 parámetros en ese valor: generate_slugs y migrations.RunPython.noop .

Ahora debe crear dentro de su función de migración ANTES de la clase de migración, nombre esa función generate_slugs . La función debe tomar 2 argumentos: apps y schema_editor . En tu función pon en primera línea:

 Category = apps.get_model('your_app_name', 'Category') 

y ahora use Category.objects.all() para hacer un bucle de todos sus registros y proporcionar un slug único para cada uno de ellos.

Si tiene más de una categoría en su tabla, entonces no puede tener unique=True y default='' , porque entonces tendrá más de una categoría con slug='' . Si tu tutorial dice que debes hacer esto, entonces es un mal consejo, aunque podría funcionar en SQLite.

El enfoque correcto para agregar un campo único a un modelo es:

  1. Elimine su migración actual que no está funcionando.
  2. Agregue el campo slug, con unique=False . Crea una nueva migración y ejecútala.
  3. Establecer una babosa única para cada categoría. Parece que la secuencia de comandos de rango de rango podría hacer esto. Alternativamente, puede escribir una migración para establecer las babosas, o incluso configurarlas manualmente en el administrador de Django.
  4. Cambia el campo de slug a unique=True . Crea una nueva migración y ejecútala.

Si eso es demasiado difícil, entonces puede eliminar todas sus categorías de su base de datos excepto una. Entonces su migración actual se ejecutará sin tener problemas con la restricción única. Puede agregar las categorías de nuevo después.

Debes tener filas en tu tabla con slugs vacías, lo cual es una violación de la restricción única de mysql que creaste. Puede actualizarlos manualmente ejecutando manage.py dbshell para llegar al cliente mysql, luego actualizando las filas ofensivas, por ejemplo

update table rango_category set slug = name where slug = '';

(Suponiendo que las filas con slugs en blanco tengan nombres). O puedes borrar las filas con

delete from rango_category where slug = '';

Después de eso, deberías poder ejecutar tus migraciones.