簡體   English   中英

Django Import-Export:使用 UUID for id 字段導入模型時出現問題

[英]Django Import-Export: Issues importing to model with UUID for id field

我正在嘗試使用 Django-import-export 包(v3.0.0b4)通過 Django Admin 將 csv 文件(utf-8 編碼)導入 Django 模型。 我最初使用的是最后一個穩定版本,但被鼓勵嘗試預發布。 導入預覽看起來正確,但界面對 csv 的所有行顯示以下錯誤:

非特定字段“”不是有效的 UUID。

我嘗試了在 import_id_fields 中包含“id”或排除“id”字段並使用重命名的“unique_id”字段作為掛鈎的幾種排列方式。 我還嘗試在 csv 的“id”列和“unique_id”列中使用空白條目進行導入。 也完全從 csv 中省略了 id 字段。 出於某種原因,無論我是否填充 id 字段,都會返回一個空白字段。 我懷疑我做錯了什么,但我不清楚是什么。 resources.py、models.py 和 admin.py 包括在下面。 如果需要,很高興分享其他片段。

模型.py

from django.db import models
from django.db.models import Sum
import uuid
from datetime import datetime

class Purchase(models.Model):
    id = models.UUIDField(primary_key=True,default=uuid.uuid4, editable=False)
    date = models.DateField(blank=True,null=True)
    seller = models.CharField(max_length=200,default='')
    number = models.CharField(max_length=200,blank=True,default='')
    customer = models.CharField(max_length=200,default='')
    salesperson = models.CharField(max_length=200,blank=True,default='')
    discount = models.FloatField(blank=True,default=0,null=True)
    shipping = models.FloatField(blank=True,default=0,null=True)
    tax = models.FloatField(blank=True,default=0,null=True)
    file = models.FileField(blank=True,default='', upload_to='uploads/')

    @property
    def subtotal(self):
        return LineItem.objects.filter(Purchase=self).aggregate(Sum('amount'))['amount__sum']
    @property
    def grand_total(self):
        return round(self.subtotal+self.tax,2)

    class Meta:
        verbose_name_plural = "Purchases"
        
    def __str__(self):
        return self.seller+" "+self.number

class LineItem(models.Model):
    id = models.UUIDField(primary_key=True,default=uuid.uuid4, editable=False)
    purchase = models.ForeignKey(Purchase, on_delete= models.CASCADE, related_name="LineItem",default='',null=True)
    product_id = models.CharField(max_length=200,blank=True,default='')
    name = models.CharField(max_length=200,default='')
    category = models.CharField(max_length=200,blank=True,default='')
    qty = models.FloatField(null=True)
    qty_uom = models.CharField(max_length=200,default='')
    amount = models.FloatField(null=True)
    pack_qty = models.FloatField(null=True)
    pack_uom = models.CharField(max_length=200,default='')

    @property
    def date(self):
        return format(self.purchase.date,f"%m/%d/%Y")
    @property
    def bulk_unit_price(self):
        return round(self.amount/self.qty,2)
    @property
    def unit_price(self):
        return round(self.bulk_unit_price/self.pack_qty,2)

    class Meta:
        verbose_name_plural = "LineItems"
    
    def __str__(self):
        return self.name

資源.py

from import_export import resources, widgets, fields
from django.db.models.query import *
from .models import LineItem, Purchase

class CharRequiredWidget(widgets.CharWidget):
    def clean(self, value, row=None, *args, **kwargs):
        val = super().clean(value)
        if val:
            return val
        else:
            raise ValueError('this field is required')

class FloatWidget(widgets.DecimalWidget):
    def clean (self, value, row=None, *args, **kwargs):
        if self.is_empty(value):
            return None
        return float(str(value))

class ForeignKeyWidgetWithCreation(widgets.ForeignKeyWidget):
    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 LineItemResource(resources.ModelResource):

    class Meta:
        model = LineItem
        import_id_fields = ['unique_id',]
        exclude = ('id',)
        fields = ('unique_id','purchase__seller','purchase__number','purchase__date','product_id','name','category','qty','qty_uom','amount','pack_qty','pack_uom',)
        report_skipped = True
    
    unique_id = fields.Field(column_name='unique_id', attribute='id',widget=CharRequiredWidget())
    purchase__seller = fields.Field(attribute='purchase', widget=ForeignKeyWidgetWithCreation(model=Purchase,field='seller',create=True))
    purchase__number = fields.Field(attribute='purchase', widget=ForeignKeyWidgetWithCreation(model=Purchase,field='number',create=True))
    purchase__date = fields.Field(attribute='purchase',widget=ForeignKeyWidgetWithCreation(model=Purchase,field='date',create=True))
    product_id = fields.Field(saves_null_values=False, attribute='product_id',widget=CharRequiredWidget())
    name = fields.Field(saves_null_values=False, attribute='name',widget=CharRequiredWidget())
    category = fields.Field(saves_null_values=False, attribute='category',widget=CharRequiredWidget())
    qty = fields.Field(saves_null_values=False, attribute='qty',widget=FloatWidget())
    qty_uom = fields.Field(saves_null_values=False, attribute='qty_uom',widget=CharRequiredWidget())
    amount = fields.Field(saves_null_values=False, attribute='amount',widget=FloatWidget())
    pack_qty = fields.Field(saves_null_values=False, attribute='pack_qty',widget=FloatWidget())
    pack_uom = fields.Field(saves_null_values=False, attribute='pack_uom',widget=CharRequiredWidget())

管理員.py

from django.contrib import admin
from django.contrib import admin
from .models import Purchase, LineItem
from .resources import LineItemResource
from django.db import models
from import_export.admin import ImportExportModelAdmin

class LineItemAdmin(ImportExportModelAdmin):
    resource_class = LineItemResource
    list_display = ('id','purchase','product_id','name','category','qty','qty_uom','amount','pack_qty','pack_uom',)

admin.site.register(Purchase)
admin.site.register(LineItem, LineItemAdmin)

csv結構:

id,unique_id,purchase__date,purchase__seller,purchase__number,product_id,name,category,amount,qty,qty_uom,unit_price,pack_qty,pack_uom,$/unit  
,4e157e12-9a92-e303-44af-ee494593f073,4/29/2022,Vendor 1, 1423840,733111,item 1, category 1, 153.92,9.65,lb,15.95,1,lb,15.95

錯誤:

Exception in thread django-main-thread:
Traceback (most recent call last):
  File "\mysite\venv\lib\site-packages\django\db\models\fields\related_descriptors.py", line 187, in __get__
    rel_obj = self.field.get_cached_value(instance)
  File "\mysite\venv\lib\site-packages\django\db\models\fields\mixins.py", line 15, in get_cached_value
    return instance._state.fields_cache[cache_name]
KeyError: 'purchase'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "\mysite\venv\lib\site-packages\django\db\models\fields\__init__.py", line 2614, in to_python
    return uuid.UUID(**{input_form: value})
  File "\lib\uuid.py", line 177, in __init__
    raise ValueError('badly formed hexadecimal UUID string')
ValueError: badly formed hexadecimal UUID string

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "\lib\threading.py", line 973, in _bootstrap_inner
    self.run()
  File "\lib\threading.py", line 910, in run
    self._target(*self._args, **self._kwargs)
  File "\mysite\venv\lib\site-packages\django\utils\autoreload.py", line 64, in wrapper
    fn(*args, **kwargs)
  File "\mysite\venv\lib\site-packages\django\core\management\commands\runserver.py", line 125, in inner_run
    autoreload.raise_last_exception()
  File "\mysite\venv\lib\site-packages\django\utils\autoreload.py", line 87, in raise_last_exception
    raise _exception[1]
  File "\mysite\venv\lib\site-packages\django\core\management\__init__.py", line 398, in execute
    autoreload.check_errors(django.setup)()
  File "\mysite\venv\lib\site-packages\django\utils\autoreload.py", line 64, in wrapper
    fn(*args, **kwargs)
  File "\mysite\venv\lib\site-packages\django\__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "\mysite\venv\lib\site-packages\django\apps\registry.py", line 125, in populate
    app_config.ready()
  File "\mysite\venv\lib\site-packages\django\contrib\admin\apps.py", line 27, in ready
    self.module.autodiscover()
  File "\mysite\venv\lib\site-packages\django\contrib\admin\__init__.py", line 50, in autodiscover
    autodiscover_modules("admin", register_to=site)
  File "\mysite\venv\lib\site-packages\django\utils\module_loading.py", line 58, in autodiscover_modules
    import_module("%s.%s" % (app_config.name, module_to_search))
  File "\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "\mysite\main\admin.py", line 6, in <module>
    from .resources import LineItemResource
  File "\mysite\main\resources.py", line 175, in <module>
    result = resource.import_data(
  File "\mysite\venv\lib\site-packages\import_export\resources.py", line 809, in import_data
    return self.import_data_inner(
  File "\mysite\venv\lib\site-packages\import_export\resources.py", line 867, in import_data_inner
    raise row_result.validation_error
  File "\mysite\venv\lib\site-packages\import_export\resources.py", line 707, in import_row
    diff = self.get_diff_class()(self, original, new)
  File "\mysite\venv\lib\site-packages\import_export\resources.py", line 241, in __init__
    self.left = self._export_resource_fields(resource, instance)
  File "\mysite\venv\lib\site-packages\import_export\resources.py", line 262, in _export_resource_fields
    return [resource.export_field(f, instance) if instance else "" for f in resource.get_user_visible_fields()]
  File "\mysite\venv\lib\site-packages\import_export\resources.py", line 262, in <listcomp>
    return [resource.export_field(f, instance) if instance else "" for f in resource.get_user_visible_fields()]
  File "\mysite\venv\lib\site-packages\import_export\resources.py", line 920, in export_field
    return field.export(obj)
  File "\mysite\venv\lib\site-packages\import_export\fields.py", line 122, in export
    value = self.get_value(obj)
  File "\mysite\venv\lib\site-packages\import_export\fields.py", line 87, in get_value
    value = getattr(value, attr, None)
  File "\mysite\venv\lib\site-packages\django\db\models\fields\related_descriptors.py", line 205, in __get__
    rel_obj = self.get_object(instance)
  File "\mysite\venv\lib\site-packages\django\db\models\fields\related_descriptors.py", line 168, in get_object
    return qs.get(self.field.get_reverse_related_filter(instance))
  File "\mysite\venv\lib\site-packages\django\db\models\query.py", line 482, in get
    clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
  File "\mysite\venv\lib\site-packages\django\db\models\query.py", line 1071, in filter
    return self._filter_or_exclude(False, args, kwargs)
  File "\mysite\venv\lib\site-packages\django\db\models\query.py", line 1089, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "\mysite\venv\lib\site-packages\django\db\models\query.py", line 1096, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "\mysite\venv\lib\site-packages\django\db\models\sql\query.py", line 1502, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "\mysite\venv\lib\site-packages\django\db\models\sql\query.py", line 1532, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "\mysite\venv\lib\site-packages\django\db\models\sql\query.py", line 1358, in build_filter
    return self._add_q(
  File "\mysite\venv\lib\site-packages\django\db\models\sql\query.py", line 1532, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "\venv\lib\site-packages\django\db\models\sql\query.py", line 1448, in build_filter
    condition = self.build_lookup(lookups, col, value)
  File "\mysite\venv\lib\site-packages\django\db\models\sql\query.py", line 1273, in build_lookup
    lookup = lookup_class(lhs, rhs)
  File "\mysite\venv\lib\site-packages\django\db\models\lookups.py", line 27, in __init__
    self.rhs = self.get_prep_lookup()
  File "\mysite\venv\lib\site-packages\django\db\models\lookups.py", line 85, in get_prep_lookup
    return self.lhs.output_field.get_prep_value(self.rhs)
  File "\mysite\venv\lib\site-packages\django\db\models\fields\__init__.py", line 2598, in get_prep_value
    return self.to_python(value)
  File "\mysite\venv\lib\site-packages\django\db\models\fields\__init__.py", line 2616, in to_python
    raise exceptions.ValidationError(
django.core.exceptions.ValidationError: ['“” is not a valid UUID.']

乍一看,有一個空的id字段這一事實似乎是相關的(盡管您說您試圖省略該字段)。 您可以嘗試從Resource和 csv 中刪除對id的所有引用,盡管您需要保持unique_id聲明原樣。

您也可以在調試期間嘗試使用ForeignKeyWidget而不是ForeignKeyWidgetWithCreation

您可以嘗試通過腳本而不是管理控制台導入。 例如:

    with open('data.csv', 'r') as fh:
        dataset = tablib.Dataset().load(fh)

    resource = LineItemResource()
    result = resource.import_data(
         dataset,
         raise_errors=True
     )
    print(result)

這可能有助於您理解錯誤,盡管發現錯誤的最快方法是使用調試器單步執行。

您還可以嘗試使用 2.8.1 版本來查看是否有所不同(如果這已在 v3 中引入)。

如果您可以在 django-import-export 示例應用程序中重現它,那將是非常有趣的。 release-3-x分支中,有一個名為UUIDBook的測試模型。 如果您可以使用示例應用程序重現它,請提出問題,我們可以看看。

如果您發現錯誤的來源,請您回帖。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM