question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Watching for ForeignKey value changes seems to not trigger the hook

See original GitHub issue

Using the below example in version 0.6.0, I am expecting to print a message any time someone changes a user’s first name that is saved in SomeModel. From what I can tell from the documentation, I am setting everything up correctly. Am I misunderstanding how this works?

Assuming a model set up like this:

from django.conf import settings
from django.db import models
from django_lifecycle import LifecycleModel, hook

class SomeModel(LifecycleModel):
    user = models.ForeignKey(
        on_delete=models.CASCADE,
        to=settings.AUTH_USER_MODEL
    )
    # More fields here...

    @hook('after_update', when='user.first_name', has_changed=True)
    def user_first_name_changed(self):
        print(
            f"User's first_name has changed from "
            f"{self.initial_value('user.first_name')} to {user.first_name}!"
        )

When we then perform the following code, nothing prints:

from django.contrib.auth import get_user_model

# Create a test user (Jane Doe)
get_user_model().objects.create_user(
    username='test', 
    password=None, 
    first_name='Jane', 
    last_name='Doe'
)

# Create an instance
SomeModel.objects.create(user=user)

# Retrieve a new instance of the user
user = get_user_model().objects.get(username='test')

# Change the name (John Doe)
user.first_name = 'John'
user.save()

# Nothing prints from the hook

In the tests, I see that it is calling user_account._clear_watched_fk_model_cache() explicitly after changing the Organization.name. However, from looking at the code, I do not see this call anywhere except for the overridden UserAccount.save() method. Thus, saving the Organization has no way to notify the UserAccount that a change has been made, and therefore, the hook cannot possibly be fired. The only reason that I can see that the test is passing is because of the explicit call to user_account._clear_watched_fk_model_cache().

    def test_has_changed_is_true_if_fk_related_model_field_has_changed(self):
        org = Organization.objects.create(name="Dunder Mifflin")
        UserAccount.objects.create(**self.stub_data, organization=org)
        user_account = UserAccount.objects.get()

        org.name = "Dwight's Paper Empire"
        org.save()
        user_account._clear_watched_fk_model_cache()
        self.assertTrue(user_account.has_changed("organization.name"))

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
rsinger86commented, Dec 15, 2019

I think I know what’s happening. You want a model’s hook to fire, upon making updates to its FK-related model. This isn’t supported. The hook will only fire when you make the change to the model itself: the SomeModel instance in your example.

from django.contrib.auth import get_user_model

# Create a test user (Jane Doe)
get_user_model().objects.create_user(
    username='test', 
    password=None, 
    first_name='Jane', 
    last_name='Doe'
)

# Create an instance
some_instance = SomeModel.objects.create(user=user)

# Retrieve a new instance of the user
user = get_user_model().objects.get(username='test')

# Change the name (John Doe)
user.first_name = 'John'
user.save()

# The hook will fire and it will print the message
some_instance.save()

Maybe the docs should be updated to clarify? I’ll think a little more about supporting this behavior down the line. Though, to be honest, I’m not quite sure if the “FK changes” feature should be included in the first place because of lack of support for handling it efficiently, ORM-wise.

0reactions
rsinger86commented, Feb 2, 2020

Apologies for the radio silence - I appreciate the thought here. I just don’t think I want to expand the scope of this project to support a feature of this complexity, given that it seems to be a pretty rare use case. Furthermore, handling chains of changes like this with application code in-memory seems a little shaky… I think it would be better solved with database triggers/functions.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Watching ForeignKey Changes - Django Lifecycle Hooks
To be clear: This hook will fire when the value in the database column that stores the foreign key (in this case, organization_id...
Read more >
Actions triggered by field change in Django
I suspect that a model instance method is needed, but the docs don't seem to have much to say about using them in...
Read more >
3 common foreign key mistakes (and how to avoid them)
Foreign key constraints are important to any SQL database, but they can also cause problems if they're not implemented correctly.
Read more >
SQL: Constraints and Triggers
1) Foreign Key must be a reference to a valid value in the referenced table. ... tuple in the referenced table that changes...
Read more >
CS206 --- Electronic Commerce
Triggers. Source: slides by Jeffrey Ullman. 2. Constraints and Triggers ... An insert or update to R introduces values not found in S....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found