diff --git a/django/core/files/storage.py b/django/core/files/storage.py index 2cf5e2a5c7..7097dc9585 100644 --- a/django/core/files/storage.py +++ b/django/core/files/storage.py @@ -316,7 +316,7 @@ class FileSystemStorage(Storage): pass def exists(self, name): - return os.path.exists(self.path(name)) + return os.path.lexists(self.path(name)) def listdir(self, path): path = self.path(path) diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index 381bc6b7b5..8b69617752 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -28,6 +28,7 @@ from django.test import ( from django.test.utils import requires_tz_support from django.urls import NoReverseMatch, reverse_lazy from django.utils import timezone +from django.utils._os import symlinks_supported from .models import ( Storage, callable_storage, temp_storage, temp_storage_location, @@ -297,6 +298,16 @@ class FileStorageTests(SimpleTestCase): self.storage.delete('path/to/test.file') + @unittest.skipUnless(symlinks_supported(), 'Must be able to symlink to run this test.') + def test_file_save_broken_symlink(self): + """A new path is created on save when a broken symlink is supplied.""" + nonexistent_file_path = os.path.join(self.temp_dir, 'nonexistent.txt') + broken_symlink_path = os.path.join(self.temp_dir, 'symlink.txt') + os.symlink(nonexistent_file_path, broken_symlink_path) + f = ContentFile('some content') + f_name = self.storage.save(broken_symlink_path, f) + self.assertIs(os.path.exists(os.path.join(self.temp_dir, f_name)), True) + def test_save_doesnt_close(self): with TemporaryUploadedFile('test', 'text/plain', 1, 'utf8') as file: file.write(b'1')