我正在使用django import-export库来处理数据。它运行良好,除了我无法导入外键中尚不存在的对象。
如果外键模型中存在对象(csv中的值),则导入正常。
但是如果外键模型中不存在对象/值,则表示“匹配查询不存在”,并且不会导入数据。
如果外键中不存在新对象,我怎么能告诉它将新对象添加到外键模型?
Models.py代码段
class Store(models.Model):
store_name = models.CharField(max_length=30)
def __unicode__(self):
return self.store_name
#etc
class Product(models.Model):
Store = models.ForeignKey(Store)
Category = models.ForeignKey(Category)
first_name = models.CharField(max_length=30)
second_name = models.CharField(max_length=30)
...
Admin.py代码段
admin.site.register(Category)
admin.site.register(Store)
class ProductResource(resources.ModelResource):
store_name = fields.Field(column_name='store_name', attribute='Store',
widget=ForeignKeyWidget(Store, 'store_name'))
def __unicode__(self):
return self.store_name.name
class Meta:
model = Product
fields = ('id', 'first_name', 'second_name','store_name')
export_order = ('id', 'second_name', 'first_name')
skip_unchanged = False
report_skipped = False
widgets = {
'published': {'format': '%d.%m.%Y'},
}
class ProductAdmin(ImportExportModelAdmin):
resource_class = ProductResource
list_display = ('first_name', 'second_name')
admin.site.register(Product, ProductAdmin)
答案 0 :(得分:1)
在ForeignKeyWidget
你有方法
def clean(self, value):
val = super(ForeignKeyWidget, self).clean(value)
return self.model.objects.get(**{self.field: val}) if val else None
您可以尝试覆盖它以执行get_or_create
...
from import_export.widgets import ForeignKeyWidget
class MyCustomizationToFKWidget(ForeignKeyWidget):
def clean(self, value):
val = super(ForeignKeyWidget, self).clean(value)
HERE SOME LOGIC OVERRIDEN
答案 1 :(得分:1)
如果您的模型的ForeignKey可以为null:
MyForeignKeyName = Models.ForeignKey(<modelclass>,blank=True, null=True)
您可以在Resource类中添加before_import_row()方法:
def before_import_row(self,row) :
fieldname = 'MyForeignKeyName'
if not( <modelclass>.objects.filter(pk=row[fieldname]).exists() ) :
# print(row['id'],row[fieldname],'unknown key !') # console log
row[fieldname ] = None # or something else.. *
* ..符合您的foreignkey configuration [python None = SQL null]
我对Django有点新意,所以也许这不是更好的方法,但这解决了我的问题。
还有一些关于db_constraint = False的东西,可以添加到ForeignKey的参数(一些信息@stackoverflow和@django)但是,我找不到我的方式它
答案 2 :(得分:1)
您可以在Resource类的before_import_row()方法中创建缺少的外键条目:
class ProductResource(resources.ModelResource):
...
def before_import_row(self, row, **kwargs):
Store.objects.get_or_create(
store_name=row.get('store_name')
)
答案 3 :(得分:0)
我发现最好的方法是使用自定义小部件。我在 django-import-export GitHub 项目 here 上找到了一个线程,其中包含很多有用的信息。从中我拼凑了这两个支持创建新对象的自定义小部件。通过将它们添加到您的 failed_jobs
模块中,这些应该都能正常工作:
admin.py
您可以像这样在导入-导出模型资源类中使用这些:
from import_export.widgets import ManyToManyWidget
from django.db.models import QuerySet
class ForeignKeyWidgetWithCreation(ForeignKeyWidget):
"""
Taken from a GitHub post.
https://github.com/django-import-export/django-import-export/issues/318#issuecomment-139989178
"""
def __init__(self, model, field="pk", create=False, **kwargs):
self.model = model
self.field = field
self.create = create
super(ForeignKeyWidgetWithCreation, self).__init__(model, field=field, **kwargs)
def clean(self, value, **kwargs):
if not value:
return None
if self.create:
self.model.objects.get_or_create(**{self.field: value})
val = super(ForeignKeyWidgetWithCreation, self).clean(value, **kwargs)
return self.model.objects.get(**{self.field: val}) if val else None
class ManyToManyWidgetWithCreation(ManyToManyWidget):
"""
Taken from a GitHub post.
https://github.com/django-import-export/django-import-export/issues/318#issuecomment-139989178
"""
def __init__(self, model, field="pk", create=False, **kwargs):
self.model = model
self.field = field
self.create = create
super(ManyToManyWidgetWithCreation, self).__init__(model, field=field, **kwargs)
def clean(self, value, **kwargs):
# If no value was passed then we don't have anything to clean.
if not value:
return self.model.objects.none()
# Call the super method. This will return a QuerySet containing any pre-existing objects.
# Any missing objects will be
cleaned_value: QuerySet = super(ManyToManyWidgetWithCreation, self).clean(
value, **kwargs
)
# Value will be a string that is separated by `self.separator`.
# Each entry in the list will be a reference to an object. If the object exists it will
# appear in the cleaned_value results. If the number of objects in the cleaned_value
# results matches the number of objects in the delimited list then all objects already
# exist and we can just return those results.
object_list = value.split(self.separator)
if len(cleaned_value.all()) == len(object_list):
return cleaned_value
# If we are creating new objects then loop over each object in the list and
# use get_or_create to, um, get or create the object.
if self.create:
for object_value in object_list:
_instance, _new = self.model.objects.get_or_create(
**{self.field: object_value}
)
# Use `filter` to re-locate all the objects in the list.
model_objects = self.model.objects.filter(**{f"{self.field}__in": object_list})
return model_objects