Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's the use of HistoricForeignKey ? I can't see any foreign key relationship between automatically created related tables inside the database #1380

Open
xtrm-co-in opened this issue Aug 8, 2024 · 3 comments

Comments

@xtrm-co-in
Copy link

xtrm-co-in commented Aug 8, 2024

I was hoping that introduction of HistoricForeignKey may establish a foreignkey relationship between the historic tables of both the related tables inside the database, so that, we can easily retrieve child records while querying for the parent's history or vice versa.

For example, If I have a LineItem model having a foreign key field pointing to Invoice model, If I change either LineItem or Invoice, I will have new records for both Invoice and LineItem in their respective history tables with LineItem's historic table having history_id of Invoice's historic table so that we can easily retrieve historical data of either of them for a given point in time.

But what I found is instead of having history_id of Invoice's historical data, LineItem's historic table is actually saving original Invoice's id, which doesn't help to retrieve corresponding Invoice data in history.

Hope I made my point clear, It may have something to do with lack of documentation / example on HistoricForeignkey field. If someone could understand my point, Will you please help me achieve the same with an example preferably in context of django rest framework ?

@wchen38
Copy link

wchen38 commented Sep 9, 2024

Have you looked over this test case?

class HistoricForeignKeyTest(TestCase):

@xtrm-co-in
Copy link
Author

Will you please clear the air? I didn't get your point.

@wchen38
Copy link

wchen38 commented Sep 22, 2024

from my understanding, django-simple-history library gets the historical records using timestamps, this is why the history table doesn't contain the actual history foreign keys but the original foreign keys.

I created an example to try to demonstrate what I mean. Maybe try running the test code below with your own demo DRF app. In the first test, I try to demonstrate getting historical data with explicit timestamps. In the second test, I try to use the history_date field in the history table to demonstrate that you first find out which line item record you want to look at, then you can use its history_date to fetch for the corresponding invoice at that time, this should also work vice versa. Hope it helps.

# models.py
from django.db import models
from simple_history.models import HistoricalRecords, HistoricForeignKey

class Invoice(models.Model):
    invoice_number = models.CharField(max_length=100)
    address = models.TextField(null=True, blank=True)
    date_created = models.DateField(auto_now_add=True)
    history = HistoricalRecords()

    def __str__(self):
        return self.invoice_number


class LineItem(models.Model):
    invoice = HistoricForeignKey(Invoice, related_name='line_items', on_delete=models.CASCADE)
    description = models.CharField(max_length=255)
    history = HistoricalRecords()

    def __str__(self):
        return f'{self.description} - {self.invoice.invoice_number}'
tests.py
from django.test import TestCase
from .models import LineItem, Invoice
from django.utils import timezone

# Create your tests here.

class TestHistoricalForeignKey(TestCase):
    def test_get_history_data_using_timezone_now(self):
        invoice_0 = Invoice.objects.create(invoice_number="0000", address="0000 street")
        item_0 = LineItem.objects.create(invoice=invoice_0, description="item0")
        # as of t0, the invoice and line item are created
        t0 = timezone.now()

        invoice_0.address = "0001 street"
        invoice_0.save()
        # as of t1, we updated the invoice address
        t1 = timezone.now()

        # getting the history line item as of t0
        t0_item_0 = item_0.history.as_of(t0)

        # at t0, the corresponding address should '0000 street'
        self.assertEqual(t0_item_0.invoice.address, "0000 street")

        # getting the history line item as of t1
        t1_item_0 = item_0.history.as_of(t1)
        # at t1, the corresponding address should be '0001 street'
        self.assertEqual(t1_item_0.invoice.address, "0001 street")

    def test_get_history_data_using_history_date(self):
        invoice_0 = Invoice.objects.create(invoice_number="0000", address="0000 street")
        item_0 = LineItem.objects.create(invoice=invoice_0, description="item0")

        invoice_0.address = "0001 street"
        invoice_0.save()

        # get the only record in the history table for LineItem.
        item_history_record = item_0.history.all()[0]

        # get the corresponding invoice using the line item history date. This should get the invoice record at the time when the item_history_record is created.
        invoice_record = invoice_0.history.as_of(item_history_record.history_date)

        self.assertEqual(invoice_record.address, "0000 street")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants