问题
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