【问题标题】:django-import-export - Importing ManyToMany from JSON filedjango-import-export - 从 JSON 文件导入多对多
【发布时间】:2019-11-21 02:40:57
【问题描述】:

我正在尝试导入一个包含多对多关系的 JSON 文件,但遇到了一些问题。这是我的代码:

book.json

{
  "title": "The Lord of the Rings",
  "categories": [
      "Fantasy",
      "Action",
      "Adventure"
  ]
}

models.py

from django.db import models

class Category(models.Model):
    guid = models.CharField(max_length=36)
    display_name = models.CharField(max_length=50)
    description = models.TextField()

    def __str__(self):
        return self.display_name

class Book(models.Model):
    title = models.CharField(max_length=200)
    categories = models.ManyToManyField(Category)

    def __str__(self):
        return self.title

resources.py

from import_export import resources
from import_export.fields import Field
from import_export.widgets import ManyToManyWidget
from .models import Book, Category

class BookResource(resources.ModelResource):
    categories = Field(widget=ManyToManyWidget(Category))

    class Meta:
        model = Book
        import_id_fields = ('title',)

importer.py

from resources import BookResource
from models import Book

with open("book.json", 'r') as f:
        book = '[' + f.read() + ']'
        result = BookResource().import_data(tablib.Dataset().load(book), raise_errors=True, dry_run=True)

这似乎没有错误地运行,并将新的book 添加到数据库中。问题是它不会在book_categories 表(PostgreSQL)中创建新条目。我想我肯定在某处遗漏了一步,因为我还没有定义如何找到正确的category 数据库条目,给定display_name 字符串列表。我在正确的轨道上吗?

【问题讨论】:

  • 好吧,您在 importer.py 中的导入不正确,但我想这是一个复制问题。

标签: python django postgresql django-models django-import-export


【解决方案1】:

简而言之,这种情况不是由django-import-export 处理的:如果您查看ManyToManyWidget.clean(),您会看到默认情况下它需要一个逗号分隔的整数字符串。您可以在那里做一些事情,但它只是希望您先导入类别,然后导入书籍。

我最终做的是删除 BookResource 上的字段和小部件并将其替换为小部件字典:

class BookResource(resources.ModelResource):
    class Meta:
        model = Book
        import_id_fields = ("title",)
        fields = ("id", "title", "categories")
        widgets = {"categories": {"field": "display_name"}}

如果您先导入类别,那么您的图书将被正确链接。好吧,对 JSON 进行了一次修改,我将导入器脚本包装起来,因此它可以在控制台上运行:

import django
import os

os.environ["DJANGO_SETTINGS_MODULE"] = "testbed.settings"

BOOK_DATA = """[{
  "title": "The Lord of the Rings",
  "categories": "Fantasy,Action,Adventure"
}]"""


def run():
    from bookstore.resources import BookResource
    import tablib

    importer = BookResource()
    dataset = tablib.Dataset()
    data = dataset.load(BOOK_DATA, format="json")
    result = importer.import_data(data, raise_errors=True, dry_run=False)
    print(result.has_errors())


if __name__ == "__main__":
    import sys

    sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
    django.setup()
    run()

可以编写一个 JSON 特定的 M2M 小部件,但是在导入类别时,您需要找出每一行已经存在和不存在的行。这是可行的,所以也许我已经让你走上了修复它的正确轨道;)

【讨论】:

  • 有趣,这似乎适用于这个非常基本的示例。我不太确定小部件字典是如何工作的——你有指向一些文档的链接吗?我觉得默认情况下不支持 JSON 数组有点奇怪..?我想知道在导入之前是否有另一种方法将 JSON 转换为 CSV。
  • 我发现这些文档充其量是非常精简的,最值得注意的是如何将事物联系在一起。我不得不查看源代码以找出小部件结构。在人类语言中:它将与书籍上的类别字段关联的 m2m 小部件的字段参数设置为“display_name”,这会导致查询集查找 filter(display_name__in=values)。如果我必须解决这个问题,我会至少为 JSON m2m 制作一个自定义小部件,这样您就可以利用值已经是数组而不是需要拆分的字符串这一事实。
  • 将数组拆分为字符串并不适用于我的所有情况,因为还有对象列表。我最终创建了一个自定义的 m2m 小部件,到目前为止它似乎还在工作。如果其他人偶然发现此问题,我会将其发布为答案。感谢您的所有帮助!
【解决方案2】:

感谢@Melvyn,我能够通过为 JSON 数组创建自定义小部件来解决这个问题。我会发布它以防其他人遇到这个问题。

class ManyToManyJSONWidget(ManyToManyWidget):
    def __init__(self, model, field=None, *args, **kwargs):
        if field is None:
            field = 'pk'
        self.model = model
        self.separator = None
        self.field = field

    def clean(self, value, row=None, *args, **kwargs):
        if not value:
            return self.model.objects.none()
        else:
            objs = []
            for i in value:
                obj = self.model.objects.filter(**{
                    '%s__iexact' % self.field: i
                }).first()
                objs.append(obj)
            return objs

    def render(self, value, obj=None):
        return ""

【讨论】:

    猜你喜欢
    • 2015-07-17
    • 2019-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-13
    相关资源
    最近更新 更多