Django 导入 - 导出:尝试在具有唯一或 unique_together 约束的字段中插入重复记录时出现 IntegrittyError
Django Import - Export: IntegrittyError when trying to insert duplicate record in field(s) with unique or unique_together constraints
更新
我已经提交了 feature request。这个想法是在 unique
或 unique_together
拒绝数据库中已存在的记录时,对数据库生成的 IntegrittyError
进行 pass
。
我有以下型号:
class Compositions(models.Model):
composer_key = models.ForeignKey(
Composer,
)
composition = models.CharField(
max_length=383,
)
class Meta(object):
unique_together = (('composer_key', 'composition'), )
在管理界面中使用 django-import-export,没有为 csv 文件中的每个条目提供 id
,...如果一对csv 文件已经存在,程序将因完整性错误而中断
duplicate key value violates unique constraint "data_compositions_composer_key_id_12f91ce7dbac16bf_uniq"
DETAIL: Key (composer_key_id, composition)=(2, Star Wars) already exists.
CSV 文件如下:
id composer_key composition
1 Hot Stuff
2 Star Wars
The idea 是使用 skip_row
并在管理员中实现它。
admin.py:
class CompositionsResource(resources.ModelResource):
class Meta:
model = Compositions
skip_unchanged = True
report_skipped = True
class CompositionsAdmin(ImportExportModelAdmin):
resource_class = CompositionsResource
admin.site.register(Compositions, CompositionsAdmin)
然而,这并不能解决问题,因为 skip_row
需要 csv 文件中的 id
来检查每一行是否与特定的数据库条目相同。
考虑到在使用unique
(_together
)的时候可以通过数据库进行这个控制,捕获这个错误会不会有效然后return skip_row = True
或者 pass
这个错误?
models.py:
class Compositions(models.Model):
composer_key = models.ForeignKey(
Composer,
)
composition = models.CharField(
max_length=383,
unique=False
)
date_created = models.DateTimeField(default=timezone.now)
class Meta(object):
unique_together = (('composer_key','composition'),)
这是我为上述模型编写的 'on the fly' 脚本,目的是自动丢弃重复的条目。当我用数据填充文件 duc.csv
的相关列时,我已将它保存到 ./project_name/csv.py
并从 shell 导入它。这些列不应包含 headers。只有数据。
$./manage.py shell
>>> from project_name import csv
csv.py:
from data.models import Composer, Compositions
import csv
import sys, traceback
from django.utils import timezone
filename = '/path/to/duc.csv'
with open(filename, newline='') as csvfile:
all_lines = csv.reader(csvfile, delimiter=',', quotechar='"')
for each_line in all_lines:
print (each_line)
try:
instance = Compositions(
id=None,
date_created=timezone.now(),
composer_key=Composer.objects.get(id=each_line[2]),
composition=each_line[3]
)
instance.save()
print ("Saved composition: {0}".format(each_line[3]))
except: // exception type must be inserted here
exc_type, exc_value, exc_traceback = sys.exc_info() //debugging mostly
print (exc_value)
只需要一个更改。你可以使用 django-import-export
models.py
class Compositions(models.Model):
composer_key = models.ForeignKey(
Composer,
)
composition = models.CharField(
max_length=383,
unique=False
)
date_created = models.DateTimeField(default=timezone.now)
class Meta(object):
unique_together = (('composer_key','composition'),)
尝试覆盖 save_instance。并在失败时忽略错误。
admin.py
class CompositionsResource(resources.ModelResource):
class Meta:
model = Compositions
skip_unchanged = True
report_skipped = True
def save_instance(self, instance, using_transactions=True, dry_run=False):
try:
super(CompositionsResource, self).save_instance(instance, using_transactions, dry_run)
except IntegrityError:
pass
class CompositionsAdmin(ImportExportModelAdmin):
resource_class = CompositionsResource
admin.site.register(Compositions, CompositionsAdmin)
并导入这个
from django.db import IntegrityError
关于已接受答案的注释:它会给出预期的结果,但会大大降低磁盘使用量和处理大文件的时间。
我一直在使用的一种更有效的方法(在花费大量时间浏览文档之后)是覆盖 skip_row
,并使用一组元组作为唯一约束作为 class。当然,我仍然覆盖 save_instance
,因为另一个答案建议处理通过的 IntegrityErrors。
Python sets
不创建重复条目,因此它们似乎适合这种唯一索引。
class CompositionsResource(resources.ModelResource):
set_unique = set()
class Meta:
model = Composers
skip_unchanged = True
report_skipped = True
def before_import(self, dataset, using_transactions, dry_run, **kwargs):
# Clear out anything that may be there from a dry_run,
# such as the admin mixin preview
self.set_unique = set()
def skip_row(self, instance, original):
composer_key = instance.composer_key # Could also use composer_key_id
composition = instance.composition
tuple_unique = (composer_key, composition)
if tuple_unique in self.set_unique:
return true
else:
self.set_unique.add(tuple_unique)
return super(CompositionsResource, self).skip_row(instance, original)
# save_instance override should still go here to pass on IntegrityError
这种方法至少会减少在同一数据集中遇到的重复项。我用它来处理多个平面文件,每个文件大约有 60000 行,但有很多 repetitive/nested 外键。这使得初始数据导入速度更快。
更新
我已经提交了 feature request。这个想法是在 unique
或 unique_together
拒绝数据库中已存在的记录时,对数据库生成的 IntegrittyError
进行 pass
。
我有以下型号:
class Compositions(models.Model):
composer_key = models.ForeignKey(
Composer,
)
composition = models.CharField(
max_length=383,
)
class Meta(object):
unique_together = (('composer_key', 'composition'), )
在管理界面中使用 django-import-export,没有为 csv 文件中的每个条目提供 id
,...如果一对csv 文件已经存在,程序将因完整性错误而中断
duplicate key value violates unique constraint "data_compositions_composer_key_id_12f91ce7dbac16bf_uniq"
DETAIL: Key (composer_key_id, composition)=(2, Star Wars) already exists.
CSV 文件如下:
id composer_key composition
1 Hot Stuff
2 Star Wars
The idea 是使用 skip_row
并在管理员中实现它。
admin.py:
class CompositionsResource(resources.ModelResource):
class Meta:
model = Compositions
skip_unchanged = True
report_skipped = True
class CompositionsAdmin(ImportExportModelAdmin):
resource_class = CompositionsResource
admin.site.register(Compositions, CompositionsAdmin)
然而,这并不能解决问题,因为 skip_row
需要 csv 文件中的 id
来检查每一行是否与特定的数据库条目相同。
考虑到在使用unique
(_together
)的时候可以通过数据库进行这个控制,捕获这个错误会不会有效然后return skip_row = True
或者 pass
这个错误?
models.py:
class Compositions(models.Model):
composer_key = models.ForeignKey(
Composer,
)
composition = models.CharField(
max_length=383,
unique=False
)
date_created = models.DateTimeField(default=timezone.now)
class Meta(object):
unique_together = (('composer_key','composition'),)
这是我为上述模型编写的 'on the fly' 脚本,目的是自动丢弃重复的条目。当我用数据填充文件 duc.csv
的相关列时,我已将它保存到 ./project_name/csv.py
并从 shell 导入它。这些列不应包含 headers。只有数据。
$./manage.py shell
>>> from project_name import csv
csv.py:
from data.models import Composer, Compositions
import csv
import sys, traceback
from django.utils import timezone
filename = '/path/to/duc.csv'
with open(filename, newline='') as csvfile:
all_lines = csv.reader(csvfile, delimiter=',', quotechar='"')
for each_line in all_lines:
print (each_line)
try:
instance = Compositions(
id=None,
date_created=timezone.now(),
composer_key=Composer.objects.get(id=each_line[2]),
composition=each_line[3]
)
instance.save()
print ("Saved composition: {0}".format(each_line[3]))
except: // exception type must be inserted here
exc_type, exc_value, exc_traceback = sys.exc_info() //debugging mostly
print (exc_value)
只需要一个更改。你可以使用 django-import-export
models.py
class Compositions(models.Model):
composer_key = models.ForeignKey(
Composer,
)
composition = models.CharField(
max_length=383,
unique=False
)
date_created = models.DateTimeField(default=timezone.now)
class Meta(object):
unique_together = (('composer_key','composition'),)
尝试覆盖 save_instance。并在失败时忽略错误。 admin.py
class CompositionsResource(resources.ModelResource):
class Meta:
model = Compositions
skip_unchanged = True
report_skipped = True
def save_instance(self, instance, using_transactions=True, dry_run=False):
try:
super(CompositionsResource, self).save_instance(instance, using_transactions, dry_run)
except IntegrityError:
pass
class CompositionsAdmin(ImportExportModelAdmin):
resource_class = CompositionsResource
admin.site.register(Compositions, CompositionsAdmin)
并导入这个
from django.db import IntegrityError
关于已接受答案的注释:它会给出预期的结果,但会大大降低磁盘使用量和处理大文件的时间。
我一直在使用的一种更有效的方法(在花费大量时间浏览文档之后)是覆盖 skip_row
,并使用一组元组作为唯一约束作为 class。当然,我仍然覆盖 save_instance
,因为另一个答案建议处理通过的 IntegrityErrors。
Python sets
不创建重复条目,因此它们似乎适合这种唯一索引。
class CompositionsResource(resources.ModelResource):
set_unique = set()
class Meta:
model = Composers
skip_unchanged = True
report_skipped = True
def before_import(self, dataset, using_transactions, dry_run, **kwargs):
# Clear out anything that may be there from a dry_run,
# such as the admin mixin preview
self.set_unique = set()
def skip_row(self, instance, original):
composer_key = instance.composer_key # Could also use composer_key_id
composition = instance.composition
tuple_unique = (composer_key, composition)
if tuple_unique in self.set_unique:
return true
else:
self.set_unique.add(tuple_unique)
return super(CompositionsResource, self).skip_row(instance, original)
# save_instance override should still go here to pass on IntegrityError
这种方法至少会减少在同一数据集中遇到的重复项。我用它来处理多个平面文件,每个文件大约有 60000 行,但有很多 repetitive/nested 外键。这使得初始数据导入速度更快。