Настройка Django-import-export со связанными полями - PullRequest
0 голосов
/ 30 ноября 2018

Мне нужно обновлять мою таблицу каждый раз, когда вводится новое значение «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 голосов
/ 02 декабря 2018

Насколько я вижу, вам нужно обновить 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

BTW Я не думаю, что вам нужно переопределять метод save для создания дубликатов элементов.Если sku и client используются в качестве множественной идентификации, django-import-export всегда будет создавать новые экземпляры, когда он видит разные client для одного sku.

0 голосов
/ 03 декабря 2018

Я нашел следующее решение:

В файле пакета 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()

И это решило все мои проблемы.

0 голосов
/ 30 ноября 2018

в функции 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(), это значение не применяется к базе данных.

Так что я думаю, что это вызывает неожиданные дублированные значения.

...