From 28b8fcab07ea6381f74b9dcebfab20a218fc7bce Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Mon, 29 Sep 2025 15:13:53 -0400 Subject: [PATCH 01/14] Update package format: setup.py -> pyproject.toml --- pyproject.toml | 20 ++++++++++++++++++++ requirements.txt | 4 ---- setup.py | 12 ------------ 3 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..926e061 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,20 @@ +[project] +name = "directus-sdk" +version = "1.0.0" +description = "python SDK for directus client wiht convenience functions" +authors = [ + {name = "Jason Cheng"}, +] + +readme = "README.md" +requires-python = ">=3.6" +dependencies = [ + "requests>=2.27.1", +] + +[project.urls] +Repository = "https://github.com/Jason-CKY/directus-sdk-python" +Issues = "https://github.com/Jason-CKY/directus-sdk-python/issues" + +[tool.setuptools.packages.find] +where = ["src/"] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 8649921..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -requests -uuid -pytest -pytest-cov \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index d8f5b09..0000000 --- a/setup.py +++ /dev/null @@ -1,12 +0,0 @@ -from setuptools import setup, find_packages -from directus import __version__ - -setup( - name='directus-sdk', - version=__version__, - description='python SDK for directus client wiht convenience functions', - url='https://github.com/Jason-CKY/directus-sdk-python', - author='Jason Cheng', - packages=find_packages(exclude=['examples']), - install_requires=['requests'] -) \ No newline at end of file From 2262e1ef93e7116bbaf7a67458da2b199fe4bf6f Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Mon, 29 Sep 2025 15:14:43 -0400 Subject: [PATCH 02/14] Import as non-versioned class for ease of updates --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 656f567..763c1c1 100644 --- a/README.md +++ b/README.md @@ -19,19 +19,19 @@ pip install -e . ### Initializa directus client ```python -from directus.clients import DirectusClient_V9 +from directus-sdk import DirectusClient_V9 as DirectusClient # Create a directus client connection with user static token -client = DirectusClient_V9(url="http://localhost:8055", token="admin-token") +client = DirectusClient(url="http://localhost:8055", token="admin-token") # Or create a directus client connection with email and password -client = DirectusClient_V9(url="http://localhost:8055", email="user@example.com", password="password") +client = DirectusClient(url="http://localhost:8055", email="user@example.com", password="password") ``` ### Logging in and out of the client ```python -client = DirectusClient_V9(url="http://localhost:8055", email="user@example.com", password="password") +client = DirectusClient(url="http://localhost:8055", email="user@example.com", password="password") # Log out and use static token instead client.logout() From 6c8d4ece1a462f18be18ca73610b37ed20be2c98 Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Mon, 29 Sep 2025 15:15:06 -0400 Subject: [PATCH 03/14] move source to src folder --- directus/__init__.py | 3 --- src/directus-sdk/__init__.py | 1 + {directus => src/directus-sdk}/clients.py | 0 3 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 directus/__init__.py create mode 100644 src/directus-sdk/__init__.py rename {directus => src/directus-sdk}/clients.py (100%) diff --git a/directus/__init__.py b/directus/__init__.py deleted file mode 100644 index eeb4c96..0000000 --- a/directus/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from directus.clients import DirectusClient_V9 - -__version__ = '1.0.0' \ No newline at end of file diff --git a/src/directus-sdk/__init__.py b/src/directus-sdk/__init__.py new file mode 100644 index 0000000..fc253c1 --- /dev/null +++ b/src/directus-sdk/__init__.py @@ -0,0 +1 @@ +from .clients import DirectusClient_V9 \ No newline at end of file diff --git a/directus/clients.py b/src/directus-sdk/clients.py similarity index 100% rename from directus/clients.py rename to src/directus-sdk/clients.py From f06be23d35d37379cabdfcdd0bf04375d6875873 Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Mon, 29 Sep 2025 15:26:41 -0400 Subject: [PATCH 04/14] Rename folder to directus_sdk to allow for valid imports --- README.md | 2 +- pyproject.toml | 2 +- src/{directus-sdk => directus_sdk}/__init__.py | 0 src/{directus-sdk => directus_sdk}/clients.py | 0 test.py | 2 ++ 5 files changed, 4 insertions(+), 2 deletions(-) rename src/{directus-sdk => directus_sdk}/__init__.py (100%) rename src/{directus-sdk => directus_sdk}/clients.py (100%) create mode 100644 test.py diff --git a/README.md b/README.md index 763c1c1..57e635f 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ pip install -e . ### Initializa directus client ```python -from directus-sdk import DirectusClient_V9 as DirectusClient +from directus_sdk import DirectusClient_V9 as DirectusClient # Create a directus client connection with user static token client = DirectusClient(url="http://localhost:8055", token="admin-token") diff --git a/pyproject.toml b/pyproject.toml index 926e061..7452c3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "directus-sdk" +name = "directus-sdk-python" version = "1.0.0" description = "python SDK for directus client wiht convenience functions" authors = [ diff --git a/src/directus-sdk/__init__.py b/src/directus_sdk/__init__.py similarity index 100% rename from src/directus-sdk/__init__.py rename to src/directus_sdk/__init__.py diff --git a/src/directus-sdk/clients.py b/src/directus_sdk/clients.py similarity index 100% rename from src/directus-sdk/clients.py rename to src/directus_sdk/clients.py diff --git a/test.py b/test.py new file mode 100644 index 0000000..2a3c22b --- /dev/null +++ b/test.py @@ -0,0 +1,2 @@ +import directus_sdk + From f17568a2a11ead14272c5c2a985c098367d956d7 Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Mon, 29 Sep 2025 15:32:34 -0400 Subject: [PATCH 05/14] Minor typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 57e635f..990a98e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ pip install -e . ## Usage -### Initializa directus client +### Initialize directus client ```python from directus_sdk import DirectusClient_V9 as DirectusClient From bee7ed7cf4b1ef3d74c1ead0b990499fb2789aaf Mon Sep 17 00:00:00 2001 From: DanielRubianes <149167316+DanielRubianes@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:50:40 -0400 Subject: [PATCH 06/14] Delete test.py --- test.py | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 test.py diff --git a/test.py b/test.py deleted file mode 100644 index 2a3c22b..0000000 --- a/test.py +++ /dev/null @@ -1,2 +0,0 @@ -import directus_sdk - From 4892774205b3c1f0637aa02925883e6a50ff017a Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Sat, 8 Nov 2025 21:39:20 -0500 Subject: [PATCH 07/14] Add case for presentation fields with null schema value --- src/directus_sdk/clients.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/directus_sdk/clients.py b/src/directus_sdk/clients.py index b48cb5a..96fe170 100644 --- a/src/directus_sdk/clients.py +++ b/src/directus_sdk/clients.py @@ -178,7 +178,7 @@ def delete_all_items(self, collection_name: str) -> None: This helper function helps to delete every item within the collection. ''' pk_name = self.get_pk_field(collection_name)['field'] - item_ids = [data['id'] for data in self.get(f"/items/{collection_name}?fields={pk_name}", params={"limit": -1})] + item_ids = [data[pk_name] for data in self.get(f"/items/{collection_name}?fields={pk_name}", params={"limit": -1})] if len(item_ids) == 0: raise AssertionError("No items to delete!") for i in range(0, len(item_ids), 100): @@ -199,7 +199,7 @@ def get_pk_field(self, collection_name: str) -> dict: ''' Return the primary key field of the collection ''' - return [field for field in self.get(f"/fields/{collection_name}") if field['schema']['is_primary_key']][0] + return [field for field in self.get(f"/fields/{collection_name}") if field['schema'] and field['schema']['is_primary_key']][0] def get_all_user_created_collection_names(self) -> list: ''' From f135f1fb3518f1bd461553428909127298f4f5ae Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Sat, 8 Nov 2025 21:40:35 -0500 Subject: [PATCH 08/14] Allow deletion call on empty collection --- src/directus_sdk/clients.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directus_sdk/clients.py b/src/directus_sdk/clients.py index 96fe170..023c078 100644 --- a/src/directus_sdk/clients.py +++ b/src/directus_sdk/clients.py @@ -180,7 +180,7 @@ def delete_all_items(self, collection_name: str) -> None: pk_name = self.get_pk_field(collection_name)['field'] item_ids = [data[pk_name] for data in self.get(f"/items/{collection_name}?fields={pk_name}", params={"limit": -1})] if len(item_ids) == 0: - raise AssertionError("No items to delete!") + return for i in range(0, len(item_ids), 100): self.delete(f"/items/{collection_name}", json=item_ids[i:i + 100]) From 7ff325ac62fa5e1810af44c63c2a2b2371264ad9 Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Sun, 9 Nov 2025 07:43:49 -0500 Subject: [PATCH 09/14] Do not look for 'errors' in response as strings can contain that text --- src/directus_sdk/clients.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directus_sdk/clients.py b/src/directus_sdk/clients.py index 023c078..cc9ccb3 100644 --- a/src/directus_sdk/clients.py +++ b/src/directus_sdk/clients.py @@ -97,7 +97,7 @@ def get(self, path, output_type: str = "json", **kwargs): verify=self.verify, **kwargs ) - if 'errors' in data.text: + if 'errors' in data.json(): raise AssertionError(data.json()['errors']) if output_type == 'csv': return data.text From 92b60b0b85622c200b0ec31bf235d714803e6a22 Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Thu, 13 Nov 2025 19:30:07 -0500 Subject: [PATCH 10/14] Add response typing & convert to httpx --- src/directus_sdk/clients.py | 57 ++++++++++++++----------------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/src/directus_sdk/clients.py b/src/directus_sdk/clients.py index cc9ccb3..462af59 100644 --- a/src/directus_sdk/clients.py +++ b/src/directus_sdk/clients.py @@ -1,14 +1,9 @@ -import requests +import httpx +from httpx import Response from urllib3.exceptions import InsecureRequestWarning class DirectusClient_V9(): - def __init__(self, url: str, token: str = None, email: str = None, password: str = None, verify: bool = False): - self.verify = verify - if not self.verify: - requests.packages.urllib3.disable_warnings( - category=InsecureRequestWarning - ) - + def __init__(self, url: str, token: str = None, email: str = None, password: str = None): self.url = url if token is not None: self.static_token = token @@ -34,7 +29,7 @@ def login(self, email: str = None, password: str = None) -> tuple: self.email = email self.password = password - auth = requests.post( + auth = httpx.post( f"{self.url}/auth/login", json={ "email": email, @@ -52,10 +47,9 @@ def logout(self, refresh_token: str = None) -> None: ''' if refresh_token is None: refresh_token = self.refresh_token - auth = requests.post( + auth = httpx.post( f"{self.url}/auth/logout", - json={"refresh_token": refresh_token}, - verify=self.verify + json={"refresh_token": refresh_token} ) self.temporary_token = None self.refresh_token = None @@ -66,12 +60,11 @@ def refresh(self, refresh_token: str = None) -> None: ''' if refresh_token is None: refresh_token = self.refresh_token - auth = requests.post( + auth = httpx.post( f"{self.url}/auth/refresh", json={ "refresh_token": refresh_token - }, - verify=self.verify + } ).json()['data'] self.temporary_token = auth['access_token'] @@ -91,10 +84,9 @@ def get_token(self): return token def get(self, path, output_type: str = "json", **kwargs): - data = requests.get( + data = httpx.get( f"{self.url}{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, - verify=self.verify, **kwargs ) if 'errors' in data.json(): @@ -104,40 +96,32 @@ def get(self, path, output_type: str = "json", **kwargs): return data.json()['data'] - def post(self, path, **kwargs): - x = requests.post( + def post(self, path, **kwargs) -> Response: + response: Response = httpx.post( f"{self.url}{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, - verify=self.verify, **kwargs ) - if x.status_code != 200: - raise AssertionError(x.text) - return x.json() + return response - def delete(self, path, **kwargs): - x = requests.delete( + def delete(self, path, **kwargs) -> Response: + response: Response = httpx.delete( f"{self.url}{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, - verify=self.verify, **kwargs ) - if x.status_code != 204: - raise AssertionError(x.text) + + return response - def patch(self, path, **kwargs): - x = requests.patch( + def patch(self, path, **kwargs) -> Response: + response: Response = httpx.patch( f"{self.url}{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, - verify=self.verify, **kwargs ) - - if x.status_code not in [200, 204]: - raise AssertionError(x.text) - return x.json() + return response def bulk_insert(self, collection_name: str, items: list, interval: int = 100, verbose: bool = False) -> None: ''' @@ -145,9 +129,10 @@ def bulk_insert(self, collection_name: str, items: list, interval: int = 100, ve ''' length = len(items) for i in range(0, length, interval): + response: Response = self.post(f"/items/{collection_name}", json=items[i:i + interval]) if verbose: print(f"Inserting {i}-{min(i+100, length)} out of {length}") - self.post(f"/items/{collection_name}", json=items[i:i + interval]) + print(response) def duplicate_collection(self, collection_name: str, duplicate_collection_name: str) -> None: ''' From 2ed7e7e2982714d1482f35ac278b1f7bda1eebb1 Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Thu, 13 Nov 2025 20:47:57 -0500 Subject: [PATCH 11/14] Simplify and further dev httpx --- src/directus_sdk/clients.py | 45 +++++++++++++++---------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/src/directus_sdk/clients.py b/src/directus_sdk/clients.py index 462af59..6aba744 100644 --- a/src/directus_sdk/clients.py +++ b/src/directus_sdk/clients.py @@ -1,6 +1,5 @@ -import httpx +import httpx, json from httpx import Response -from urllib3.exceptions import InsecureRequestWarning class DirectusClient_V9(): def __init__(self, url: str, token: str = None, email: str = None, password: str = None): @@ -83,18 +82,22 @@ def get_token(self): token = "" return token - def get(self, path, output_type: str = "json", **kwargs): - data = httpx.get( + def request(self, method, path, **kwargs) -> Response: + response: Response = httpx.request( + method=method, + url=f"{self.url}{path}", + headers={"Authorization": f"Bearer {self.get_token()}"}, + **kwargs + ) + return response + + def get(self, path, **kwargs) -> Response: + response: Response = httpx.get( f"{self.url}{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, **kwargs ) - if 'errors' in data.json(): - raise AssertionError(data.json()['errors']) - if output_type == 'csv': - return data.text - - return data.json()['data'] + return response def post(self, path, **kwargs) -> Response: response: Response = httpx.post( @@ -102,10 +105,10 @@ def post(self, path, **kwargs) -> Response: headers={"Authorization": f"Bearer {self.get_token()}"}, **kwargs ) - return response def delete(self, path, **kwargs) -> Response: + print(f"{self.url}{path}") response: Response = httpx.delete( f"{self.url}{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, @@ -123,16 +126,15 @@ def patch(self, path, **kwargs) -> Response: return response - def bulk_insert(self, collection_name: str, items: list, interval: int = 100, verbose: bool = False) -> None: + def bulk_insert(self, collection_name: str, items: list, interval: int = 100) -> Response: ''' Post items is capped at 100 items. This function breaks up any list of items more than 100 long and bulk insert + Returns repsonse of last request ''' length = len(items) for i in range(0, length, interval): response: Response = self.post(f"/items/{collection_name}", json=items[i:i + interval]) - if verbose: - print(f"Inserting {i}-{min(i+100, length)} out of {length}") - print(response) + return response def duplicate_collection(self, collection_name: str, duplicate_collection_name: str) -> None: ''' @@ -162,12 +164,7 @@ def delete_all_items(self, collection_name: str) -> None: Delete all items from the directus collection. Delete api from directus only able to delete based on ID. This helper function helps to delete every item within the collection. ''' - pk_name = self.get_pk_field(collection_name)['field'] - item_ids = [data[pk_name] for data in self.get(f"/items/{collection_name}?fields={pk_name}", params={"limit": -1})] - if len(item_ids) == 0: - return - for i in range(0, len(item_ids), 100): - self.delete(f"/items/{collection_name}", json=item_ids[i:i + 100]) + self.request("DELETE", f"/items/{collection_name}", json={"query":{"limit": -1}}) def get_all_fields(self, collection_name: str) -> list: ''' @@ -180,12 +177,6 @@ def get_all_fields(self, collection_name: str) -> list: return fields - def get_pk_field(self, collection_name: str) -> dict: - ''' - Return the primary key field of the collection - ''' - return [field for field in self.get(f"/fields/{collection_name}") if field['schema'] and field['schema']['is_primary_key']][0] - def get_all_user_created_collection_names(self) -> list: ''' Returns all user created collections. By default Directus GET /collections API will return system collections as well which From dcfc9a993ee6ade4160f1877885a6caa4b55d0a3 Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Mon, 1 Dec 2025 14:23:30 -0500 Subject: [PATCH 12/14] Increase interval size --- src/directus_sdk/clients.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/directus_sdk/clients.py b/src/directus_sdk/clients.py index 6aba744..bae10c7 100644 --- a/src/directus_sdk/clients.py +++ b/src/directus_sdk/clients.py @@ -126,15 +126,19 @@ def patch(self, path, **kwargs) -> Response: return response - def bulk_insert(self, collection_name: str, items: list, interval: int = 100) -> Response: + def bulk_insert(self, collection_name: str, items: list, interval: int = 1000) -> None: ''' Post items is capped at 100 items. This function breaks up any list of items more than 100 long and bulk insert Returns repsonse of last request ''' - length = len(items) - for i in range(0, length, interval): + if len(items) == 0: + return None + + for i in range(0, len(items), interval): response: Response = self.post(f"/items/{collection_name}", json=items[i:i + interval]) - return response + if response.status_code in (400, 500): + print(response.content) + return None def duplicate_collection(self, collection_name: str, duplicate_collection_name: str) -> None: ''' From 706fb537a03dffe734da2e5ae4f352af63ddb4ef Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Tue, 2 Dec 2025 06:55:59 -0500 Subject: [PATCH 13/14] Update / --- src/directus_sdk/clients.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/directus_sdk/clients.py b/src/directus_sdk/clients.py index bae10c7..849f9e3 100644 --- a/src/directus_sdk/clients.py +++ b/src/directus_sdk/clients.py @@ -85,7 +85,7 @@ def get_token(self): def request(self, method, path, **kwargs) -> Response: response: Response = httpx.request( method=method, - url=f"{self.url}{path}", + url=f"{self.url}/{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, **kwargs ) @@ -93,7 +93,7 @@ def request(self, method, path, **kwargs) -> Response: def get(self, path, **kwargs) -> Response: response: Response = httpx.get( - f"{self.url}{path}", + f"{self.url}/{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, **kwargs ) @@ -101,16 +101,16 @@ def get(self, path, **kwargs) -> Response: def post(self, path, **kwargs) -> Response: response: Response = httpx.post( - f"{self.url}{path}", + f"{self.url}/{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, **kwargs ) return response def delete(self, path, **kwargs) -> Response: - print(f"{self.url}{path}") + print(f"{self.url}/{path}") response: Response = httpx.delete( - f"{self.url}{path}", + f"{self.url}/{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, **kwargs ) @@ -119,14 +119,14 @@ def delete(self, path, **kwargs) -> Response: def patch(self, path, **kwargs) -> Response: response: Response = httpx.patch( - f"{self.url}{path}", + f"{self.url}/{path}", headers={"Authorization": f"Bearer {self.get_token()}"}, **kwargs ) return response - def bulk_insert(self, collection_name: str, items: list, interval: int = 1000) -> None: + def bulk_insert(self, collection_name: str, items: list, interval: int = 500) -> None: ''' Post items is capped at 100 items. This function breaks up any list of items more than 100 long and bulk insert Returns repsonse of last request @@ -135,7 +135,7 @@ def bulk_insert(self, collection_name: str, items: list, interval: int = 1000) - return None for i in range(0, len(items), interval): - response: Response = self.post(f"/items/{collection_name}", json=items[i:i + interval]) + response: Response = self.post(f"items/{collection_name}", json=items[i:i + interval]) if response.status_code in (400, 500): print(response.content) return None @@ -144,17 +144,17 @@ def duplicate_collection(self, collection_name: str, duplicate_collection_name: ''' Duplicate the collection with schema, fields, and data ''' - duplicate_collection = self.get(f"/collections/{collection_name}") + duplicate_collection = self.get(f"collections/{collection_name}") duplicate_collection['collection'] = duplicate_collection_name duplicate_collection['meta']['collection'] = duplicate_collection_name duplicate_collection['schema']['name'] = duplicate_collection_name - self.post("/collections", json=duplicate_collection) + self.post("collections", json=duplicate_collection) fields = [ field for field in self.get_all_fields(collection_name) if not field['schema']['is_primary_key'] ] for field in fields: - self.post(f"/fields/{duplicate_collection_name}", json=field) - self.bulk_insert(duplicate_collection_name, self.get(f"/items/{collection_name}", params={"limit": -1})) + self.post(f"fields/{duplicate_collection_name}", json=field) + self.bulk_insert(duplicate_collection_name, self.get(f"items/{collection_name}", params={"limit": -1})) def collection_exists(self, collection_name: str): ''' @@ -168,13 +168,13 @@ def delete_all_items(self, collection_name: str) -> None: Delete all items from the directus collection. Delete api from directus only able to delete based on ID. This helper function helps to delete every item within the collection. ''' - self.request("DELETE", f"/items/{collection_name}", json={"query":{"limit": -1}}) + self.request("DELETE", f"items/{collection_name}", json={"query":{"limit": -1}}) def get_all_fields(self, collection_name: str) -> list: ''' Return all fields in the directus collection. Remove the id key in metya to avoid errors in inseting this directus field again ''' - fields = self.get(f"/fields/{collection_name}") + fields = self.get(f"fields/{collection_name}") for field in fields: if 'meta' in field and field['meta'] is not None and 'id' in field['meta']: field['meta'].pop('id') @@ -186,13 +186,13 @@ def get_all_user_created_collection_names(self) -> list: Returns all user created collections. By default Directus GET /collections API will return system collections as well which may not always be useful. ''' - return [ col['collection'] for col in self.get('/collections') if not col['collection'].startswith('directus') ] + return [ col['collection'] for col in self.get('collections') if not col['collection'].startswith('directus') ] def get_all_fk_fields(self, collection_name: str) -> dict: ''' Return all foreign key fields in the directus collection ''' - return [ field for field in self.get(f"/fields/{collection_name}") + return [ field for field in self.get(f"fields/{collection_name}") if 'foreign_key_table' in field['schema'].keys() and field['schema']['foreign_key_table'] is not None ] @@ -205,7 +205,7 @@ def get_relations(self, collection_name: str) -> list: "collection": relation["collection"], "field": relation["field"], "related_collection": relation["related_collection"] - } for relation in self.get(f"/relations/{collection_name}")] + } for relation in self.get(f"relations/{collection_name}")] def post_relation(self, relation: dict) -> None: ''' @@ -214,7 +214,7 @@ def post_relation(self, relation: dict) -> None: ''' assert set(relation.keys()) == set(['collection', 'field', 'related_collection']) try: - self.post(f"/relations", json=relation) + self.post(f"relations", json=relation) except AssertionError as e: if '"id" has to be unique' in str(e): self.post_relation(relation) From 294e5a3cd18dfeb9b325a66b664f7a8135bab33a Mon Sep 17 00:00:00 2001 From: DanielRubianes Date: Mon, 26 Jan 2026 16:44:42 +0000 Subject: [PATCH 14/14] add bulk_update --- src/directus_sdk/clients.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/directus_sdk/clients.py b/src/directus_sdk/clients.py index 849f9e3..bc2b096 100644 --- a/src/directus_sdk/clients.py +++ b/src/directus_sdk/clients.py @@ -126,17 +126,23 @@ def patch(self, path, **kwargs) -> Response: return response - def bulk_insert(self, collection_name: str, items: list, interval: int = 500) -> None: - ''' - Post items is capped at 100 items. This function breaks up any list of items more than 100 long and bulk insert - Returns repsonse of last request - ''' + def bulk_insert(self, collection_name: str, items: list[dict], interval: int = 500) -> None: if len(items) == 0: return None for i in range(0, len(items), interval): response: Response = self.post(f"items/{collection_name}", json=items[i:i + interval]) - if response.status_code in (400, 500): + if response.status_code > 200: + print(response.content) + return None + + def bulk_update(self, collection_name: str, items: list[dict], interval: int = 500) -> None: + if len(items) == 0: + return None + + for i in range(0, len(items), interval): + response: Response = self.patch(f"items/{collection_name}", json=items[i:i + interval]) + if response.status_code > 200: print(response.content) return None