I have this unique field
on my Profile model
:
email = models.EmailField(max_length=200, unique=True)
and I have this ProfileResource
:
class ProfileResource(resources.ModelResource):
# associated_issuer = fields.Field(attribute='add_associated_issuer', widget=ManyToManyWidget(Issuer, field='name'), column_name='Associated Issuer')
email = fields.Field(attribute='email',
widget=CharWidget(),
column_name='email')
class Meta:
model = Profile
clean_model_instances = True
import_id_fields = ('id', 'email')
def before_import_row(self, row, row_number=None, **kwargs):
try:
self.email = row["email"]
except Exception as e:
self.email = ""
try:
if row["id"] == '':
row["id"] = None
self.id = row["id"]
except Exception as e:
self.id = None
try:
self.firstname = row["firstname"]
except Exception as e:
self.firstname = ""
try:
self.lastname = row["lastname"]
except Exception as e:
self.lastname = ""
Now when I try to do an import from a file, I receive this error:
I need to be able to do an update_or_create
method, but where do I add that code? I tried doing it on an after_import_instance
but it did not work.
I also tried on import_row
like this:
def import_row(self, row, instance_loader, using_transactions=True, dry_run=False, **kwargs):
try:
Profile.objects.update_or_create(email=self.email)
except Exception as e:
print(e, file=sys.stderr)
but it produced this error:
Exception Value: 'NoneType' object has no attribute 'import_type'
Any insights on a fix would be appreciated.
CodePudding user response:
#try this
email = models.EmailField(max_length=200, unique=True, null=True)
try:
self.email = row["email"]
except Exception as e:
self.email = None
unique=True
also block duplicated "", but null is not blocked.
CodePudding user response:
Notice that you have this line on your ProfileResource
import_id_fields = ('id', 'email')
This means that the import will look at each row in your import file, and attempt to load any Profile
instances which match both id
and email
.
Therefore if you only need to update email
, then simply remove this from the list of import_id_fields
. By doing that, the logic will uniquely identify instances based solely on id
, and then new records will be created if they don't exist, and any existing records will be updated (which will include updating the email address).
This is how import-export
is supposed to work and no further logic is required.
If email
is the "unique field" which matches your existing instances, then this should be the only field in import_id_fields
. There cannot be two Profile
instances with the same email, because unique=True
, therefore you can use email
as the import_id_field
. However it will not be possible to update the Profile
email using import-export
(although you could introduce a field called new_email
and have logic to update the Profile
that way, but that is a separate topic).
I don't know why you are seeing the 'email already exists' error - possibly something else is going on, and it could be the logic in before_import_row()
which is causing the problem, probably due to the fact that a row is being created with empty string as the email.
btw - you shouldn't need any of the logic in before_import_row()
- import-export
should handle this for you, and by overriding you might be introducing further bugs.
The best advice for using import-export
is to set breakpoints in your IDE, and then step through during an import and you can see exactly what is happening. This will save you a lot of time in the long run.
In summary, use either id
or email
in import_id_fields
and it should work.