Изменение экземпляра до его сохранения при импорте
Я настраиваю процесс импорта для модели, которая имеет отношение ForeignKey, и у меня есть дополнительное поле в модели, в котором я хотел бы хранить предоставленное значение, если это недопустимый ID связанной таблицы.
В данном случае речь идет об идентификаторах UUID, поэтому велика вероятность того, что кто-то из участников предоставит некорректные данные.
class Result(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False
)
passport = models.ForeignKey(
to='results.Passport',
null=True,
blank=True,
on_delete=models.SET_NULL
)
invalid_passport = models.CharField(
max_length=255,
blank=True,
null=True,
help_text=_("An invalid passport number")
)
Файл импорта будет содержать колонку passport для UUID связанного объекта, но я хотел бы сохранить предоставленное значение в invalid_passport, если оно не соответствует ни одному Passport экземпляру.
Мой ресурс выглядит следующим образом;
class ResultResource(resources.ModelResource):
""" Integrate django-import-export with the Result model """
passport = fields.Field(
column_name='passport',
attribute='passport',
widget=ForeignKeyWidget(Passport, 'id')
)
class Meta:
instance_loader_class = CachedInstanceLoader
model = Result
fields = (
'id',
'passport',
'invalid_passport'
)
def init_instance(self, row=None):
""" Initializes a new Django model. """
instance = self._meta.model()
if not instance.passport:
instance.invalid_passport = row['passport']
return instance
def before_save_instance(self, instance, using_transactions, dry_run):
""" Sets the verified flags where there is a passport match. """
if instance.passport:
instance.verified = True
instance.verified_by = self.import_job.author
instance.invalid_passport = None
else:
pass
Поэтому при создании экземпляра значение идентификатора Passport сохраняется в invalid_passport. Моя теория заключается в том, что в before_save_instance я могу определить, существует ли связанный Passport, за исключением того, что обе стороны if выполняются в before_save_instance.
Является ли это неправильным подходом для этого?
ok, я думаю, что у меня есть решение, возможно, оно не идеально, но я расскажу вам, что является подходящим инструментом, который очень помог мне в таких задачах. Итак, я думаю, что signals является лучшим способом для успешного выполнения этой задачи
Итак, я расскажу вам о шагах, которые я получил в псевдокоде, и вы можете изменить их в соответствии с вашей идеей:
- вы будете использовать pre_save для получения данных, которые были размещены в запросе
- в функции, к которой вы будете применять сигналы, откройте ваш CSV файл .
- возьмите UUID и найдите его в CSV-файле
- проведите сравнение между данными, полученными из CSV-файла, и UUID .
- если данные не соответствуют UUID или любому другому полю, с которым вы хотите сравнить, сохраните этот UUID в invalid_passport .
- в противном случае, не сохраняйте его и верните ваш ответ как успешный вид
подробнее о pre_save и сигналах вы можете прочитать здесь: https://docs.djangoproject.com/en/4.0/ref/signals/
подсказка:
самым быстрым способом открыть файл и получить данные из файла будет использование dataframe в pandas, если вы знакомы с pandas, вы сможете решить эту задачу быстрее, чем использование открытого файла в Django, а также использовать высокую производительность в поиске, потому что в pandas вам не нужно будет использовать цикл для поиска большого количества данных, в отличие от обычного цикла в python
Одна проблема заключается в том, что если паспорт не существует, то ForeignKeyWidget вызовет исключение DoesNotExist и ваша строка не будет импортирована и будет помечена как ошибка. Однако, это может быть нормально для вас, потому что вас может интересовать только запись строк без ошибок.
Так что, если я чего-то не понимаю, я не могу понять, как вызывается before_save_instance(), потому что сначала вызывается исключение DoesNotExist, что означает, что ни одна из логик сохранения не вызывается.
Если вы посмотрите на эту строку, то увидите, что исключение добавляется в объект Error, а затем сохраняется в результатах для импорта. Это кажется хорошим вариантом для записи недействительных идентификаторов паспортов. На самом деле, существующий объект Error будет записывать исключение (DoesNotExist) и строку, так что у вас уже будет запись неудачных строк без какой-либо настройки. Вы можете переопределить объект Result или Error, чтобы иметь больше контроля над этим.
Другой идеей может быть переопределение before_import() для выполнения некоторой предварительной обработки. В before_import() вы можете загрузить все существующие uuids паспорта в память, а затем просканировать набор данных, чтобы удалить все несуществующие строки. Это может быть непрактично, если у вас большие таблицы.
Однако вы говорите, что хотите записать отсутствующий UUID в модель, поэтому вы можете ввести пользовательский подкласс ForeignKeyWidget, который не будет вызывать исключение DoesNotExist, а вместо этого может вызывать ValidationError. В этом случае ошибка будет храниться в import_validation_errors. Затем вы можете переопределить validate_instance(), чтобы прочитать ошибку обратно и сохранить ее в модели.