I use django-simple-history.I keep the historical table of the product and the price, the price is associated with the product and an inlane is added to the admin panelю I want to display in the admin panel in the product history records of the stories of related models (price). How can I do this? And that the fields changed would be displayed my model
class Product(models.Model):
article = models.PositiveIntegerField()
history = HistoricalRecords()
class Price(models.Model):
prod = models.OneToOneField(
Product,)
price_supplier = models.FloatField()
history = HistoricalRecords()
my admin
class PriceInline(admin.TabularInline):
model = Price
class ProductAdmin(SimpleHistoryAdmin):
inlines = [
PriceInline,]
admin.site.register(Product, ProductAdmin)
enter image description here enter image description here
I tried to set it up via history_view() and get_history_queryset() to receive objects of another model and I received them, but I do not understand how to embed them in the render so that both changes in the product model and changes in the price model would be reflected, and at the same time the fields that changed were corresponding to their models. or is there another method to achieve this result
I found a solution, it's a bit of a kludge. Since we will not display the app price separately in the admin panel (it only participates in the inline of the product), we can change the history_view() function from django-simple-history. I will add an image model to the example to show how to use this approach with both one-to-one and one-to-many fields. At the same time, I disabled the ability to return the old value from history and for the template I changed what I displayed in the first column as a link because the links need to be configured separately. Now in the first column I just have the names of the field type.
Now my code looks like this:
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)