Я использую django-simple-history. Я сохраняю историческую таблицу продукта и цены, цена связана с продуктом, и в админ-панель добавляется встроенная линия. Хочу отображать в админке в истории товаров записи историй связанных моделей (цен). Как я могу это сделать? И чтобы измененные поля отображались моя модель
class Product(models.Model):
article = models.PositiveIntegerField()
history = HistoricalRecords()
class Price(models.Model):
prod = models.OneToOneField(
Product,)
price_supplier = models.FloatField()
history = HistoricalRecords()
мой администратор
class PriceInline(admin.TabularInline):
model = Price
class ProductAdmin(SimpleHistoryAdmin):
inlines = [
PriceInline,]
admin.site.register(Product, ProductAdmin)
введите сюда описание изображениявведите сюда описание изображения
Попробовал настроить через History_view() и get_history_queryset() на получение объектов другой модели и я их получил, но не понимаю как их встроить в рендер, чтобы и менялась модель товара, и менялась цена модель будет отражена, и в то же время изменившиеся поля будут соответствовать их моделям. или есть другой способ добиться этого результата
это необходимость проекта и перенос невозможен. также есть связанные таблицы со стоковыми изображениями документов и характеристиками с такой же привязкой к товарам и их тоже нужно отображать в истории
Я нашел решение, это что-то вроде ляпа. Поскольку мы не будем отображать цену приложения отдельно в панели администратора (она участвует только во встроенной части продукта), мы можем изменить функцию History_view() с django-simple-history. Я добавлю в пример модель изображения, чтобы показать, как использовать этот подход как с полями «один к одному», так и с полями «один ко многим». При этом я отключил возможность возвращать старое значение из истории и для шаблона изменил то, что отображалось в первом столбце как ссылка, потому что ссылки нужно настраивать отдельно. Теперь в первом столбце у меня есть только названия типов полей.
Теперь мой код выглядит так:
class Product(models.Model):
article = models.PositiveIntegerField()
history = HistoricalRecords()
class Price(models.Model):
prod = models.OneToOneField(Product,)
price_supplier = models.FloatField()
history = HistoricalRecords()
class ProductImage(models.Model):
product = models.ForeignKey(Product,)
photo = models.ImageField("Изображение", upload_to=get_file_path_add)
history = HistoricalRecords()
class PriceInline(admin.TabularInline):
model = Price
class ProductImageInline(admin.TabularInline):
model = ProductImage
class PriceAdmin(SimpleHistoryAdmin):
model = Price
def history_view(self, request, object_id, extra_context=None):
"""The 'history' admin view for this model."""
model = self.model
opts = model._meta
pk_name = opts.pk.attname
history = getattr(model, model._meta.simple_history_manager_attribute)
historical_records = PriceAdmin.get_history_queryset( PriceAdmin,
request, history, pk_name, object_id )
history_list_display =
PriceAdmin.get_history_list_display(PriceAdmin,request)
# Set attribute on each historical record from admin methods
for history_list_entry in history_list_display:
value_for_entry = getattr(self, history_list_entry, None)
if value_for_entry and callable(value_for_entry):
for record in historical_records:
setattr(record, history_list_entry,
value_for_entry(record))
PriceAdmin.set_history_delta_changes(PriceAdmin, request,
historical_records)
return historical_records
def set_history_delta_changes(
self,
request,
historical_records,
foreign_keys_are_objs=True,
):
previous = None
for current in historical_records:
if previous is None:
previous = current
continue
# Related objects should have been prefetched in `get_history_queryset()`
delta = previous.diff_against(
current, foreign_keys_are_objs=foreign_keys_are_objs
)
helper = PriceAdmin.get_historical_record_context_helper(
PriceAdmin, request, previous
)
previous.history_delta_changes = helper.context_for_delta_changes(delta)
previous = current
class ProductImageAdmin(SimpleHistoryAdmin):
model = ProductImage
def history_view(self, request, object_id, extra_context=None):
"""The 'history' admin view for this model."""
model = self.model
opts = model._meta
pk_name = opts.pk.attname
history = getattr(model, model._meta.simple_history_manager_attribute)
historical_records = ProductImageAdmin.get_history_queryset(
ProductImageAdmin, request, history, pk_name, object_id
)
history_list_display = ProductImageAdmin.get_history_list_display(
ProductImageAdmin, request
)
# Set attribute on each historical record from admin methods
for history_list_entry in history_list_display:
value_for_entry = getattr(self, history_list_entry, None)
if value_for_entry and callable(value_for_entry):
for record in historical_records:
setattr(record, history_list_entry, value_for_entry(record))
ProductImageAdmin.set_history_delta_changes(
ProductImageAdmin, request, historical_records
)
return historical_records
def set_history_delta_changes(
self,
request,
historical_records,
foreign_keys_are_objs=True,
):
previous = None
for current in historical_records:
if previous is None:
previous = current
continue
# Related objects should have been prefetched in `get_history_queryset()`
delta = previous.diff_against(
current, foreign_keys_are_objs=foreign_keys_are_objs
)
helper = ProductImageAdmin.get_historical_record_context_helper(
ProductImageAdmin, request, previous
)
previous.history_delta_changes = helper.context_for_delta_changes(delta)
previous = current
class ProductAdmin(SimpleHistoryAdmin):
inlines = [
PriceInline,
ProductImageInline,
]
def history_view(self, request, object_id, extra_context=None):
"""The 'history' admin view for this model."""
request.current_app = self.admin_site.name
model = self.model
opts = model._meta
app_label = opts.app_label
pk_name = opts.pk.attname
history = getattr(model, model._meta.simple_history_manager_attribute)
object_id = unquote(object_id)
price_id = Price.objects.get(prod=object_id)
image_id = ProductImage.objects.filter(product=object_id)
historical_records = self.get_history_queryset(
request, history, pk_name, object_id
)
#**here we get historical_records in image and price**
historical_records_image = []
for item in image_id:
item_list = ProductImageAdmin.history_view(
ProductImageAdmin, request, item.id, extra_context=None
)
if historical_records_image == []:
historical_records_image = item_list
else:
historical_records_image = list(
chain(
historical_records_image,
item_list,
)
)
historical_records_price = PriceAdmin.history_view(
PriceAdmin, request, price_id.id, extra_context=None
)
history_list_display = self.get_history_list_display(request)
# If no history was found, see whether this object even exists.
try:
obj = self.get_queryset(request).get(**{pk_name: object_id})
except model.DoesNotExist:
try:
obj = historical_records.latest("history_date").instance
except historical_records.model.DoesNotExist:
raise http.Http404
if not self.has_view_history_or_change_history_permission(request, obj):
raise PermissionDenied
# Set attribute on each historical record from admin methods
for history_list_entry in history_list_display:
value_for_entry = getattr(self, history_list_entry, None)
if value_for_entry and callable(value_for_entry):
for record in historical_records:
setattr(record, history_list_entry, value_for_entry(record))
self.set_history_delta_changes(request, historical_records)
# HERE WE COLLECT A GENERAL LIST OF ALL RECORDS
result_list = list(
chain(
historical_records,
historical_records_price,
historical_records_image,
)
)
# HERE WE SORT THEM ALL BY TIME
def get_date(element):
return element.history_date
result_list_sorted = result_list.sort(key=get_date, reverse=True)
content_type = self.content_type_model_cls.objects.get_for_model(
get_user_model()
)
admin_user_view = "admin:{}_{}_change".format(
content_type.app_label,
content_type.model,
)
context = {
"title": self.history_view_title(request, obj),
"object_history_list_template": self.object_history_list_template,
"historical_records": result_list,
"module_name": capfirst(force_str(opts.verbose_name_plural)),
"object": obj,
"root_path": getattr(self.admin_site, "root_path", None),
"app_label": app_label,
"opts": opts,
"admin_user_view": admin_user_view,
"history_list_display": history_list_display,
"revert_disabled": self.revert_disabled(request, obj),
}
context.update(self.admin_site.each_context(request))
context.update(extra_context or {})
extra_kwargs = {}
return self.render_history_view(
request, self.object_history_template, context, **extra_kwargs
)
admin.site.register(Product, ProductAdmin)
Почему бы не перенести цену в модель
Product
?