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

Auditlog blocks cascade deletion of an object #549

Closed
VasanthCL opened this issue Jul 23, 2023 · 9 comments · Fixed by #592
Closed

Auditlog blocks cascade deletion of an object #549

VasanthCL opened this issue Jul 23, 2023 · 9 comments · Fixed by #592
Assignees
Labels

Comments

@VasanthCL
Copy link

VasanthCL commented Jul 23, 2023

django audit log is not allowing me to do cascaded delete of an object, how to make audit log to allow for cascaded deletion, for example i have a Model A and Model B model B is related to Model A as a foreign key, i registered only Model B in audit log, if i delete an instance of Model A it is not allowing me to delete that instance, it is throws matching query does not exist error

@VasanthCL VasanthCL changed the title django audit log is not allowing me to do cascaded delete of an object, how to make audit log to allow for cascaded deletion django audit log is not allowing me to do cascaded delete of an object, how to make audit log to allow for cascaded deletion, for example i have a Model A and Model B model B is related to Model A as a foreign key, i registered only Model B in audit log, if i delete an instance of Model A it is not allowing me to delete that instance, it is throws Model Does not exist error Jul 23, 2023
@VasanthCL VasanthCL changed the title django audit log is not allowing me to do cascaded delete of an object, how to make audit log to allow for cascaded deletion, for example i have a Model A and Model B model B is related to Model A as a foreign key, i registered only Model B in audit log, if i delete an instance of Model A it is not allowing me to delete that instance, it is throws Model Does not exist error django audit log is not allowing me to do cascaded delete of an object, how to make audit log to allow for cascaded deletion, for example i have a Model A and Model B model B is related to Model A as a foreign key, i registered only Model B in audit log, if i delete an instance of Model A it is not allowing me to delete that instance, it is throws matching query does not exist error Jul 23, 2023
@aleh-rymasheuski aleh-rymasheuski changed the title django audit log is not allowing me to do cascaded delete of an object, how to make audit log to allow for cascaded deletion, for example i have a Model A and Model B model B is related to Model A as a foreign key, i registered only Model B in audit log, if i delete an instance of Model A it is not allowing me to delete that instance, it is throws matching query does not exist error Auditlog blocks cascade deletion of an object Jul 24, 2023
@aleh-rymasheuski
Copy link
Contributor

@VasanthCL, could you please provide the minimal code which reproduces the issue?

@VasanthCL
Copy link
Author

VasanthCL commented Jul 31, 2023

class Details(models.Model):
    name = models.CharField(max_length=50, blank=True)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    website = models.TextField()
    images = JSONField()
    description = models.TextField()
    categories = ArrayField(models.CharField(max_length=50, blank=True))
    platforms = ArrayField(models.CharField(max_length=10, blank=True))
    settings_available = ArrayField(models.CharField(max_length=50, blank=True))
    created_date = models.DateTimeField(auto_now_add=True)
    modified_date = models.DateTimeField(auto_now=True)
    web_content = JSONField(blank=True, null=True, default=dict)
    is_premium = models.BooleanField(default=False)
    is_beta = models.BooleanField(default=False)
    external_id = models.TextField(blank=True, null=True)
    external_keys = ArrayField(models.CharField(max_length=50, blank=True), null=True, default=list, blank=True)
    external_src = models.CharField(max_length=50, blank=True, null=True)
class ConfigData(models.Model):
    integration = models.ForeignKey(Details, on_delete=models.CASCADE, null=True)
    data = JSONField()
    account = models.ForeignKey(Account, on_delete=models.CASCADE, null=True)
    created_date = models.DateTimeField(auto_now_add=True)
    modified_date = models.DateTimeField(auto_now=True)
    is_active = models.BooleanField(default=True)
    is_subscribed = models.BooleanField(default=True)
    is_enabled = models.BooleanField(default=False)
    identify_by_key = models.TextField(default="default")

    def __str__(self):  # added by @aleh-rymasheuski based on the stack trace seen below
        return '%s-%s' % (self.integration.name, self.account)

here i have two models, details and config data models , i registered only ConfigData model

the following process i have done for registering ConfigData model in django-auditlog:

  1. created a file called auditlog_tracking_models.py and registered the model like this,
    tracking_models = (
        "integrations.ConfigData",
        {
        "model": "integrations.ConfigData",
        "include_fields": ["integration", "data","account", "is_active", "is_subscribed", "is_enabled"]
        }
    ),
  1. in settings.py file
    AUDITLOG_INCLUDE_TRACKING_MODELS = tracking_models

now, i added a data in Details model and related that data as a foreign key in ConfigData model
if i try to delete the data in Details Model i get an error, if i commented out the AUDITLOG_INCLUDE_TRACKING_MODELS variable in setting.py file it works good,

here is the error what i get,

Traceback (most recent call last):
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/contrib/admin/options.py", line 683, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/utils/decorators.py", line 133, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/views/decorators/cache.py", line 62, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 242, in inner
    return view(request, *args, **kwargs)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/utils/decorators.py", line 46, in _wrapper
    return bound_method(*args, **kwargs)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/utils/decorators.py", line 133, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/contrib/admin/options.py", line 2104, in delete_view
    return self._delete_view(request, object_id, extra_context)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/contrib/admin/options.py", line 2141, in _delete_view
    self.delete_model(request, obj)
  File "/Users/vasanthakumar/projects/CLabsApp/integrations/admin.py", line 60, in delete_model
    super(DetailsAdmin, self).delete_model(request, obj)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1226, in delete_model
    obj.delete()
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/db/models/base.py", line 1094, in delete
    return collector.delete()
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/db/models/deletion.py", line 488, in delete
    signals.post_delete.send(
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 176, in send
    return [
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 177, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/auditlog/receivers.py", line 23, in wrapper
    signal_handler(*args, **kwargs)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/auditlog/receivers.py", line 81, in log_delete
    LogEntry.objects.log_create(
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/auditlog/models.py", line 47, in log_create
    kwargs.setdefault("object_repr", smart_str(instance))
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/utils/encoding.py", line 33, in smart_str
    return force_str(s, encoding, strings_only, errors)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/utils/encoding.py", line 72, in force_str
    s = str(s)
  File "/Users/vasanthakumar/projects/CLabsApp/integrations/models.py", line 77, in __str__
    return '%s-%s' % (self.integration.name, self.account)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 205, in __get__
    rel_obj = self.get_object(instance)
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 168, in get_object
    return qs.get(self.field.get_reverse_related_filter(instance))
  File "/Users/vasanthakumar/.pyenv/versions/djangov4/lib/python3.8/site-packages/django/db/models/query.py", line 496, in get
    raise self.model.DoesNotExist(
integrations.models.Details.DoesNotExist: Details matching query does not exist.

@charleswhchan
Copy link

Is there a solution? I am catching the exception as a workaround for now.

Using the example above:

    def __str__(self):  # added by @aleh-rymasheuski based on the stack trace seen below
        try:
            return '%s-%s' % (self.integration.name, self.account)
        except Details.DoesNotExist:
            # return some string ...

@aleh-rymasheuski
Copy link
Contributor

aleh-rymasheuski commented Dec 2, 2023

I guess we could handle DoesNotExist raised from the smart_str used for generating object_repr and provide a dumb default string for the object representation, such as "<error making object repr>". @hramezani, do you think this is a good solution to the bug? The bug itself is caused by auditlog making a string representation of an object that's in the middle of deletion and, having its string representation dependent on now-removed another object, not being representable as a string any more.

@aleh-rymasheuski aleh-rymasheuski self-assigned this Dec 2, 2023
@hramezani
Copy link
Member

@aleh-rymasheuski seems reasonable.
I can check it in depth when we have a PR for this

@kgosal03
Copy link

kgosal03 commented Mar 7, 2024

@aleh-rymasheuski and @hramezani , we are curious when you will be releasing the latest version as we are planning to use the tool. It works great but we had issue with the cascade thing which you have fixed but not in the version we have. We appreciate your time and effort.

@hramezani
Copy link
Member

@kgosal03 as you may now, v3 is in beta. We still are not sure that is really doing well.
That's it hasn't released yet. it would be a great help if you can test the v3 beta and let us know about the possible problems

@kgosal03
Copy link

kgosal03 commented Mar 26, 2024

@hramezani I have tested your latest code in the repo and all works fine. There is one thing I just want to discuss that once an object is deleted, we still have a link to the object which I feel like should not be there as it is gone and won't show anything about the object. Just the log entry with the disabled link might be nice.
Otherwise, great tool and fast as desired. Thanks and I hope this helps a bit :)

image

@hramezani
Copy link
Member

@kgosal03 Thanks for the testing and giving feedback.

It would be good to create an issue for this improvement

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

Successfully merging a pull request may close this issue.

5 participants