Django-import-export customization with related fields

陌路散爱 提交于 2019-12-11 04:14:26

问题


I need to update my table every time a new value of "sku" is entered (not to create a new entry), but it does have to happen only if the "client" selected is the same. If the "client" is different, then the model should add a new object with the same "sku", but with different "clients".

One StackOverflow user gave me the solution:

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)

After I added the save function the problem got solved. However, if I try to update an existing table I get the following problem for each field in my file:

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!

My admin.py file is presented below:

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)

回答1:


As far as I can see, you need to update your ProductListResource. You can try like this:

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 I don't think you need to override the save method for generating duplicate items. If sku and client are used as multiple identification, django-import-export will always create new instances when it sees different client for one sku.




回答2:


in save() function.

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

Change to

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

If self.pk gives you a value of self.id_new, the value assigned to self.pk does not apply to self.id_new. Exactly, the value of the instance changes immediately, but when you call the save() function, the value does not apply to the database.

So I think it causes unexpected duplicated values.




回答3:


The solution that I found is the following:

In the package file instance_loaders.py I modified the function:

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

To:

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

And that solved all my problems.



来源:https://stackoverflow.com/questions/53560023/django-import-export-customization-with-related-fields

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!