Работа в django с данными с сохраненной историей изменений

Я создаю информационную систему, в которой некоторые сущности имеют отдельные атрибуты или их набор, для которых необходимо хранить историю изменений с привязкой к дате их актуальности для дальнейшей обработки совместно с другими сущностями. В частности, у меня есть основная сущность Customer и один или связанный с ним набор атрибутов, связанных с датой (например, официальные имена клиентов), которые могут меняться со временем.

Каков наилучший или возможный способ реализации взаимодействия модели с основным объектом, но при этом их история должна быть сохранена?

Вот к чему я пришел на данный момент. Код класса, вероятно, избыточен, но это сделано специально для демонстрации проблемы.

Основной класс:

class Customer(models.Model):
name = models.CharField(max_length=128,
                        verbose_name='Customer system name')
# A key for relation with current value of secondary class
# I have doubts about the correctness of adding this field,
# but so far I have not come up with anything better.
legal_names = models.ForeignKey('CustomerNames', on_delete=models.SET_NULL,
                                null=True,
                                blank=True,
                                verbose_name='Legal customer names')

Вспомогательный (вторичный) класс:

class CustomerNames(models.Model):
# A key for relation with the main model, specifying a list of names
customer = models.ForeignKey(Customer, on_delete=models.CASCADE,
                             related_name='customer_names',
                             verbose_name='Customer')
short_name = models.CharField(max_length=256,
                              verbose_name='Short name')
full_name = models.CharField(max_length=1024,
                             verbose_name='Full name')

# An attribute for determining the current value
# if we do not use a foreign key in the main class 
is_current = models.BooleanField(default=True,
                                 verbose_name='Is current value')
# Dates of relevance of the names
date_begin = models.DateField(verbose_name='Relevance begin date for names')
date_end = models.DateField(verbose_name='Relevance end date for names')

Соответственно, тоже есть проблема с реализацией обработки этой модели в форме и представлениях. Как вариант для внешнего ключа в основной модели inlineformset, но как-то криво это выглядит.

Сталкивался ли кто-нибудь когда-нибудь с подобной проблемой и каково было решение?

В случае, если вы используете Postgres, вы можете использовать джанго пгистория а пакет, который использует триггеры базы данных для сохранения истории всех изменений поля models в модели событий. Он легко адаптируется, вы можете отслеживать только определенные поля или адаптировать модель событий. Использование триггеров базы данных имеет дополнительное преимущество в том, что изменения в таблице всегда отслеживаются, даже если кто-то создает представление, забыв сохранить старые данные в EventModel, или если кто-то изменяет строку непосредственно в базе данных.

https://django-pghistory.readthedocs.io/en/3.7.0/

@pghistory.track()
class Customer(models.Model):
   name = models.CharField(max_length=128,
       verbose_name='Customer system name'
   )

   short_name = models.CharField(max_length=256,
                              verbose_name='Short name')
   full_name = models.CharField(max_length=1024,
                             verbose_name='Full name')

Вы добавляете поля, которые были у вас в модели CustomerNames, в основную модель и добавляете декоратор pghistory.track. Это добавит CustomerEventModel, который по умолчанию отслеживает все изменения для каждого поля отслеживаемой модели. Более того, в нем есть несколько полей метаданных, таких как временные метки, которые, таким образом, позволят вам проверять достоверность данных о клиентах во времени. Вы также можете ограничить отслеживание определенными полями или создать полностью настраиваемую модель событий для клиента (см. pg history docs).

В своих представлениях вы можете извлекать историю моделей с вашими пользователями. Что касается форм, которые вы просто используете для основной модели.

Вернуться на верх