【问题标题】:Django-import-export customization with related fieldsDjango-import-export 自定义相关字段
【发布时间】:2018-11-30 15:04:53
【问题描述】:

每次输入“sku”的新值时,我都需要更新我的表(而不是创建新条目),但只有在选择的“客户端”相同时才会发生这种情况。如果“客户端”不同,则模型应添加具有相同“sku”但具有不同“客户端”的新对象。

一位 StackOverflow 用户给了我解决方案:

class ProductList(models.Model):
    id_new = models.IntegerField(primary_key=True)
    sku = models.CharField(primary_key=False, max_length=200)
    client = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    name = models.CharField(max_length=256)
    description = models.CharField(max_length=1000)
    storage = models.CharField(max_length=256)
    cost_price = models.CharField(max_length=256)
    sell_price = models.CharField(max_length=256)
    ncm = models.CharField(max_length=256)
    inventory = models.IntegerField(null=True)

    class Meta:
        unique_together = (('sku', 'client'),)

    def save(self, *args, **kwargs):
        if self.pk:
            current_instance = self.__class__.objects.get(pk=self.pk)
            if current_instance.client != self.client:
                self.pk = None
        return super(ProductList, self).save(*args, **kwargs)

添加保存功能后,问题就解决了。但是,如果我尝试更新现有表,我的文件中的每个字段都会出现以下问题:

Line number: 1 - get() returned more than one ProductList -- it returned 2!
345, Teste 1, Descrição 1, 87654, 59,99, 180, 65, 884, 25

Traceback (most recent call last):
File "/home/checkstore/.local/lib/python3.6/site-packages/import_export/resources.py", line 453, in import_row
instance, new = self.get_or_init_instance(instance_loader, row)
File "/home/checkstore/.local/lib/python3.6/site-packages/import_export/resources.py", line 267, in get_or_init_instance
instance = self.get_instance(instance_loader, row)
File "/home/checkstore/.local/lib/python3.6/site-packages/import_export/resources.py", line 261, in get_instance
return instance_loader.get_instance(row)
File "/home/checkstore/.local/lib/python3.6/site-packages/import_export/instance_loaders.py", line 33, in get_instance
return self.get_queryset().get(**params)
File "/home/checkstore/.local/lib/python3.6/site-packages/django/db/models/query.py", line 403, in get
(self.model._meta.object_name, num)
Clientes.models.ProductList.MultipleObjectsReturned: get() returned more than one ProductList -- it returned 2!

我的 admin.py 文件如下所示:

from django.contrib import admin
from .forms import FaturaForm, ConfirmImportForm
from .models import (Token,
                     Sell,
                     LogisticCost,
                     IncomeCost,
                     FinalPayment,
                     CustomerServiceCost,
                     Fatura,
                     ProductList)
from import_export.admin import ImportExportModelAdmin, ImportMixin
from .resources import ProductListResource
from django.contrib.auth.models import User


try:
    from django.utils.encoding import force_text
except ImportError:
    from django.utils.encoding import force_unicode as force_text
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.template.response import TemplateResponse
from django.utils.translation import ugettext_lazy as _


class ProductAdminImport(ImportExportModelAdmin, ImportMixin):
    resource_class = ProductListResource
    list_display = ('sku', 'client', 'name', 'description', 'storage', 'cost_price', 'sell_price', 'ncm', 'inventory')
    list_filter = ['client']

    def process_import(self, request, *args, **kwargs):
        """
        Perform the actual import action (after the user has confirmed the import)
        """
        if not self.has_import_permission(request):
            raise PermissionDenied

        confirm_form = ConfirmImportForm(request.POST)
        if confirm_form.is_valid():
            import_formats = self.get_import_formats()
            input_format = import_formats[
                int(confirm_form.cleaned_data['input_format'])
            ]()
            tmp_storage = self.get_tmp_storage_class()(name=confirm_form.cleaned_data['import_file_name'])
            data = tmp_storage.read(input_format.get_read_mode())
            if not input_format.is_binary() and self.from_encoding:
                data = force_text(data, self.from_encoding)
            dataset = input_format.create_dataset(data)
            for i in User.objects.all():
                if i.username == str(request.POST['original_file_name'].split('.')[0]):
                    dataset.append_col([i.id] * dataset.height, header='client')

            result = self.process_dataset(dataset, confirm_form, request, *args, **kwargs)

            tmp_storage.remove()

            return self.process_result(result, request)


    def import_action(self, request, *args, **kwargs):
        '''
        Perform a dry_run of the import to make sure the import will not
        result in errors.  If there where no error, save the user
        uploaded file to a local temp file that will be used by
        'process_import' for the actual import.
        '''
        if not self.has_import_permission(request):
            raise PermissionDenied

        resource = self.get_import_resource_class()(**self.get_import_resource_kwargs(request, *args, **kwargs))

        context = self.get_import_context_data()

        import_formats = self.get_import_formats()
        form_type = self.get_import_form()
        form = form_type(import_formats,
                         request.POST or None,
                         request.FILES or None)

        if request.POST and form.is_valid():
            input_format = import_formats[
                int(form.cleaned_data['input_format'])
            ]()
            import_file = form.cleaned_data['import_file']
            # first always write the uploaded file to disk as it may be a
            # memory file or else based on settings upload handlers
            tmp_storage = self.write_to_tmp_storage(import_file, input_format)

            # then read the file, using the proper format-specific mode
            # warning, big files may exceed memory
            try:
                data = tmp_storage.read(input_format.get_read_mode())
                if not input_format.is_binary() and self.from_encoding:
                    data = force_text(data, self.from_encoding)
                dataset = input_format.create_dataset(data)
            except UnicodeDecodeError as e:
                return HttpResponse(_(u"<h1>Imported file has a wrong encoding: %s</h1>" % e))
            except Exception as e:
                return HttpResponse(
                    _(u"<h1>%s encountered while trying to read file: %s</h1>" % (type(e).__name__, import_file.name)))
            # if str(request.user.username) == str(import_file.name.split('.')[0]):
            for i in User.objects.all():
                csv_client_name = str(import_file.name.split('.')[0])
                if i.username == csv_client_name:
                    dataset.append_col([i.id] * dataset.height, header='client')
            result = resource.import_data(dataset, dry_run=True,
                                          raise_errors=False,
                                          file_name=import_file.name,
                                          user=request.user)

            context['result'] = result

            if not result.has_errors():
                context['confirm_form'] = ConfirmImportForm(initial={
                    'import_file_name': tmp_storage.name,
                    'original_file_name': import_file.name,
                    'input_format': form.cleaned_data['input_format'],
                })

        context.update(self.admin_site.each_context(request))

        context['title'] = _("Import")
        context['form'] = form
        context['opts'] = self.model._meta
        context['fields'] = [f.column_name for f in resource.get_user_visible_fields()]

        request.current_app = self.admin_site.name
        return TemplateResponse(request, [self.import_template_name],
                                context)


class FaturaModel(admin.ModelAdmin):
    form = FaturaForm
    list_filter = ['cliente', 'pago']
    list_display = ('cliente',
                    'id',
                    'dia',
                    'numero_da_fatura',
                    'mes',
                    'ano',
                    'valor',
                    'pago')


class SellsAdmin(admin.ModelAdmin):
    list_filter = ['client']
    list_display = ['client', 'date']


admin.site.register(Token)
admin.site.register(Sell, SellsAdmin)
admin.site.register(LogisticCost)
admin.site.register(IncomeCost)
admin.site.register(FinalPayment)
admin.site.register(CustomerServiceCost)
admin.site.register(Fatura, FaturaModel)
admin.site.register(ProductList, ProductAdminImport)

【问题讨论】:

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


    【解决方案1】:

    据我所知,您需要更新您的ProductListResource。你可以这样尝试:

    class ProductListResource(resources.ModelResource):
        class Meta:
            model = ProductList
            import_id_fields = ('sku','client',)  # <-- Add multiple id fields
            fields = (sku', 'client', 'name', 'description', 'storage',...) # other fields
    

    顺便说一句我认为您不需要重写 save 方法来生成重复项。如果将skuclient 用作多个标识,django-import-export 将始终在为一个sku 看到不同的client 时创建新实例。

    【讨论】:

    • 太棒了,它就像一个魅力!很抱歉没有回答这个解决方案。非常感谢您花时间解决这个问题。
    【解决方案2】:

    save() 函数中。

    if current_instance.client != self.client:
        self.pk = None
    

    改成

    if current_instance.client != self.client:
        self.id_new = None
    

    如果self.pk 为您提供self.id_new 的值,则分配给self.pk 的值不适用于self.id_new。没错,实例的值是立即改变的,但是当你调用save()函数时,该值并不适用于数据库。

    所以我认为它会导致意外的重复值。

    【讨论】:

    • 我已经尝试过了,但问题仍然存在。
    • 哦,那么,在调试模式下运行你的代码,然后追逐你的代码。必须知道是什么导致重复数据。并检查get 方法。
    • 是的,我通过这种方式找到了解决方案。我会在星期一把它贴在这里!
    【解决方案3】:

    我找到的解决方案如下:

    在包文件instance_loaders.py我修改了函数:

    def get_queryset(self):
        return return self.resource._meta.model.objects.all()
    

    收件人:

    def get_queryset(self):
        return self.resource._meta.model.objects.filter(client=self.dataset._data[0]._row[-1]).all()
    

    这解决了我所有的问题。

    【讨论】:

    • 这不是一个好的解决方案。假设您已升级版本或有人在生产中对其进行了更新,应用程序将崩溃。如果您在代码中而不是在包中编写某种覆盖,那就更好了
    • 是的,我是这么认为的。你知道我怎么能覆盖它吗?如果它被更新,我所做的就是上传一个带有我对 pypi 的修改的新包,所以我可以使用它而不是原始包。
    • 我不知道你是怎么做到的。如果我可以管理业余时间,我会尝试深入研究(虽然不能保证,所以不要依赖它)。同时你能试试我的解决方案吗?
    • 谢谢你,伙计!我刚刚尝试了您的解决方案,效果很好。
    • 供将来参考:您可以继承 ForeignKeyWidget 并为其提供自己的 get_queryset 方法。因此,在您的情况下,您可以创建一个名为ClientWidget 的子类,然后在ProductListResource 中添加一行:client = fields.Field(attribute='client', column_name='client', widget=ClientWidget() 注意我没有对此进行测试,您应该参考ForeignKeyWidget 的源代码.
    猜你喜欢
    • 2021-03-07
    • 1970-01-01
    • 2023-02-08
    • 1970-01-01
    • 2015-05-08
    • 2022-01-18
    • 2013-11-13
    • 2013-01-29
    • 2021-06-11
    相关资源
    最近更新 更多