具有相关字段的Django导入-导出自定义

时间:2018-11-30 15:04:53

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

每次输入新值“ 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)

3 个答案:

答案 0 :(得分: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 看到一个{{1}的不同client时,将始终创建新实例。 }。

答案 1 :(得分:0)

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()函数时,该值不适用于数据库。

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

答案 2 :(得分:0)

我找到的解决方案如下:

在软件包文件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()

那解决了我所有的问题。