jazzband / django-simple-history

Store model history and view/revert changes from admin site.
https://django-simple-history.readthedocs.org
BSD 3-Clause "New" or "Revised" License
2.18k stars 476 forks source link

Disable transform of FileField to TextField/CharField #766

Open mcsimps2 opened 3 years ago

mcsimps2 commented 3 years ago

Problem In my application, I have a use case where I do not want to transform a FileField/ImageField to a TextField/CharField in a historical model. For developers who want to access the URLs of files and images in historical records without a hassle, maintaining the underlying type of a FileField is very useful. Note that Django, or at least the most recent version, does not delete files when a referring row is deleted, which is great for historical records (unless you want to clean up dangling files).

E.g. I cannot write user.history.most_recent().profile_picture.url with the current codebase. I have to manually resolve user.history.most_recent().profile_picture to a URL myself.

Solution I've forked off this codebase with a simple modification. In models.py, I added a setting SIMPLE_HISTORY_TRANFORM_FILEFIELD, which defaults to True. In my use case, I set this to False so that I maintain ImageFields/FileFields as I need to:

def transform_field(field):
       ...
      elif isinstance(field, models.FileField) and getattr(settings, "SIMPLE_HISTORY_TRANSFORM_FILEFIELD", True):
        # Don't copy file, just path.
        if getattr(settings, "SIMPLE_HISTORY_FILEFIELD_TO_CHARFIELD", False):
            field.__class__ = models.CharField
        else:
            field.__class__ = models.TextField

This has worked well for me without a problem, even as records are updated and deleted. I'm always able to access the historical image/file URL as I want to. However, I am interested if anyone familiar with the codebase foresees logical problems with this solution that could arise, which may explain why this option was never offered in the first place.

Alternatives A manual alternative to this is that developers can manually resolve the CharFields to files. This is a bit of a hassle, because with file fields, you can call user.profile_picture.url. In the case of a historical model, you would need to resolve user.profile_picture to a URL using Django storage backends and File subclasses.

I would be glad to open a PR if others think this would be a useful addition.

Cellou404 commented 1 month ago

I want to display the full path of uploaded files like this:

"commercial_proposal": { "old": "http://localhost:8000/submissions/commercial/tender_fAcfkpf.doc", "new": "http://localhost:8000/submissions/commercial/programme_backend_gaulGQh.pdf" },

Instead of this:

"commercial_proposal": { "old": "submissions/commercial/tender_fAcfkpf.doc", "new": "submissions/commercial/programme_backend_gaulGQh.pdf" },