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

Incorrect content_type in LogEntry if m2m_changed signal is emitted with reverse=True. #632

Open
gghildyal opened this issue Apr 18, 2024 · 0 comments

Comments

@gghildyal
Copy link

gghildyal commented Apr 18, 2024

Normally m2m_changed signal is emitted with reverse = False where the forward relationship is updated. When the m2m changes are done on the reverse side of the relationship, the log entry changes aren't reported correctly, as in there's no way to logically follow the change.

E.g, say we have two classes

class Child(model.Model):
      pass
 
class Parent(models.Model):
      children = ManyToManyField(Child, related_name='parents')

If the update is done forwards:

     parent = Parent()
     parent.save()
     child = Child()
     child.save()
     parent.children.set([child])

then the log entry m2m change has a content_type of Parent, object_pk has the unique id of the parent that was changed, and changes has children changed in the "object"

 {"children": {"operation": "update", "type": "m2m", "objects": ["Child 0"]}}

Makes perfect sense. From this audit log, you can exactly work out what was changed. E.g.

    parent = log_entry.content_type.model_class.get(object_pk=log_entry.object_pk)
    field, field_value = next(iter(log_entry.changes))
    children = getattr(parent, field).all()
    # The children list will match that of the objects field in log_entry.changes

But if the update is done in a reverse manner

child = Child()
parent = Parent()
parent.save()
child.parents.set([parent])

then the log_entry generated isn't correct, content_type has Child, object_pk has the unique id of the child and the changes are

 {"children": {"operation": "update", "type": "m2m", "objects": ["Parent 0"]}}

Now if you try to do the same thing as above, you get an attribute error

    child = log_entry.content_type.model_class.get(object_pk=log_entry.object_pk)
    field, field_value = next(iter(log_entry.changes))
     parents = getattr(child, field).all()  # Attribute error, no attribute 'children' in Child.
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

1 participant