django.db.utils.IntegrityError: (1062, “Duplicate

2019-01-26 22:38发布

I'm trying to follow the tangowithdjango book and must add a slug to update the category table. However I'm getting an error after trying to migrate the databases.

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

I didn't provide a default value for the slug, so Django asked me to provide one and as the book instructed I type in ''.

It's worth noticing that instead of using sqlite as in the original book I'm using 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

The command prompt

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 <module>
    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'")

3条回答
【Aperson】
2楼-- · 2019-01-26 23:19

You must have rows in your table already with empty slugs, which is a violation of the mysql unique constraint you created. You can update them manually by running manage.py dbshell to get to the mysql client, then updating the offending rows, e.g.

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

(assuming the rows with blank slugs have names). Or you can delete the rows with

delete from rango_category where slug = '';

After that, you should be able to run your migrations.

查看更多
放我归山
3楼-- · 2019-01-26 23:38

If you have more than one category in your table, then you cannot have unique=True and default='', because then you will have more than one category with slug=''. If your tutorial says to do this, then it's bad advice, although it might work in SQLite.

The correct approach to add a unique field to a model is:

  1. Delete your current migration that isn't working.
  2. Add the slug field, with unique=False. Create a new migration and run it.
  3. Set a unique slug for every category. It sounds like the rango populate script might do this. Alternatively, you could write a migration to set the slugs, or even set them manually in the Django admin.
  4. Change the slug field to unique=True. Create a new migration and run it.

If that's too difficult, then you could delete all your categories from your database except one. Then your current migration will run without having problems with the unique constraint. You can add the categories again afterwards.

查看更多
一纸荒年 Trace。
4楼-- · 2019-01-26 23:39

Let's analyse it step by step:

  1. You're adding slug field with unique = True, that means: each record must have different value, there can't be two records with same value in slug
  2. You're creating migration: django asks you for default value for fields that exists already in database, so you provided '' (empty string) as that value.
  3. Now django is trying to migrate your database. In database we have at least 2 records
  4. First record is migrated, slug column is populated with empty string. That's good because no other record is having empty string in slug field
  5. Second record is migrated, slug column is populated with empty string. That fails, because first record already have empty string in slug field. Exception is raised and migration is aborted.

That's why your migration fails. All you should do is to edit migration, copy migrations.AlterField operation twice, in first operation remove unique=True. Between that operations you should put migrations.RunPython operation and provide 2 parameters into that: generate_slugs and migrations.RunPython.noop.

Now you must create inside your migration function BEFORE migration class, name that function generate_slugs. Function should take 2 arguments: apps and schema_editor. In your function put at first line:

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

and now use Category.objects.all() to loop all your records and provide unique slug for each of them.

查看更多
登录 后发表回答