PYTHON-4946 - Add GridFSBucket.rename_by_name (#2219)
This commit is contained in:
parent
8675a163df
commit
a3f3ec52bc
@ -9,6 +9,8 @@ PyMongo 4.12 brings a number of changes including:
|
||||
- Support for configuring DEK cache lifetime via the ``key_expiration_ms`` argument to
|
||||
:class:`~pymongo.encryption_options.AutoEncryptionOpts`.
|
||||
- Support for $lookup in CSFLE and QE supported on MongoDB 8.1+.
|
||||
- Added :meth:`gridfs.asynchronous.grid_file.AsyncGridFSBucket.rename_by_name` and :meth:`gridfs.grid_file.GridFSBucket.rename_by_name`
|
||||
for more performant renaming of a file with multiple revisions.
|
||||
- Added :meth:`gridfs.asynchronous.grid_file.AsyncGridFSBucket.delete_by_name` and :meth:`gridfs.grid_file.GridFSBucket.delete_by_name`
|
||||
for more performant deletion of a file with multiple revisions.
|
||||
- AsyncMongoClient no longer performs DNS resolution for "mongodb+srv://" connection strings on creation.
|
||||
|
||||
@ -1050,6 +1050,35 @@ class AsyncGridFSBucket:
|
||||
"matched file_id %i" % (new_filename, file_id)
|
||||
)
|
||||
|
||||
async def rename_by_name(
|
||||
self, filename: str, new_filename: str, session: Optional[AsyncClientSession] = None
|
||||
) -> None:
|
||||
"""Renames the stored file with the specified filename.
|
||||
|
||||
For example::
|
||||
|
||||
my_db = AsyncMongoClient().test
|
||||
fs = AsyncGridFSBucket(my_db)
|
||||
await fs.upload_from_stream("test_file", "data I want to store!")
|
||||
await fs.rename_by_name("test_file", "new_test_name")
|
||||
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no file with the given filename exists.
|
||||
|
||||
:param filename: The filename of the file to be renamed.
|
||||
:param new_filename: The new name of the file.
|
||||
:param session: a :class:`~pymongo.client_session.AsyncClientSession`
|
||||
|
||||
.. versionadded:: 4.12
|
||||
"""
|
||||
_disallow_transactions(session)
|
||||
result = await self._files.update_many(
|
||||
{"filename": filename}, {"$set": {"filename": new_filename}}, session=session
|
||||
)
|
||||
if not result.matched_count:
|
||||
raise NoFile(
|
||||
f"no files could be renamed {new_filename!r} because none matched filename {filename!r}"
|
||||
)
|
||||
|
||||
|
||||
class AsyncGridIn:
|
||||
"""Class to write data to GridFS."""
|
||||
|
||||
@ -1042,6 +1042,35 @@ class GridFSBucket:
|
||||
"matched file_id %i" % (new_filename, file_id)
|
||||
)
|
||||
|
||||
def rename_by_name(
|
||||
self, filename: str, new_filename: str, session: Optional[ClientSession] = None
|
||||
) -> None:
|
||||
"""Renames the stored file with the specified filename.
|
||||
|
||||
For example::
|
||||
|
||||
my_db = MongoClient().test
|
||||
fs = GridFSBucket(my_db)
|
||||
fs.upload_from_stream("test_file", "data I want to store!")
|
||||
fs.rename_by_name("test_file", "new_test_name")
|
||||
|
||||
Raises :exc:`~gridfs.errors.NoFile` if no file with the given filename exists.
|
||||
|
||||
:param filename: The filename of the file to be renamed.
|
||||
:param new_filename: The new name of the file.
|
||||
:param session: a :class:`~pymongo.client_session.ClientSession`
|
||||
|
||||
.. versionadded:: 4.12
|
||||
"""
|
||||
_disallow_transactions(session)
|
||||
result = self._files.update_many(
|
||||
{"filename": filename}, {"$set": {"filename": new_filename}}, session=session
|
||||
)
|
||||
if not result.matched_count:
|
||||
raise NoFile(
|
||||
f"no files could be renamed {new_filename!r} because none matched filename {filename!r}"
|
||||
)
|
||||
|
||||
|
||||
class GridIn:
|
||||
"""Class to write data to GridFS."""
|
||||
|
||||
@ -450,6 +450,19 @@ class TestGridfs(AsyncIntegrationTest):
|
||||
b"testing", await (await self.fs.open_download_stream_by_name("second_name")).read()
|
||||
)
|
||||
|
||||
async def test_rename_by_name(self):
|
||||
_id = await self.fs.upload_from_stream("first_name", b"testing")
|
||||
self.assertEqual(
|
||||
b"testing", await (await self.fs.open_download_stream_by_name("first_name")).read()
|
||||
)
|
||||
|
||||
await self.fs.rename_by_name("first_name", "second_name")
|
||||
with self.assertRaises(NoFile):
|
||||
await self.fs.open_download_stream_by_name("first_name")
|
||||
self.assertEqual(
|
||||
b"testing", await (await self.fs.open_download_stream_by_name("second_name")).read()
|
||||
)
|
||||
|
||||
@patch("gridfs.asynchronous.grid_file._UPLOAD_BUFFER_SIZE", 5)
|
||||
async def test_abort(self):
|
||||
gin = self.fs.open_upload_stream("test_filename", chunk_size_bytes=5)
|
||||
|
||||
@ -541,6 +541,7 @@ class TestSession(AsyncIntegrationTest):
|
||||
(bucket.download_to_stream_by_name, ["f", sio], {}),
|
||||
(find, [], {}),
|
||||
(bucket.rename, [1, "f2"], {}),
|
||||
(bucket.rename_by_name, ["f2", "f3"], {}),
|
||||
# Delete both files so _test_ops can run these operations twice.
|
||||
(bucket.delete, [1], {}),
|
||||
(bucket.delete_by_name, ["f"], {}),
|
||||
|
||||
@ -295,7 +295,14 @@ class TestTransactions(AsyncTransactionsBase):
|
||||
"new-name",
|
||||
),
|
||||
),
|
||||
(bucket.delete_by_name, ("new-name",)),
|
||||
(
|
||||
bucket.rename_by_name,
|
||||
(
|
||||
"new-name",
|
||||
"new-name2",
|
||||
),
|
||||
),
|
||||
(bucket.delete_by_name, ("new-name2",)),
|
||||
]
|
||||
|
||||
async with client.start_session() as s, await s.start_transaction():
|
||||
|
||||
313
test/gridfs/renameByName.json
Normal file
313
test/gridfs/renameByName.json
Normal file
@ -0,0 +1,313 @@
|
||||
{
|
||||
"description": "gridfs-renameByName",
|
||||
"schemaVersion": "1.0",
|
||||
"createEntities": [
|
||||
{
|
||||
"client": {
|
||||
"id": "client0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"database": {
|
||||
"id": "database0",
|
||||
"client": "client0",
|
||||
"databaseName": "gridfs-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bucket": {
|
||||
"id": "bucket0",
|
||||
"database": "database0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": {
|
||||
"id": "bucket0_files_collection",
|
||||
"database": "database0",
|
||||
"collectionName": "fs.files"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": {
|
||||
"id": "bucket0_chunks_collection",
|
||||
"database": "database0",
|
||||
"collectionName": "fs.chunks"
|
||||
}
|
||||
}
|
||||
],
|
||||
"initialData": [
|
||||
{
|
||||
"collectionName": "fs.files",
|
||||
"databaseName": "gridfs-tests",
|
||||
"documents": [
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000001"
|
||||
},
|
||||
"length": 0,
|
||||
"chunkSize": 4,
|
||||
"uploadDate": {
|
||||
"$date": "1970-01-01T00:00:00.000Z"
|
||||
},
|
||||
"filename": "filename",
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000002"
|
||||
},
|
||||
"length": 0,
|
||||
"chunkSize": 4,
|
||||
"uploadDate": {
|
||||
"$date": "1970-01-01T00:00:00.000Z"
|
||||
},
|
||||
"filename": "filename",
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000003"
|
||||
},
|
||||
"length": 2,
|
||||
"chunkSize": 4,
|
||||
"uploadDate": {
|
||||
"$date": "1970-01-01T00:00:00.000Z"
|
||||
},
|
||||
"filename": "filename",
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000004"
|
||||
},
|
||||
"length": 8,
|
||||
"chunkSize": 4,
|
||||
"uploadDate": {
|
||||
"$date": "1970-01-01T00:00:00.000Z"
|
||||
},
|
||||
"filename": "otherfilename",
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"collectionName": "fs.chunks",
|
||||
"databaseName": "gridfs-tests",
|
||||
"documents": [
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000001"
|
||||
},
|
||||
"files_id": {
|
||||
"$oid": "000000000000000000000002"
|
||||
},
|
||||
"n": 0,
|
||||
"data": {
|
||||
"$binary": {
|
||||
"base64": "",
|
||||
"subType": "00"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000002"
|
||||
},
|
||||
"files_id": {
|
||||
"$oid": "000000000000000000000003"
|
||||
},
|
||||
"n": 0,
|
||||
"data": {
|
||||
"$binary": {
|
||||
"base64": "",
|
||||
"subType": "00"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000003"
|
||||
},
|
||||
"files_id": {
|
||||
"$oid": "000000000000000000000004"
|
||||
},
|
||||
"n": 0,
|
||||
"data": {
|
||||
"$binary": {
|
||||
"base64": "",
|
||||
"subType": "00"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000004"
|
||||
},
|
||||
"files_id": {
|
||||
"$oid": "000000000000000000000004"
|
||||
},
|
||||
"n": 1,
|
||||
"data": {
|
||||
"$binary": {
|
||||
"base64": "",
|
||||
"subType": "00"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tests": [
|
||||
{
|
||||
"description": "rename when multiple revisions of the file exist",
|
||||
"operations": [
|
||||
{
|
||||
"name": "renameByName",
|
||||
"object": "bucket0",
|
||||
"arguments": {
|
||||
"filename": "filename",
|
||||
"newFilename": "newfilename"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": [
|
||||
{
|
||||
"collectionName": "fs.files",
|
||||
"databaseName": "gridfs-tests",
|
||||
"documents": [
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000001"
|
||||
},
|
||||
"length": 0,
|
||||
"chunkSize": 4,
|
||||
"uploadDate": {
|
||||
"$date": "1970-01-01T00:00:00.000Z"
|
||||
},
|
||||
"filename": "newfilename",
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000002"
|
||||
},
|
||||
"length": 0,
|
||||
"chunkSize": 4,
|
||||
"uploadDate": {
|
||||
"$date": "1970-01-01T00:00:00.000Z"
|
||||
},
|
||||
"filename": "newfilename",
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000003"
|
||||
},
|
||||
"length": 2,
|
||||
"chunkSize": 4,
|
||||
"uploadDate": {
|
||||
"$date": "1970-01-01T00:00:00.000Z"
|
||||
},
|
||||
"filename": "newfilename",
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000004"
|
||||
},
|
||||
"length": 8,
|
||||
"chunkSize": 4,
|
||||
"uploadDate": {
|
||||
"$date": "1970-01-01T00:00:00.000Z"
|
||||
},
|
||||
"filename": "otherfilename",
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"collectionName": "fs.chunks",
|
||||
"databaseName": "gridfs-tests",
|
||||
"documents": [
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000001"
|
||||
},
|
||||
"files_id": {
|
||||
"$oid": "000000000000000000000002"
|
||||
},
|
||||
"n": 0,
|
||||
"data": {
|
||||
"$binary": {
|
||||
"base64": "",
|
||||
"subType": "00"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000002"
|
||||
},
|
||||
"files_id": {
|
||||
"$oid": "000000000000000000000003"
|
||||
},
|
||||
"n": 0,
|
||||
"data": {
|
||||
"$binary": {
|
||||
"base64": "",
|
||||
"subType": "00"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000003"
|
||||
},
|
||||
"files_id": {
|
||||
"$oid": "000000000000000000000004"
|
||||
},
|
||||
"n": 0,
|
||||
"data": {
|
||||
"$binary": {
|
||||
"base64": "",
|
||||
"subType": "00"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"_id": {
|
||||
"$oid": "000000000000000000000004"
|
||||
},
|
||||
"files_id": {
|
||||
"$oid": "000000000000000000000004"
|
||||
},
|
||||
"n": 1,
|
||||
"data": {
|
||||
"$binary": {
|
||||
"base64": "",
|
||||
"subType": "00"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "rename when file name does not exist",
|
||||
"operations": [
|
||||
{
|
||||
"name": "renameByName",
|
||||
"object": "bucket0",
|
||||
"arguments": {
|
||||
"filename": "missing-file",
|
||||
"newFilename": "newfilename"
|
||||
},
|
||||
"expectError": {
|
||||
"isClientError": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -424,6 +424,15 @@ class TestGridfs(IntegrationTest):
|
||||
self.fs.open_download_stream_by_name("first_name")
|
||||
self.assertEqual(b"testing", (self.fs.open_download_stream_by_name("second_name")).read())
|
||||
|
||||
def test_rename_by_name(self):
|
||||
_id = self.fs.upload_from_stream("first_name", b"testing")
|
||||
self.assertEqual(b"testing", (self.fs.open_download_stream_by_name("first_name")).read())
|
||||
|
||||
self.fs.rename_by_name("first_name", "second_name")
|
||||
with self.assertRaises(NoFile):
|
||||
self.fs.open_download_stream_by_name("first_name")
|
||||
self.assertEqual(b"testing", (self.fs.open_download_stream_by_name("second_name")).read())
|
||||
|
||||
@patch("gridfs.synchronous.grid_file._UPLOAD_BUFFER_SIZE", 5)
|
||||
def test_abort(self):
|
||||
gin = self.fs.open_upload_stream("test_filename", chunk_size_bytes=5)
|
||||
|
||||
@ -541,6 +541,7 @@ class TestSession(IntegrationTest):
|
||||
(bucket.download_to_stream_by_name, ["f", sio], {}),
|
||||
(find, [], {}),
|
||||
(bucket.rename, [1, "f2"], {}),
|
||||
(bucket.rename_by_name, ["f2", "f3"], {}),
|
||||
# Delete both files so _test_ops can run these operations twice.
|
||||
(bucket.delete, [1], {}),
|
||||
(bucket.delete_by_name, ["f"], {}),
|
||||
|
||||
@ -287,7 +287,14 @@ class TestTransactions(TransactionsBase):
|
||||
"new-name",
|
||||
),
|
||||
),
|
||||
(bucket.delete_by_name, ("new-name",)),
|
||||
(
|
||||
bucket.rename_by_name,
|
||||
(
|
||||
"new-name",
|
||||
"new-name2",
|
||||
),
|
||||
),
|
||||
(bucket.delete_by_name, ("new-name2",)),
|
||||
]
|
||||
|
||||
with client.start_session() as s, s.start_transaction():
|
||||
|
||||
Loading…
Reference in New Issue
Block a user