From 765a76bda7519a787ba42ed8d9910da8815597f1 Mon Sep 17 00:00:00 2001 From: Jacob Wegner Date: Mon, 13 May 2024 13:37:13 -0500 Subject: [PATCH] Ensure virtual FileField instances mirror FileField Use descriptor_class to mimic how FileField returns a FieldFile instance https://github.com/django/django/blob/629398e55fd260c2ac4c2a9fc8b0c7d8dbda9e56/django/db/models/fields/files.py#L321 https://github.com/django/django/blob/629398e55fd260c2ac4c2a9fc8b0c7d8dbda9e56/django/db/models/fields/files.py#L166 --- modeltrans/fields.py | 5 +++-- tests/test_models.py | 51 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/modeltrans/fields.py b/modeltrans/fields.py index 5696181..45b3544 100644 --- a/modeltrans/fields.py +++ b/modeltrans/fields.py @@ -111,8 +111,9 @@ def get_localized_value(self, instance, field_name): value = instance.i18n.get(field_name) if isinstance(self.original_field, fields.files.FileField): - # TODO: Review this versus `descriptor_class`; need to write some additional tests to verify - return self.attr_class(instance, self, value) + descriptor = self.descriptor_class(self) + descriptor.__set__(instance, value) + return descriptor.__get__(instance) return value diff --git a/tests/test_models.py b/tests/test_models.py index de3cdc5..ae99200 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,6 +1,7 @@ from django import VERSION as DJANGO_VERSION from django.core.exceptions import ValidationError from django.core.files.base import ContentFile +from django.core.files.storage.memory import InMemoryFileNode from django.db import DataError, models, transaction from django.test import TestCase, override_settings from django.utils.translation import override @@ -127,12 +128,10 @@ def test_fallback_getting_FileField(self): post = Post.objects.create(title="Test Post", is_published=True) sample_file = ContentFile("sample content", name="sample-en.txt") attachment = Attachment.objects.create(post=post, file=sample_file) - default_file_name = attachment.file.name - assert default_file_name with override("fr"): self.assertIsInstance(attachment.file_i18n, models.fields.files.FieldFile) - self.assertEqual(attachment.file_i18n.name, default_file_name) + self.assertIsInstance(attachment.file_i18n.file, InMemoryFileNode) def test_set_FileField(self): post = Post.objects.create(title="Test Post", is_published=True) @@ -145,14 +144,58 @@ def test_set_FileField(self): attachment = Attachment.objects.create( post=post, file=sample_file_en, file_fr=sample_file_fr ) + self.assertIsInstance(attachment.file_fr, models.fields.files.FieldFile) + + self.assertIsInstance(attachment.file.file, InMemoryFileNode) + self.assertIsInstance(attachment.file_fr.file, InMemoryFileNode) saved_fr_content = attachment.file_fr.read().decode("utf-8") self.assertEqual(saved_fr_content, fr_content) - self.assertIsInstance(attachment.file_fr, models.fields.files.FieldFile) with override("fr"): self.assertEqual(attachment.file_i18n, attachment.file_fr) + def test_FileField_getter(self): + post = Post.objects.create(title="Test Post", is_published=True) + + fr_content = "exemple de contenu 2" + sample_file_fr = ContentFile(fr_content, name="sample-fr-2.txt") + + attachment = Attachment(post=post, file_fr=sample_file_fr) + # prior to invoking save, the file_fr should be an instance of FieldFile + self.assertIsInstance(attachment.file_fr, models.fields.files.FieldFile) + # but the file object should be an instance of ContentFile + self.assertIsInstance(attachment.file_fr.file, ContentFile) + attachment.save() + + # After saving, the file object should be the default storage class + self.assertIsInstance(attachment.file_fr.file, InMemoryFileNode) + + # Retreiving the instance from the database, file and file_fr + # should return the same kind of interface (a FieldFile instance) + attachment = Attachment.objects.get(pk=attachment.pk) + self.assertIsInstance(attachment.file, models.fields.files.FieldFile) + self.assertIsInstance(attachment.file_fr, models.fields.files.FieldFile) + + # Test that we can overwrite those with new content + new_content = "new content" + new_fr_content = "new French content" + attachment.file = ContentFile(new_content, name="content-new.txt") + attachment.file_fr = ContentFile(new_fr_content, name="content-new-fr.txt") + self.assertIsInstance(attachment.file, models.fields.files.FieldFile) + self.assertIsInstance(attachment.file_fr, models.fields.files.FieldFile) + + attachment.save() + + self.assertIsInstance(attachment.file_fr, models.fields.files.FieldFile) + self.assertIsInstance(attachment.file, models.fields.files.FieldFile) + + with attachment.file.open() as f: + self.assertEqual(f.read().decode("utf-8"), new_content) + + with attachment.file_fr.open() as f: + self.assertEqual(f.read().decode("utf-8"), new_fr_content) + def test_creating_using_virtual_default_language_field(self): m = Blog.objects.create(title_en="Falcon")