From 3ef1b9d0dd799a88f77f299cdbf9b0d34730e31a Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Fri, 12 Sep 2025 22:13:20 +0300 Subject: [PATCH 01/20] Add all checks makefile command. --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 724e4ab..c019002 100644 --- a/Makefile +++ b/Makefile @@ -19,4 +19,7 @@ format: poetry run ruff format .PHONY: test -test: unit \ No newline at end of file +test: unit + +.PHONY: all-checks +all-checks: lint typing unit \ No newline at end of file From b77f14d60e879095a857209a7faa58bbfbb53963 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Fri, 12 Sep 2025 22:17:29 +0300 Subject: [PATCH 02/20] Update pyproject toml. --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 42011e4..fde7a8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] -name = "django-params-field" +name = "django-dict-field" version = "0.0.1" -description = "Django extension for storing a lot of parameters in one field." +description = "Django extension for efficient and easy storing dict data in django field." authors = [{ name = "skv0zsneg", email = "" }] readme = "README.md" requires-python = ">=3.9" From bf63de52cc670699d0e4f12f9f8f31b016aa7309 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Fri, 12 Sep 2025 22:18:04 +0300 Subject: [PATCH 03/20] Update main dir name. --- src/{django_params_field => django_dict_field}/__init__.py | 0 src/{django_params_field => django_dict_field}/params_field.py | 0 src/{django_params_field => django_dict_field}/serializer.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/{django_params_field => django_dict_field}/__init__.py (100%) rename src/{django_params_field => django_dict_field}/params_field.py (100%) rename src/{django_params_field => django_dict_field}/serializer.py (100%) diff --git a/src/django_params_field/__init__.py b/src/django_dict_field/__init__.py similarity index 100% rename from src/django_params_field/__init__.py rename to src/django_dict_field/__init__.py diff --git a/src/django_params_field/params_field.py b/src/django_dict_field/params_field.py similarity index 100% rename from src/django_params_field/params_field.py rename to src/django_dict_field/params_field.py diff --git a/src/django_params_field/serializer.py b/src/django_dict_field/serializer.py similarity index 100% rename from src/django_params_field/serializer.py rename to src/django_dict_field/serializer.py From 763d831db8f997ecdaa86d40562ddeb9f62ac7ad Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Thu, 18 Sep 2025 01:03:27 +0300 Subject: [PATCH 04/20] Add msgpack for working with dict field. --- poetry.lock | 71 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 +- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5a6ec94..6e0fff1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -225,6 +225,75 @@ files = [ {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] +[[package]] +name = "msgpack" +version = "1.1.1" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "msgpack-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:353b6fc0c36fde68b661a12949d7d49f8f51ff5fa019c1e47c87c4ff34b080ed"}, + {file = "msgpack-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:79c408fcf76a958491b4e3b103d1c417044544b68e96d06432a189b43d1215c8"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78426096939c2c7482bf31ef15ca219a9e24460289c00dd0b94411040bb73ad2"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b17ba27727a36cb73aabacaa44b13090feb88a01d012c0f4be70c00f75048b4"}, + {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a17ac1ea6ec3c7687d70201cfda3b1e8061466f28f686c24f627cae4ea8efd0"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:88d1e966c9235c1d4e2afac21ca83933ba59537e2e2727a999bf3f515ca2af26"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f6d58656842e1b2ddbe07f43f56b10a60f2ba5826164910968f5933e5178af75"}, + {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96decdfc4adcbc087f5ea7ebdcfd3dee9a13358cae6e81d54be962efc38f6338"}, + {file = "msgpack-1.1.1-cp310-cp310-win32.whl", hash = "sha256:6640fd979ca9a212e4bcdf6eb74051ade2c690b862b679bfcb60ae46e6dc4bfd"}, + {file = "msgpack-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:8b65b53204fe1bd037c40c4148d00ef918eb2108d24c9aaa20bc31f9810ce0a8"}, + {file = "msgpack-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:71ef05c1726884e44f8b1d1773604ab5d4d17729d8491403a705e649116c9558"}, + {file = "msgpack-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:36043272c6aede309d29d56851f8841ba907a1a3d04435e43e8a19928e243c1d"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a32747b1b39c3ac27d0670122b57e6e57f28eefb725e0b625618d1b59bf9d1e0"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a8b10fdb84a43e50d38057b06901ec9da52baac6983d3f709d8507f3889d43f"}, + {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0c325c3f485dc54ec298d8b024e134acf07c10d494ffa24373bea729acf704"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:88daaf7d146e48ec71212ce21109b66e06a98e5e44dca47d853cbfe171d6c8d2"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8b55ea20dc59b181d3f47103f113e6f28a5e1c89fd5b67b9140edb442ab67f2"}, + {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a28e8072ae9779f20427af07f53bbb8b4aa81151054e882aee333b158da8752"}, + {file = "msgpack-1.1.1-cp311-cp311-win32.whl", hash = "sha256:7da8831f9a0fdb526621ba09a281fadc58ea12701bc709e7b8cbc362feabc295"}, + {file = "msgpack-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fd1b58e1431008a57247d6e7cc4faa41c3607e8e7d4aaf81f7c29ea013cb458"}, + {file = "msgpack-1.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae497b11f4c21558d95de9f64fff7053544f4d1a17731c866143ed6bb4591238"}, + {file = "msgpack-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33be9ab121df9b6b461ff91baac6f2731f83d9b27ed948c5b9d1978ae28bf157"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f64ae8fe7ffba251fecb8408540c34ee9df1c26674c50c4544d72dbf792e5ce"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a494554874691720ba5891c9b0b39474ba43ffb1aaf32a5dac874effb1619e1a"}, + {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb643284ab0ed26f6957d969fe0dd8bb17beb567beb8998140b5e38a90974f6c"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d275a9e3c81b1093c060c3837e580c37f47c51eca031f7b5fb76f7b8470f5f9b"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fd6b577e4541676e0cc9ddc1709d25014d3ad9a66caa19962c4f5de30fc09ef"}, + {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb29aaa613c0a1c40d1af111abf025f1732cab333f96f285d6a93b934738a68a"}, + {file = "msgpack-1.1.1-cp312-cp312-win32.whl", hash = "sha256:870b9a626280c86cff9c576ec0d9cbcc54a1e5ebda9cd26dab12baf41fee218c"}, + {file = "msgpack-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:5692095123007180dca3e788bb4c399cc26626da51629a31d40207cb262e67f4"}, + {file = "msgpack-1.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3765afa6bd4832fc11c3749be4ba4b69a0e8d7b728f78e68120a157a4c5d41f0"}, + {file = "msgpack-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8ddb2bcfd1a8b9e431c8d6f4f7db0773084e107730ecf3472f1dfe9ad583f3d9"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:196a736f0526a03653d829d7d4c5500a97eea3648aebfd4b6743875f28aa2af8"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d592d06e3cc2f537ceeeb23d38799c6ad83255289bb84c2e5792e5a8dea268a"}, + {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4df2311b0ce24f06ba253fda361f938dfecd7b961576f9be3f3fbd60e87130ac"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e4141c5a32b5e37905b5940aacbc59739f036930367d7acce7a64e4dec1f5e0b"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b1ce7f41670c5a69e1389420436f41385b1aa2504c3b0c30620764b15dded2e7"}, + {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4147151acabb9caed4e474c3344181e91ff7a388b888f1e19ea04f7e73dc7ad5"}, + {file = "msgpack-1.1.1-cp313-cp313-win32.whl", hash = "sha256:500e85823a27d6d9bba1d057c871b4210c1dd6fb01fbb764e37e4e8847376323"}, + {file = "msgpack-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:6d489fba546295983abd142812bda76b57e33d0b9f5d5b71c09a583285506f69"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bba1be28247e68994355e028dcd668316db30c1f758d3241a7b903ac78dcd285"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8f93dcddb243159c9e4109c9750ba5b335ab8d48d9522c5308cd05d7e3ce600"}, + {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fbbc0b906a24038c9958a1ba7ae0918ad35b06cb449d398b76a7d08470b0ed9"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:61e35a55a546a1690d9d09effaa436c25ae6130573b6ee9829c37ef0f18d5e78"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:1abfc6e949b352dadf4bce0eb78023212ec5ac42f6abfd469ce91d783c149c2a"}, + {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:996f2609ddf0142daba4cefd767d6db26958aac8439ee41db9cc0db9f4c4c3a6"}, + {file = "msgpack-1.1.1-cp38-cp38-win32.whl", hash = "sha256:4d3237b224b930d58e9d83c81c0dba7aacc20fcc2f89c1e5423aa0529a4cd142"}, + {file = "msgpack-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:da8f41e602574ece93dbbda1fab24650d6bf2a24089f9e9dbb4f5730ec1e58ad"}, + {file = "msgpack-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5be6b6bc52fad84d010cb45433720327ce886009d862f46b26d4d154001994b"}, + {file = "msgpack-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a89cd8c087ea67e64844287ea52888239cbd2940884eafd2dcd25754fb72232"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d75f3807a9900a7d575d8d6674a3a47e9f227e8716256f35bc6f03fc597ffbf"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d182dac0221eb8faef2e6f44701812b467c02674a322c739355c39e94730cdbf"}, + {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b13fe0fb4aac1aa5320cd693b297fe6fdef0e7bea5518cbc2dd5299f873ae90"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:435807eeb1bc791ceb3247d13c79868deb22184e1fc4224808750f0d7d1affc1"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4835d17af722609a45e16037bb1d4d78b7bdf19d6c0128116d178956618c4e88"}, + {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8ef6e342c137888ebbfb233e02b8fbd689bb5b5fcc59b34711ac47ebd504478"}, + {file = "msgpack-1.1.1-cp39-cp39-win32.whl", hash = "sha256:61abccf9de335d9efd149e2fff97ed5974f2481b3353772e8e2dd3402ba2bd57"}, + {file = "msgpack-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:40eae974c873b2992fd36424a5d9407f93e97656d999f43fca9d29f820899084"}, + {file = "msgpack-1.1.1.tar.gz", hash = "sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd"}, +] + [[package]] name = "mypy" version = "1.15.0" @@ -530,4 +599,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "c34a39280e7b88ab72f7f3f9838eb82c981d7ebe6863630093d2410559182e73" +content-hash = "4280bb8893bab4661c07afb8411375a9483f37a0a717ac539226bfb6c3673d86" diff --git a/pyproject.toml b/pyproject.toml index fde7a8c..0eaecff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ authors = [{ name = "skv0zsneg", email = "" }] readme = "README.md" requires-python = ">=3.9" -dependencies = ["django>=4.2.0"] +dependencies = ["django>=4.2.0", "msgpack (>=1.1.1,<2.0.0)"] [tool.poetry.group.dev.dependencies] pytest = "^8.3.5" From 275f028e5bb15b7df75d786d8a139cb46e91177a Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Thu, 18 Sep 2025 01:04:07 +0300 Subject: [PATCH 05/20] Rename param field to dict field. --- src/django_dict_field/__init__.py | 4 ++-- src/django_dict_field/params_field.py | 29 ++++++++++++++------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/django_dict_field/__init__.py b/src/django_dict_field/__init__.py index a73bbaf..7c2e2fb 100644 --- a/src/django_dict_field/__init__.py +++ b/src/django_dict_field/__init__.py @@ -1,3 +1,3 @@ -from .params_field import ParamsField +from .params_field import DictField -__all__ = ("ParamsField",) +__all__ = ("DictField",) diff --git a/src/django_dict_field/params_field.py b/src/django_dict_field/params_field.py index 205713b..aee2407 100644 --- a/src/django_dict_field/params_field.py +++ b/src/django_dict_field/params_field.py @@ -1,33 +1,34 @@ -from typing import Any, Generic, Optional +from typing import Any from django.core.exceptions import ValidationError from django.db.models.fields import BinaryField -from django_params_field.serializer import P, Serializer +from django_dict_field.serializer import Serializer -class ParamsField(BinaryField, Generic[P]): +class DictField(BinaryField): """Storing a set of params in one field.""" - def __init__(self, params_type: Optional[P] = None, *args, **kwargs) -> None: - self.params_type = params_type - self.serializer = Serializer(self.params_type) + def __init__(self, *args, **kwargs) -> None: + self.serializer = Serializer() super().__init__(*args, **kwargs) def deconstruct(self) -> tuple[Any, Any, Any, Any]: name, path, args, kwargs = super().deconstruct() - kwargs["params_type"] = self.params_type return name, path, args, kwargs - def from_db_value(self, value: bytes, *args, **kwargs) -> P: + def from_db_value(self, value: bytes, *args, **kwargs) -> dict: return self.serializer.deserialize(value) - def to_python(self, value: P) -> P: + def to_python(self, value: dict) -> dict: + if not isinstance(value, dict): + raise ValidationError(f"Given value '{value}' must be 'dict' instance!") return value - def get_db_prep_value(self, value: P, *args, **kwargs) -> bytes: - return self.serializer.serialize(value) + def get_db_prep_value(self, value: dict, *args, **kwargs) -> bytes: + if value is not None: + return self.serializer.serialize(value) + return value - def validate(self, value: P, *args, **kwargs) -> None: - if not self.serializer.is_valid(value): - raise ValidationError(f"Given value '{value}' is not valid!") + def value_to_string(self, obj): + return str(self.value_from_object(obj)) From 1dfaf999ba9aea98c0b92d0c9ec4b7a515e4e203 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Thu, 18 Sep 2025 01:04:30 +0300 Subject: [PATCH 06/20] Refactor serializer for using msgpack. --- src/django_dict_field/serializer.py | 32 ++++++----------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/src/django_dict_field/serializer.py b/src/django_dict_field/serializer.py index 5ef4751..4bf15b0 100644 --- a/src/django_dict_field/serializer.py +++ b/src/django_dict_field/serializer.py @@ -1,31 +1,11 @@ -import bz2 -import pickle -from typing import Any, ClassVar, Generic, Optional, TypeVar +import msgpack -P = TypeVar("P") - -class Serializer(Generic[P]): +class Serializer: """Params serializer & deserializer.""" - ALLOWED_TYPES: ClassVar[tuple[Any, ...]] = (dict,) - - def __init__(self, params_type: Optional[P] = None) -> None: - self.params_type = params_type - - def serialize(self, value: P) -> bytes: - serialized = pickle.dumps(value) - compressed = bz2.compress(serialized) - return compressed - - def deserialize(self, value: bytes) -> P: - decompressed = bz2.decompress(value) - deserialized = pickle.loads(decompressed) - return deserialized + def serialize(self, value: dict) -> bytes: + return msgpack.packb(value) - def is_valid(self, value: P) -> bool: - return any( - map( - lambda allowed_type: isinstance(value, allowed_type), self.ALLOWED_TYPES - ) - ) + def deserialize(self, value: bytes) -> dict: + return msgpack.unpackb(value) From 432307bf9eaa6335adf8ee1561a3d04d57c39c1d Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Thu, 18 Sep 2025 01:04:54 +0300 Subject: [PATCH 07/20] Upd test model. --- .../params_field/migrations/0001_initial.py | 11 ++++------- tests/django_app/params_field/models.py | 6 ++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/django_app/params_field/migrations/0001_initial.py b/tests/django_app/params_field/migrations/0001_initial.py index 181b02d..7f070b7 100644 --- a/tests/django_app/params_field/migrations/0001_initial.py +++ b/tests/django_app/params_field/migrations/0001_initial.py @@ -1,11 +1,11 @@ -# Generated by Django 5.2.1 on 2025-05-30 20:21 +# Generated by Django 4.2.22 on 2025-09-17 21:36 from django.db import migrations, models - -import django_params_field.params_field +import django_dict_field.params_field class Migration(migrations.Migration): + initial = True dependencies = [] @@ -23,10 +23,7 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ( - "params_field_default", - django_params_field.params_field.ParamsField(params_type=None), - ), + ("default_dict_field", django_dict_field.params_field.DictField()), ], ), ] diff --git a/tests/django_app/params_field/models.py b/tests/django_app/params_field/models.py index a887e4f..610d5d5 100644 --- a/tests/django_app/params_field/models.py +++ b/tests/django_app/params_field/models.py @@ -1,9 +1,7 @@ from django.db import models -from django_params_field import ParamsField +from django_dict_field import DictField class ModelForTest(models.Model): - """Simple model using for test ParamsField.""" - - params_field_default = ParamsField() + default_dict_field = DictField() From eb90ed3644d3cc30c27703c18e78482a1362d892 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Thu, 18 Sep 2025 01:05:11 +0300 Subject: [PATCH 08/20] Update tests. --- tests/django_app/params_field/tests.py | 102 ++++++++++++++++--------- 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/tests/django_app/params_field/tests.py b/tests/django_app/params_field/tests.py index 1e2fe24..b837b58 100644 --- a/tests/django_app/params_field/tests.py +++ b/tests/django_app/params_field/tests.py @@ -1,60 +1,88 @@ import pytest from django.core.exceptions import ValidationError +from django.db.utils import IntegrityError from .models import ModelForTest @pytest.mark.django_db -def test_model_full_clean_with_correct_value(): - """Test model `clean()` params_field_default.""" - test_model = ModelForTest.objects.create(params_field_default={"just": "test"}) - # check that no exception raised - assert test_model.full_clean() is None +def test_empty_dict(): + """Test saving and retrieving an empty dict.""" + test_model = ModelForTest.objects.create(default_dict_field={}) + test_model.refresh_from_db() + assert test_model.default_dict_field == {} @pytest.mark.django_db -def test_model_full_clean_with_bas_value(): - """Test model `clean()` params_field_default.""" - test_model = ModelForTest.objects.create(params_field_default="I'm Not Valid!") - with pytest.raises(ValidationError): - test_model.full_clean() +def test_nested_dict(): + """Test saving and retrieving a nested dictionary.""" + nested_value = {"level1": {"level2": {"key": "value"}}} + test_model = ModelForTest.objects.create(default_dict_field=nested_value) + test_model.refresh_from_db() + assert test_model.default_dict_field == nested_value @pytest.mark.django_db -@pytest.mark.parametrize("dict_size", [1000, 10_000, 100_000, 1_000_000]) -def test_set_and_get_params_field_default(dict_size): - """Test set and get params fields different size with default ``params_type``.""" - test_model = ModelForTest.objects.create() - simple_data_struct = {i: i for i in range(dict_size)} - test_model.params_field_default = simple_data_struct - test_model.save() +def test_dict_with_various_types(): + """Test dict with various standard types (str, int, float, list, bool).""" + value = { + "string": "text", + "integer": 42, + "float": 3.14, + "boolean": True, + "list": [1, 2, 3], + "null": None, + } + test_model = ModelForTest.objects.create(default_dict_field=value) + test_model.refresh_from_db() + assert test_model.default_dict_field == value - test_model_from_query = ModelForTest.objects.get(pk=test_model.pk) - assert test_model_from_query.params_field_default == simple_data_struct + +@pytest.mark.django_db +def test_invalid_type_raises_validation_error(): + """Test that non-dict value raises ValidationError.""" + model_1 = ModelForTest.objects.create(default_dict_field="not a dict") + with pytest.raises(ValidationError): + model_1.full_clean() + + model_2 = ModelForTest.objects.create(default_dict_field=123) + with pytest.raises(ValidationError): + model_2.full_clean() @pytest.mark.django_db -@pytest.mark.parametrize("dict_size", [1000, 10_000, 100_000, 1_000_000]) -def test_update_params_field_default(dict_size): - """Test update params fields different size with default ``params_type``.""" - simple_data_struct = {i: i for i in range(dict_size)} - test_model = ModelForTest.objects.create(params_field_default=simple_data_struct) +def test_none_value_handling(): + """Test behavior when None is passed.""" + # Depending on your field settings (null=True or not), this may raise an error + # Assuming the field is not nullable by default + with pytest.raises(IntegrityError): + ModelForTest.objects.create(default_dict_field=None) - test_model.params_field_default[0] = "test-change-value" - test_model.save() - test_model_from_query = ModelForTest.objects.get(pk=test_model.pk) - assert test_model_from_query.params_field_default[0] == "test-change-value" + +@pytest.mark.django_db +def test_field_serialization_deserialization_consistency(): + """Ensure that serialization and deserialization are consistent.""" + original = {"key": "value", "nested": {"a": 1}} + test_model = ModelForTest.objects.create(default_dict_field=original) + test_model.refresh_from_db() + assert test_model.default_dict_field == original @pytest.mark.django_db -@pytest.mark.parametrize("dict_size", [1000, 10_000, 100_000, 1_000_000]) -def test_delete_params_field_default(dict_size): - """Test delete params fields different size with default ``params_type``.""" - simple_data_struct = {i: i for i in range(dict_size)} - test_model = ModelForTest.objects.create(params_field_default=simple_data_struct) +def test_modifying_dict_in_place(): + """Test that in-place modifications are saved correctly.""" + test_model = ModelForTest.objects.create(default_dict_field={"a": 1}) + test_model.default_dict_field["b"] = 2 + test_model.save() + test_model.refresh_from_db() + assert test_model.default_dict_field == {"a": 1, "b": 2} + - keys_num_before = len(test_model.params_field_default.keys()) - del test_model.params_field_default[1] +@pytest.mark.django_db +def test_clear_dict(): + """Test clearing the dictionary.""" + test_model = ModelForTest.objects.create(default_dict_field={"a": 1}) + test_model.default_dict_field.clear() test_model.save() - test_model_from_query = ModelForTest.objects.get(pk=test_model.pk) - assert len(test_model_from_query.params_field_default.keys()) == keys_num_before - 1 + test_model.refresh_from_db() + assert test_model.default_dict_field == {} From 1d097b6beffc5ec7737db92c45ceffcb5479942e Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Sat, 20 Sep 2025 22:30:16 +0300 Subject: [PATCH 09/20] Add bechmark script. --- Makefile | 4 ++ benchmarks/__init__.py | 0 benchmarks/jsonfield_benchmark.py | 62 +++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 benchmarks/__init__.py create mode 100644 benchmarks/jsonfield_benchmark.py diff --git a/Makefile b/Makefile index c019002..1c60819 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,10 @@ SHELL:=/usr/bin/env bash unit: poetry run pytest +.PHONY: benchmark +benchmark: + poetry run python3 benchmarks/jsonfield_benchmark.py + .PHONY: typing typing: poetry run mypy src diff --git a/benchmarks/__init__.py b/benchmarks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/jsonfield_benchmark.py b/benchmarks/jsonfield_benchmark.py new file mode 100644 index 0000000..2113338 --- /dev/null +++ b/benchmarks/jsonfield_benchmark.py @@ -0,0 +1,62 @@ +import os +import random +import sys +import timeit + +import django +from tabulate import tabulate + +project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +django_app_path = os.path.join(project_root, 'tests', 'django_app') + +sys.path.insert(0, project_root) +sys.path.insert(0, django_app_path) + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_app.settings') + +# Инициализируем Django +django.setup() + +from dict_field.models import ModelForTest + +DICT_SIZE = 100_000 +BIG_DICT = {str(i): i for i in range(DICT_SIZE)} + + +def crud_on_big_dict_field(test_model: ModelForTest, field_name: str) -> None: + """Function for testing CRUD operations for `DictFiled` and `JSONField`.""" + rand_index = str(random.randint(0, DICT_SIZE - 1)) + # get + test_model.__getattribute__(field_name)[rand_index] + # delete + del test_model.__getattribute__(field_name)[rand_index] + test_model.save() + # update + test_model.__getattribute__(field_name)[rand_index] = "restored" + test_model.save() + + +if __name__ == "__main__": + test_model_jsonfield = ModelForTest.objects.create( + default_json_field=BIG_DICT, default_dict_field={} + ) + time_for_jsonfield = timeit.timeit( + lambda: crud_on_big_dict_field(test_model_jsonfield, "default_json_field"), + number=100, + ) + + test_model_dictfield = ModelForTest.objects.create( + default_json_field={}, default_dict_field=BIG_DICT + ) + time_for_dictfield = timeit.timeit( + lambda: crud_on_big_dict_field(test_model_dictfield, "default_dict_field"), + number=100, + ) + + report_table = tabulate( + [ + ["Time for JSONField", time_for_jsonfield], + ["Time for DictField", time_for_dictfield], + ] + ) + print(report_table) From 13f125dbe88d43f00b8cecadbb072d2d9a94d9aa Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Sat, 20 Sep 2025 22:30:50 +0300 Subject: [PATCH 10/20] Add bechmark deps. Replace msgpack with orjson. --- poetry.lock | 212 +++++++++++++++++++++++++++++++++---------------- pyproject.toml | 4 +- 2 files changed, 145 insertions(+), 71 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6e0fff1..fc8b32a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -225,75 +225,6 @@ files = [ {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] -[[package]] -name = "msgpack" -version = "1.1.1" -description = "MessagePack serializer" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "msgpack-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:353b6fc0c36fde68b661a12949d7d49f8f51ff5fa019c1e47c87c4ff34b080ed"}, - {file = "msgpack-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:79c408fcf76a958491b4e3b103d1c417044544b68e96d06432a189b43d1215c8"}, - {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78426096939c2c7482bf31ef15ca219a9e24460289c00dd0b94411040bb73ad2"}, - {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b17ba27727a36cb73aabacaa44b13090feb88a01d012c0f4be70c00f75048b4"}, - {file = "msgpack-1.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a17ac1ea6ec3c7687d70201cfda3b1e8061466f28f686c24f627cae4ea8efd0"}, - {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:88d1e966c9235c1d4e2afac21ca83933ba59537e2e2727a999bf3f515ca2af26"}, - {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f6d58656842e1b2ddbe07f43f56b10a60f2ba5826164910968f5933e5178af75"}, - {file = "msgpack-1.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96decdfc4adcbc087f5ea7ebdcfd3dee9a13358cae6e81d54be962efc38f6338"}, - {file = "msgpack-1.1.1-cp310-cp310-win32.whl", hash = "sha256:6640fd979ca9a212e4bcdf6eb74051ade2c690b862b679bfcb60ae46e6dc4bfd"}, - {file = "msgpack-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:8b65b53204fe1bd037c40c4148d00ef918eb2108d24c9aaa20bc31f9810ce0a8"}, - {file = "msgpack-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:71ef05c1726884e44f8b1d1773604ab5d4d17729d8491403a705e649116c9558"}, - {file = "msgpack-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:36043272c6aede309d29d56851f8841ba907a1a3d04435e43e8a19928e243c1d"}, - {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a32747b1b39c3ac27d0670122b57e6e57f28eefb725e0b625618d1b59bf9d1e0"}, - {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a8b10fdb84a43e50d38057b06901ec9da52baac6983d3f709d8507f3889d43f"}, - {file = "msgpack-1.1.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0c325c3f485dc54ec298d8b024e134acf07c10d494ffa24373bea729acf704"}, - {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:88daaf7d146e48ec71212ce21109b66e06a98e5e44dca47d853cbfe171d6c8d2"}, - {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8b55ea20dc59b181d3f47103f113e6f28a5e1c89fd5b67b9140edb442ab67f2"}, - {file = "msgpack-1.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a28e8072ae9779f20427af07f53bbb8b4aa81151054e882aee333b158da8752"}, - {file = "msgpack-1.1.1-cp311-cp311-win32.whl", hash = "sha256:7da8831f9a0fdb526621ba09a281fadc58ea12701bc709e7b8cbc362feabc295"}, - {file = "msgpack-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fd1b58e1431008a57247d6e7cc4faa41c3607e8e7d4aaf81f7c29ea013cb458"}, - {file = "msgpack-1.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae497b11f4c21558d95de9f64fff7053544f4d1a17731c866143ed6bb4591238"}, - {file = "msgpack-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33be9ab121df9b6b461ff91baac6f2731f83d9b27ed948c5b9d1978ae28bf157"}, - {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f64ae8fe7ffba251fecb8408540c34ee9df1c26674c50c4544d72dbf792e5ce"}, - {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a494554874691720ba5891c9b0b39474ba43ffb1aaf32a5dac874effb1619e1a"}, - {file = "msgpack-1.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb643284ab0ed26f6957d969fe0dd8bb17beb567beb8998140b5e38a90974f6c"}, - {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d275a9e3c81b1093c060c3837e580c37f47c51eca031f7b5fb76f7b8470f5f9b"}, - {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fd6b577e4541676e0cc9ddc1709d25014d3ad9a66caa19962c4f5de30fc09ef"}, - {file = "msgpack-1.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb29aaa613c0a1c40d1af111abf025f1732cab333f96f285d6a93b934738a68a"}, - {file = "msgpack-1.1.1-cp312-cp312-win32.whl", hash = "sha256:870b9a626280c86cff9c576ec0d9cbcc54a1e5ebda9cd26dab12baf41fee218c"}, - {file = "msgpack-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:5692095123007180dca3e788bb4c399cc26626da51629a31d40207cb262e67f4"}, - {file = "msgpack-1.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3765afa6bd4832fc11c3749be4ba4b69a0e8d7b728f78e68120a157a4c5d41f0"}, - {file = "msgpack-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8ddb2bcfd1a8b9e431c8d6f4f7db0773084e107730ecf3472f1dfe9ad583f3d9"}, - {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:196a736f0526a03653d829d7d4c5500a97eea3648aebfd4b6743875f28aa2af8"}, - {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d592d06e3cc2f537ceeeb23d38799c6ad83255289bb84c2e5792e5a8dea268a"}, - {file = "msgpack-1.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4df2311b0ce24f06ba253fda361f938dfecd7b961576f9be3f3fbd60e87130ac"}, - {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e4141c5a32b5e37905b5940aacbc59739f036930367d7acce7a64e4dec1f5e0b"}, - {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b1ce7f41670c5a69e1389420436f41385b1aa2504c3b0c30620764b15dded2e7"}, - {file = "msgpack-1.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4147151acabb9caed4e474c3344181e91ff7a388b888f1e19ea04f7e73dc7ad5"}, - {file = "msgpack-1.1.1-cp313-cp313-win32.whl", hash = "sha256:500e85823a27d6d9bba1d057c871b4210c1dd6fb01fbb764e37e4e8847376323"}, - {file = "msgpack-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:6d489fba546295983abd142812bda76b57e33d0b9f5d5b71c09a583285506f69"}, - {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bba1be28247e68994355e028dcd668316db30c1f758d3241a7b903ac78dcd285"}, - {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8f93dcddb243159c9e4109c9750ba5b335ab8d48d9522c5308cd05d7e3ce600"}, - {file = "msgpack-1.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fbbc0b906a24038c9958a1ba7ae0918ad35b06cb449d398b76a7d08470b0ed9"}, - {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:61e35a55a546a1690d9d09effaa436c25ae6130573b6ee9829c37ef0f18d5e78"}, - {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:1abfc6e949b352dadf4bce0eb78023212ec5ac42f6abfd469ce91d783c149c2a"}, - {file = "msgpack-1.1.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:996f2609ddf0142daba4cefd767d6db26958aac8439ee41db9cc0db9f4c4c3a6"}, - {file = "msgpack-1.1.1-cp38-cp38-win32.whl", hash = "sha256:4d3237b224b930d58e9d83c81c0dba7aacc20fcc2f89c1e5423aa0529a4cd142"}, - {file = "msgpack-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:da8f41e602574ece93dbbda1fab24650d6bf2a24089f9e9dbb4f5730ec1e58ad"}, - {file = "msgpack-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5be6b6bc52fad84d010cb45433720327ce886009d862f46b26d4d154001994b"}, - {file = "msgpack-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a89cd8c087ea67e64844287ea52888239cbd2940884eafd2dcd25754fb72232"}, - {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d75f3807a9900a7d575d8d6674a3a47e9f227e8716256f35bc6f03fc597ffbf"}, - {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d182dac0221eb8faef2e6f44701812b467c02674a322c739355c39e94730cdbf"}, - {file = "msgpack-1.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b13fe0fb4aac1aa5320cd693b297fe6fdef0e7bea5518cbc2dd5299f873ae90"}, - {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:435807eeb1bc791ceb3247d13c79868deb22184e1fc4224808750f0d7d1affc1"}, - {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4835d17af722609a45e16037bb1d4d78b7bdf19d6c0128116d178956618c4e88"}, - {file = "msgpack-1.1.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8ef6e342c137888ebbfb233e02b8fbd689bb5b5fcc59b34711ac47ebd504478"}, - {file = "msgpack-1.1.1-cp39-cp39-win32.whl", hash = "sha256:61abccf9de335d9efd149e2fff97ed5974f2481b3353772e8e2dd3402ba2bd57"}, - {file = "msgpack-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:40eae974c873b2992fd36424a5d9407f93e97656d999f43fca9d29f820899084"}, - {file = "msgpack-1.1.1.tar.gz", hash = "sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd"}, -] - [[package]] name = "mypy" version = "1.15.0" @@ -360,6 +291,99 @@ files = [ {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] +[[package]] +name = "orjson" +version = "3.11.3" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "orjson-3.11.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:29cb1f1b008d936803e2da3d7cba726fc47232c45df531b29edf0b232dd737e7"}, + {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97dceed87ed9139884a55db8722428e27bd8452817fbf1869c58b49fecab1120"}, + {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:58533f9e8266cb0ac298e259ed7b4d42ed3fa0b78ce76860626164de49e0d467"}, + {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c212cfdd90512fe722fa9bd620de4d46cda691415be86b2e02243242ae81873"}, + {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff835b5d3e67d9207343effb03760c00335f8b5285bfceefd4dc967b0e48f6a"}, + {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5aa4682912a450c2db89cbd92d356fef47e115dffba07992555542f344d301b"}, + {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d18dd34ea2e860553a579df02041845dee0af8985dff7f8661306f95504ddf"}, + {file = "orjson-3.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8b11701bc43be92ea42bd454910437b355dfb63696c06fe953ffb40b5f763b4"}, + {file = "orjson-3.11.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90368277087d4af32d38bd55f9da2ff466d25325bf6167c8f382d8ee40cb2bbc"}, + {file = "orjson-3.11.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd7ff459fb393358d3a155d25b275c60b07a2c83dcd7ea962b1923f5a1134569"}, + {file = "orjson-3.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8d902867b699bcd09c176a280b1acdab57f924489033e53d0afe79817da37e6"}, + {file = "orjson-3.11.3-cp310-cp310-win32.whl", hash = "sha256:bb93562146120bb51e6b154962d3dadc678ed0fce96513fa6bc06599bb6f6edc"}, + {file = "orjson-3.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:976c6f1975032cc327161c65d4194c549f2589d88b105a5e3499429a54479770"}, + {file = "orjson-3.11.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d2ae0cc6aeb669633e0124531f342a17d8e97ea999e42f12a5ad4adaa304c5f"}, + {file = "orjson-3.11.3-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ba21dbb2493e9c653eaffdc38819b004b7b1b246fb77bfc93dc016fe664eac91"}, + {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f1a271e56d511d1569937c0447d7dce5a99a33ea0dec76673706360a051904"}, + {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b67e71e47caa6680d1b6f075a396d04fa6ca8ca09aafb428731da9b3ea32a5a6"}, + {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7d012ebddffcce8c85734a6d9e5f08180cd3857c5f5a3ac70185b43775d043d"}, + {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd759f75d6b8d1b62012b7f5ef9461d03c804f94d539a5515b454ba3a6588038"}, + {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6890ace0809627b0dff19cfad92d69d0fa3f089d3e359a2a532507bb6ba34efb"}, + {file = "orjson-3.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d4a5e041ae435b815e568537755773d05dac031fee6a57b4ba70897a44d9d2"}, + {file = "orjson-3.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d68bf97a771836687107abfca089743885fb664b90138d8761cce61d5625d55"}, + {file = "orjson-3.11.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfc27516ec46f4520b18ef645864cee168d2a027dbf32c5537cb1f3e3c22dac1"}, + {file = "orjson-3.11.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f66b001332a017d7945e177e282a40b6997056394e3ed7ddb41fb1813b83e824"}, + {file = "orjson-3.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:212e67806525d2561efbfe9e799633b17eb668b8964abed6b5319b2f1cfbae1f"}, + {file = "orjson-3.11.3-cp311-cp311-win32.whl", hash = "sha256:6e8e0c3b85575a32f2ffa59de455f85ce002b8bdc0662d6b9c2ed6d80ab5d204"}, + {file = "orjson-3.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:6be2f1b5d3dc99a5ce5ce162fc741c22ba9f3443d3dd586e6a1211b7bc87bc7b"}, + {file = "orjson-3.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:fafb1a99d740523d964b15c8db4eabbfc86ff29f84898262bf6e3e4c9e97e43e"}, + {file = "orjson-3.11.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8c752089db84333e36d754c4baf19c0e1437012242048439c7e80eb0e6426e3b"}, + {file = "orjson-3.11.3-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:9b8761b6cf04a856eb544acdd82fc594b978f12ac3602d6374a7edb9d86fd2c2"}, + {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b13974dc8ac6ba22feaa867fc19135a3e01a134b4f7c9c28162fed4d615008a"}, + {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f83abab5bacb76d9c821fd5c07728ff224ed0e52d7a71b7b3de822f3df04e15c"}, + {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6fbaf48a744b94091a56c62897b27c31ee2da93d826aa5b207131a1e13d4064"}, + {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc779b4f4bba2847d0d2940081a7b6f7b5877e05408ffbb74fa1faf4a136c424"}, + {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd4b909ce4c50faa2192da6bb684d9848d4510b736b0611b6ab4020ea6fd2d23"}, + {file = "orjson-3.11.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524b765ad888dc5518bbce12c77c2e83dee1ed6b0992c1790cc5fb49bb4b6667"}, + {file = "orjson-3.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:84fd82870b97ae3cdcea9d8746e592b6d40e1e4d4527835fc520c588d2ded04f"}, + {file = "orjson-3.11.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbecb9709111be913ae6879b07bafd4b0785b44c1eb5cac8ac76da048b3885a1"}, + {file = "orjson-3.11.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9dba358d55aee552bd868de348f4736ca5a4086d9a62e2bfbbeeb5629fe8b0cc"}, + {file = "orjson-3.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eabcf2e84f1d7105f84580e03012270c7e97ecb1fb1618bda395061b2a84a049"}, + {file = "orjson-3.11.3-cp312-cp312-win32.whl", hash = "sha256:3782d2c60b8116772aea8d9b7905221437fdf53e7277282e8d8b07c220f96cca"}, + {file = "orjson-3.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:79b44319268af2eaa3e315b92298de9a0067ade6e6003ddaef72f8e0bedb94f1"}, + {file = "orjson-3.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:0e92a4e83341ef79d835ca21b8bd13e27c859e4e9e4d7b63defc6e58462a3710"}, + {file = "orjson-3.11.3-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:af40c6612fd2a4b00de648aa26d18186cd1322330bd3a3cc52f87c699e995810"}, + {file = "orjson-3.11.3-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:9f1587f26c235894c09e8b5b7636a38091a9e6e7fe4531937534749c04face43"}, + {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61dcdad16da5bb486d7227a37a2e789c429397793a6955227cedbd7252eb5a27"}, + {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11c6d71478e2cbea0a709e8a06365fa63da81da6498a53e4c4f065881d21ae8f"}, + {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff94112e0098470b665cb0ed06efb187154b63649403b8d5e9aedeb482b4548c"}, + {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae8b756575aaa2a855a75192f356bbda11a89169830e1439cfb1a3e1a6dde7be"}, + {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9416cc19a349c167ef76135b2fe40d03cea93680428efee8771f3e9fb66079d"}, + {file = "orjson-3.11.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b822caf5b9752bc6f246eb08124c3d12bf2175b66ab74bac2ef3bbf9221ce1b2"}, + {file = "orjson-3.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:414f71e3bdd5573893bf5ecdf35c32b213ed20aa15536fe2f588f946c318824f"}, + {file = "orjson-3.11.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:828e3149ad8815dc14468f36ab2a4b819237c155ee1370341b91ea4c8672d2ee"}, + {file = "orjson-3.11.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac9e05f25627ffc714c21f8dfe3a579445a5c392a9c8ae7ba1d0e9fb5333f56e"}, + {file = "orjson-3.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e44fbe4000bd321d9f3b648ae46e0196d21577cf66ae684a96ff90b1f7c93633"}, + {file = "orjson-3.11.3-cp313-cp313-win32.whl", hash = "sha256:2039b7847ba3eec1f5886e75e6763a16e18c68a63efc4b029ddf994821e2e66b"}, + {file = "orjson-3.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:29be5ac4164aa8bdcba5fa0700a3c9c316b411d8ed9d39ef8a882541bd452fae"}, + {file = "orjson-3.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:18bd1435cb1f2857ceb59cfb7de6f92593ef7b831ccd1b9bfb28ca530e539dce"}, + {file = "orjson-3.11.3-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:cf4b81227ec86935568c7edd78352a92e97af8da7bd70bdfdaa0d2e0011a1ab4"}, + {file = "orjson-3.11.3-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:bc8bc85b81b6ac9fc4dae393a8c159b817f4c2c9dee5d12b773bddb3b95fc07e"}, + {file = "orjson-3.11.3-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:88dcfc514cfd1b0de038443c7b3e6a9797ffb1b3674ef1fd14f701a13397f82d"}, + {file = "orjson-3.11.3-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d61cd543d69715d5fc0a690c7c6f8dcc307bc23abef9738957981885f5f38229"}, + {file = "orjson-3.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2b7b153ed90ababadbef5c3eb39549f9476890d339cf47af563aea7e07db2451"}, + {file = "orjson-3.11.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7909ae2460f5f494fecbcd10613beafe40381fd0316e35d6acb5f3a05bfda167"}, + {file = "orjson-3.11.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2030c01cbf77bc67bee7eef1e7e31ecf28649353987775e3583062c752da0077"}, + {file = "orjson-3.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a0169ebd1cbd94b26c7a7ad282cf5c2744fce054133f959e02eb5265deae1872"}, + {file = "orjson-3.11.3-cp314-cp314-win32.whl", hash = "sha256:0c6d7328c200c349e3a4c6d8c83e0a5ad029bdc2d417f234152bf34842d0fc8d"}, + {file = "orjson-3.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:317bbe2c069bbc757b1a2e4105b64aacd3bc78279b66a6b9e51e846e4809f804"}, + {file = "orjson-3.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:e8f6a7a27d7b7bec81bd5924163e9af03d49bbb63013f107b48eb5d16db711bc"}, + {file = "orjson-3.11.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:56afaf1e9b02302ba636151cfc49929c1bb66b98794291afd0e5f20fecaf757c"}, + {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:913f629adef31d2d350d41c051ce7e33cf0fd06a5d1cb28d49b1899b23b903aa"}, + {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0a23b41f8f98b4e61150a03f83e4f0d566880fe53519d445a962929a4d21045"}, + {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d721fee37380a44f9d9ce6c701b3960239f4fb3d5ceea7f31cbd43882edaa2f"}, + {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73b92a5b69f31b1a58c0c7e31080aeaec49c6e01b9522e71ff38d08f15aa56de"}, + {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2489b241c19582b3f1430cc5d732caefc1aaf378d97e7fb95b9e56bed11725f"}, + {file = "orjson-3.11.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5189a5dab8b0312eadaf9d58d3049b6a52c454256493a557405e77a3d67ab7f"}, + {file = "orjson-3.11.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9d8787bdfbb65a85ea76d0e96a3b1bed7bf0fbcb16d40408dc1172ad784a49d2"}, + {file = "orjson-3.11.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:8e531abd745f51f8035e207e75e049553a86823d189a51809c078412cefb399a"}, + {file = "orjson-3.11.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8ab962931015f170b97a3dd7bd933399c1bae8ed8ad0fb2a7151a5654b6941c7"}, + {file = "orjson-3.11.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:124d5ba71fee9c9902c4a7baa9425e663f7f0aecf73d31d54fe3dd357d62c1a7"}, + {file = "orjson-3.11.3-cp39-cp39-win32.whl", hash = "sha256:22724d80ee5a815a44fc76274bb7ba2e7464f5564aacb6ecddaa9970a83e3225"}, + {file = "orjson-3.11.3-cp39-cp39-win_amd64.whl", hash = "sha256:215c595c792a87d4407cb72dd5e0f6ee8e694ceeb7f9102b533c5a9bf2a916bb"}, + {file = "orjson-3.11.3.tar.gz", hash = "sha256:1c0603b1d2ffcd43a411d64797a19556ef76958aef1c182f22dc30860152a98a"}, +] + [[package]] name = "packaging" version = "25.0" @@ -388,6 +412,18 @@ files = [ dev = ["pre-commit", "tox"] testing = ["coverage", "pytest", "pytest-benchmark"] +[[package]] +name = "py-cpuinfo" +version = "9.0.0" +description = "Get CPU info with pure Python" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, + {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, +] + [[package]] name = "pytest" version = "8.3.5" @@ -411,6 +447,27 @@ tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-benchmark" +version = "5.1.0" +description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-benchmark-5.1.0.tar.gz", hash = "sha256:9ea661cdc292e8231f7cd4c10b0319e56a2118e2c09d9f50e1b3d150d2aca105"}, + {file = "pytest_benchmark-5.1.0-py3-none-any.whl", hash = "sha256:922de2dfa3033c227c96da942d1878191afa135a29485fb942e85dff1c592c89"}, +] + +[package.dependencies] +py-cpuinfo = "*" +pytest = ">=8.1" + +[package.extras] +aspect = ["aspectlib"] +elasticsearch = ["elasticsearch"] +histogram = ["pygal", "pygaljs", "setuptools"] + [[package]] name = "pytest-cov" version = "6.1.1" @@ -514,6 +571,21 @@ files = [ dev = ["build", "hatch"] doc = ["sphinx"] +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + [[package]] name = "tomli" version = "2.2.1" @@ -599,4 +671,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "4280bb8893bab4661c07afb8411375a9483f37a0a717ac539226bfb6c3673d86" +content-hash = "547fbfe9f6315b151f4e8a06636e50e39ed6a72161e3abe268025424aebd19fb" diff --git a/pyproject.toml b/pyproject.toml index 0eaecff..3d04d79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ authors = [{ name = "skv0zsneg", email = "" }] readme = "README.md" requires-python = ">=3.9" -dependencies = ["django>=4.2.0", "msgpack (>=1.1.1,<2.0.0)"] +dependencies = ["django>=4.2.0", "orjson (>=3.11.3,<4.0.0)"] [tool.poetry.group.dev.dependencies] pytest = "^8.3.5" @@ -16,6 +16,8 @@ pytest-cov = "^6.1.1" pytest-xdist = "^3.7.0" django-stubs = { extras = ["compatible-mypy"], version = "^5.2.0", markers = "python_version >= '3.10'" } ruff = "^0.11.12" +pytest-benchmark = "^5.1.0" +tabulate = "^0.9.0" [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE = "django_app.settings" From d0a8c4a43a0960851dc6bd267013e3fc6ee9d3ae Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Sat, 20 Sep 2025 22:31:07 +0300 Subject: [PATCH 11/20] Replace msgpack with orjson. --- src/django_dict_field/serializer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/django_dict_field/serializer.py b/src/django_dict_field/serializer.py index 4bf15b0..dd33f28 100644 --- a/src/django_dict_field/serializer.py +++ b/src/django_dict_field/serializer.py @@ -1,11 +1,11 @@ -import msgpack +import orjson class Serializer: """Params serializer & deserializer.""" def serialize(self, value: dict) -> bytes: - return msgpack.packb(value) + return orjson.dumps(value) def deserialize(self, value: bytes) -> dict: - return msgpack.unpackb(value) + return orjson.loads(value) From 3876034159d042ff91534d87e188b1b4f2de16d5 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Sat, 20 Sep 2025 22:32:46 +0300 Subject: [PATCH 12/20] Change naming: params field to dict field. --- src/django_dict_field/__init__.py | 2 +- .../{params_field.py => dict_field.py} | 19 ++++++++----------- .../{params_field => dict_field}/__init__.py | 0 .../{params_field => dict_field}/apps.py | 4 ++-- .../migrations/0001_initial.py | 8 +++++--- .../migrations/__init__.py | 0 .../{params_field => dict_field}/models.py | 2 ++ tests/django_app/dict_field/tests/__init__.py | 0 .../tests/test_default_dict_field.py} | 2 +- tests/django_app/django_app/settings.py | 2 +- 10 files changed, 20 insertions(+), 19 deletions(-) rename src/django_dict_field/{params_field.py => dict_field.py} (62%) rename tests/django_app/{params_field => dict_field}/__init__.py (100%) rename tests/django_app/{params_field => dict_field}/apps.py (60%) rename tests/django_app/{params_field => dict_field}/migrations/0001_initial.py (70%) rename tests/django_app/{params_field => dict_field}/migrations/__init__.py (100%) rename tests/django_app/{params_field => dict_field}/models.py (59%) create mode 100644 tests/django_app/dict_field/tests/__init__.py rename tests/django_app/{params_field/tests.py => dict_field/tests/test_default_dict_field.py} (98%) diff --git a/src/django_dict_field/__init__.py b/src/django_dict_field/__init__.py index 7c2e2fb..1331120 100644 --- a/src/django_dict_field/__init__.py +++ b/src/django_dict_field/__init__.py @@ -1,3 +1,3 @@ -from .params_field import DictField +from .dict_field import DictField __all__ = ("DictField",) diff --git a/src/django_dict_field/params_field.py b/src/django_dict_field/dict_field.py similarity index 62% rename from src/django_dict_field/params_field.py rename to src/django_dict_field/dict_field.py index aee2407..e1873f5 100644 --- a/src/django_dict_field/params_field.py +++ b/src/django_dict_field/dict_field.py @@ -1,5 +1,3 @@ -from typing import Any - from django.core.exceptions import ValidationError from django.db.models.fields import BinaryField @@ -11,13 +9,12 @@ class DictField(BinaryField): def __init__(self, *args, **kwargs) -> None: self.serializer = Serializer() + kwargs.setdefault('editable', False) super().__init__(*args, **kwargs) - def deconstruct(self) -> tuple[Any, Any, Any, Any]: - name, path, args, kwargs = super().deconstruct() - return name, path, args, kwargs - - def from_db_value(self, value: bytes, *args, **kwargs) -> dict: + def from_db_value(self, value: bytes | None, *args, **kwargs) -> dict | None: + if value is None: + return value return self.serializer.deserialize(value) def to_python(self, value: dict) -> dict: @@ -25,10 +22,10 @@ def to_python(self, value: dict) -> dict: raise ValidationError(f"Given value '{value}' must be 'dict' instance!") return value - def get_db_prep_value(self, value: dict, *args, **kwargs) -> bytes: - if value is not None: - return self.serializer.serialize(value) - return value + def get_db_prep_value(self, value: dict | None, *args, **kwargs) -> bytes | None: + if value is None: + return value + return self.serializer.serialize(value) def value_to_string(self, obj): return str(self.value_from_object(obj)) diff --git a/tests/django_app/params_field/__init__.py b/tests/django_app/dict_field/__init__.py similarity index 100% rename from tests/django_app/params_field/__init__.py rename to tests/django_app/dict_field/__init__.py diff --git a/tests/django_app/params_field/apps.py b/tests/django_app/dict_field/apps.py similarity index 60% rename from tests/django_app/params_field/apps.py rename to tests/django_app/dict_field/apps.py index be92398..98da121 100644 --- a/tests/django_app/params_field/apps.py +++ b/tests/django_app/dict_field/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig -class ParamsFieldConfig(AppConfig): +class DictFieldConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" - name = "params_field" + name = "dict_field" diff --git a/tests/django_app/params_field/migrations/0001_initial.py b/tests/django_app/dict_field/migrations/0001_initial.py similarity index 70% rename from tests/django_app/params_field/migrations/0001_initial.py rename to tests/django_app/dict_field/migrations/0001_initial.py index 7f070b7..2da9b47 100644 --- a/tests/django_app/params_field/migrations/0001_initial.py +++ b/tests/django_app/dict_field/migrations/0001_initial.py @@ -1,7 +1,8 @@ -# Generated by Django 4.2.22 on 2025-09-17 21:36 +# Generated by Django 4.2.22 on 2025-09-20 19:12 from django.db import migrations, models -import django_dict_field.params_field + +import django_dict_field.dict_field class Migration(migrations.Migration): @@ -23,7 +24,8 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("default_dict_field", django_dict_field.params_field.DictField()), + ("default_json_field", models.JSONField(null=True)), + ("default_dict_field", django_dict_field.dict_field.DictField()), ], ), ] diff --git a/tests/django_app/params_field/migrations/__init__.py b/tests/django_app/dict_field/migrations/__init__.py similarity index 100% rename from tests/django_app/params_field/migrations/__init__.py rename to tests/django_app/dict_field/migrations/__init__.py diff --git a/tests/django_app/params_field/models.py b/tests/django_app/dict_field/models.py similarity index 59% rename from tests/django_app/params_field/models.py rename to tests/django_app/dict_field/models.py index 610d5d5..0e2a0e8 100644 --- a/tests/django_app/params_field/models.py +++ b/tests/django_app/dict_field/models.py @@ -1,7 +1,9 @@ from django.db import models +from django.db.models.fields.json import JSONField from django_dict_field import DictField class ModelForTest(models.Model): + default_json_field = JSONField(null=True) default_dict_field = DictField() diff --git a/tests/django_app/dict_field/tests/__init__.py b/tests/django_app/dict_field/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/django_app/params_field/tests.py b/tests/django_app/dict_field/tests/test_default_dict_field.py similarity index 98% rename from tests/django_app/params_field/tests.py rename to tests/django_app/dict_field/tests/test_default_dict_field.py index b837b58..be78cbd 100644 --- a/tests/django_app/params_field/tests.py +++ b/tests/django_app/dict_field/tests/test_default_dict_field.py @@ -2,7 +2,7 @@ from django.core.exceptions import ValidationError from django.db.utils import IntegrityError -from .models import ModelForTest +from ..models import ModelForTest @pytest.mark.django_db diff --git a/tests/django_app/django_app/settings.py b/tests/django_app/django_app/settings.py index f98e9d3..011a15a 100644 --- a/tests/django_app/django_app/settings.py +++ b/tests/django_app/django_app/settings.py @@ -37,7 +37,7 @@ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", - "params_field", + "dict_field", ] MIDDLEWARE = [ From 566e31ba73626df6aa784fde93ffac87a26e40ff Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Mon, 13 Oct 2025 22:41:56 +0300 Subject: [PATCH 13/20] Add docker compose with postgres db for testing and bench. --- docker-compose.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..dda60d1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,7 @@ +services: + postgres: + image: postgres:15.14-alpine3.21 + ports: + - "8888:5432" + environment: + POSTGRES_PASSWORD: postgrespassword \ No newline at end of file From c3e6110007cb8a9ced2b5264156bbd41ff462d74 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Mon, 13 Oct 2025 22:42:27 +0300 Subject: [PATCH 14/20] Add shortcuts with getting up db in Makefile. --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1c60819..ce6ee7a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,13 @@ SHELL:=/usr/bin/env bash +.PHONY: db-up +db-up: + docker compose up -d + +.PHONY: db-down +db-down: + docker compose down -v + .PHONY: unit unit: poetry run pytest @@ -26,4 +34,4 @@ format: test: unit .PHONY: all-checks -all-checks: lint typing unit \ No newline at end of file +all-checks: lint typing test \ No newline at end of file From fd4196bafb9dbd20906f1c9c87a523c89131c063 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Mon, 13 Oct 2025 22:42:56 +0300 Subject: [PATCH 15/20] Add deps for postgresql db. --- poetry.lock | 67 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index fc8b32a..65ad700 100644 --- a/poetry.lock +++ b/poetry.lock @@ -412,6 +412,71 @@ files = [ dev = ["pre-commit", "tox"] testing = ["coverage", "pytest", "pytest-benchmark"] +[[package]] +name = "psycopg2-binary" +version = "2.9.11" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "psycopg2-binary-2.9.11.tar.gz", hash = "sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e"}, + {file = "psycopg2_binary-2.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908"}, + {file = "psycopg2_binary-2.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d"}, + {file = "psycopg2_binary-2.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1"}, + {file = "psycopg2_binary-2.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d"}, + {file = "psycopg2_binary-2.9.11-cp314-cp314-win_amd64.whl", hash = "sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:20e7fb94e20b03dcc783f76c0865f9da39559dcc0c28dd1a3fce0d01902a6b9c"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4bdab48575b6f870f465b397c38f1b415520e9879fdf10a53ee4f49dcbdf8a21"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9d3a9edcfbe77a3ed4bc72836d466dfce4174beb79eda79ea155cc77237ed9e8"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:44fc5c2b8fa871ce7f0023f619f1349a0aa03a0857f2c96fbc01c657dcbbdb49"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9c55460033867b4622cda1b6872edf445809535144152e5d14941ef591980edf"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:691c807d94aecfbc76a14e1408847d59ff5b5906a04a23e12a89007672b9e819"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b81627b691f29c4c30a8f322546ad039c40c328373b11dff7490a3e1b517855"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:41360b01c140c2a03d346cec3280cf8a71aa07d94f3b1509fa0161c366af66b4"}, + {file = "psycopg2_binary-2.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:875039274f8a2361e5207857899706da840768e2a775bf8c65e82f60b197df02"}, +] + [[package]] name = "py-cpuinfo" version = "9.0.0" @@ -671,4 +736,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "547fbfe9f6315b151f4e8a06636e50e39ed6a72161e3abe268025424aebd19fb" +content-hash = "d273192390d8470f9de9bba7e4a5e611c15211f5af6d4d9c215a1e597d28be68" diff --git a/pyproject.toml b/pyproject.toml index 3d04d79..1569c74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ django-stubs = { extras = ["compatible-mypy"], version = "^5.2.0", markers = "py ruff = "^0.11.12" pytest-benchmark = "^5.1.0" tabulate = "^0.9.0" +psycopg2-binary = "^2.9.11" [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE = "django_app.settings" From 714a7a845e89abb64bf382a0ad495bfd710eb284 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Mon, 13 Oct 2025 22:43:17 +0300 Subject: [PATCH 16/20] Setup postgresql django settings. --- tests/django_app/django_app/settings.py | 57 +++---------------------- 1 file changed, 7 insertions(+), 50 deletions(-) diff --git a/tests/django_app/django_app/settings.py b/tests/django_app/django_app/settings.py index 011a15a..1edccac 100644 --- a/tests/django_app/django_app/settings.py +++ b/tests/django_app/django_app/settings.py @@ -1,35 +1,10 @@ -""" -Django settings for django_app project. - -Generated by 'django-admin startproject' using Django 5.2.1. - -For more information on this file, see -https://docs.djangoproject.com/en/5.2/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/5.2/ref/settings/ -""" - from pathlib import Path -# Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure-4b=4+et^j4s$0h)b^gf&(j*@mhibjl!dknh0y4dgb(s*tbpd83" - -# SECURITY WARNING: don't run with debug turned on in production! +SECRET_KEY = "keep-it-secret" DEBUG = True - ALLOWED_HOSTS = [] - -# Application definition - INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", @@ -69,21 +44,17 @@ WSGI_APPLICATION = "django_app.wsgi.application" - -# Database -# https://docs.djangoproject.com/en/5.2/ref/settings/#databases - DATABASES = { "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", + "ENGINE": "django.db.backends.postgresql", + "NAME": "dbfortests", + "USER": "postgres", + "PASSWORD": "postgrespassword", # from docker-compose.yml + "HOST": "localhost", + "PORT": "8888", # from docker-compose.yml } } - -# Password validation -# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators - AUTH_PASSWORD_VALIDATORS = [ { "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", @@ -100,24 +71,10 @@ ] -# Internationalization -# https://docs.djangoproject.com/en/5.2/topics/i18n/ - LANGUAGE_CODE = "en-us" - TIME_ZONE = "UTC" - USE_I18N = True - USE_TZ = True - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/5.2/howto/static-files/ - STATIC_URL = "static/" - -# Default primary key field type -# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field - DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" From 523b4bc2b08a1832c83d12cd8e63bec9b71b74b2 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Mon, 13 Oct 2025 22:44:22 +0300 Subject: [PATCH 17/20] Improve bench module. --- benchmarks/jsonfield_benchmark.py | 87 +++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/benchmarks/jsonfield_benchmark.py b/benchmarks/jsonfield_benchmark.py index 2113338..c2c46cc 100644 --- a/benchmarks/jsonfield_benchmark.py +++ b/benchmarks/jsonfield_benchmark.py @@ -4,59 +4,102 @@ import timeit import django +from django.db import connection +from django.test.utils import setup_test_environment, teardown_test_environment from tabulate import tabulate project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -django_app_path = os.path.join(project_root, 'tests', 'django_app') +django_app_path = os.path.join(project_root, "tests", "django_app") sys.path.insert(0, project_root) sys.path.insert(0, django_app_path) -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_app.settings') - -# Инициализируем Django +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_app.settings") django.setup() +setup_test_environment() +db_name = connection.creation.create_test_db(verbosity=1, autoclobber=True) + from dict_field.models import ModelForTest DICT_SIZE = 100_000 BIG_DICT = {str(i): i for i in range(DICT_SIZE)} -def crud_on_big_dict_field(test_model: ModelForTest, field_name: str) -> None: - """Function for testing CRUD operations for `DictFiled` and `JSONField`.""" +def get_big_dict_field(test_model: ModelForTest, field_name: str) -> None: rand_index = str(random.randint(0, DICT_SIZE - 1)) - # get + test_model = ModelForTest.objects.get(pk=test_model.pk) test_model.__getattribute__(field_name)[rand_index] - # delete + + +def delete_big_dict_field(test_model: ModelForTest, field_name: str) -> None: + rand_index = str(random.randint(0, DICT_SIZE - 1)) + val = test_model.__getattribute__(field_name)[rand_index] del test_model.__getattribute__(field_name)[rand_index] test_model.save() - # update + # NOTE: Here is overhead because of saving deleted value. Must to figure + # out how to test delete correctly. + test_model.__getattribute__(field_name)[rand_index] = val + test_model.save() + + +def update_big_dict_field(test_model: ModelForTest, field_name: str) -> None: + rand_index = str(random.randint(0, DICT_SIZE - 1)) test_model.__getattribute__(field_name)[rand_index] = "restored" test_model.save() -if __name__ == "__main__": +def run_bench(): + # NOTE: Can take a while. Be careful. + number = 1000 test_model_jsonfield = ModelForTest.objects.create( default_json_field=BIG_DICT, default_dict_field={} ) - time_for_jsonfield = timeit.timeit( - lambda: crud_on_big_dict_field(test_model_jsonfield, "default_json_field"), - number=100, - ) - test_model_dictfield = ModelForTest.objects.create( default_json_field={}, default_dict_field=BIG_DICT ) - time_for_dictfield = timeit.timeit( - lambda: crud_on_big_dict_field(test_model_dictfield, "default_dict_field"), - number=100, + + # JSONField + time_to_get = timeit.timeit( + lambda: get_big_dict_field(test_model_jsonfield, "default_json_field"), + number=number, + ) + time_to_delete = timeit.timeit( + lambda: delete_big_dict_field(test_model_jsonfield, "default_json_field"), + number=number, ) + time_to_update = timeit.timeit( + lambda: update_big_dict_field(test_model_jsonfield, "default_json_field"), + number=number, + ) + json_field_time = ("JSONField", time_to_get, time_to_update, time_to_delete) + + # DictField + time_to_get = timeit.timeit( + lambda: get_big_dict_field(test_model_dictfield, "default_dict_field"), + number=number, + ) + time_to_delete = timeit.timeit( + lambda: delete_big_dict_field(test_model_dictfield, "default_dict_field"), + number=number, + ) + time_to_update = timeit.timeit( + lambda: update_big_dict_field(test_model_dictfield, "default_dict_field"), + number=number, + ) + dict_field_time = ("DictField", time_to_get, time_to_update, time_to_delete) report_table = tabulate( - [ - ["Time for JSONField", time_for_jsonfield], - ["Time for DictField", time_for_dictfield], - ] + (json_field_time, dict_field_time), + headers=("Field", "Get", "Delete", "Update"), + tablefmt="grid", ) print(report_table) + + +if __name__ == "__main__": + try: + run_bench() + finally: + teardown_test_environment() + connection.creation.destroy_test_db(db_name, verbosity=1) From 6605c3bddacc9fb52bb1e1f60be89db8ff1ba033 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Mon, 13 Oct 2025 22:47:42 +0300 Subject: [PATCH 18/20] Fix dirs to check. --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index ce6ee7a..27569da 100644 --- a/Makefile +++ b/Makefile @@ -22,13 +22,13 @@ typing: .PHONY: lint lint: - poetry run ruff check --select I - poetry run ruff format --check + poetry run ruff check --select I src + poetry run ruff format --check src .PHONY: format format: - poetry run ruff check --select I --fix - poetry run ruff format + poetry run ruff check --select I --fix src + poetry run ruff format src .PHONY: test test: unit From df55bc1867f42253399bb1197ac64b6b3ce4bfb4 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Mon, 13 Oct 2025 22:47:53 +0300 Subject: [PATCH 19/20] Format job. --- src/django_dict_field/dict_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/django_dict_field/dict_field.py b/src/django_dict_field/dict_field.py index e1873f5..cd69f61 100644 --- a/src/django_dict_field/dict_field.py +++ b/src/django_dict_field/dict_field.py @@ -9,7 +9,7 @@ class DictField(BinaryField): def __init__(self, *args, **kwargs) -> None: self.serializer = Serializer() - kwargs.setdefault('editable', False) + kwargs.setdefault("editable", False) super().__init__(*args, **kwargs) def from_db_value(self, value: bytes | None, *args, **kwargs) -> dict | None: From abce42498e2cfbfb564b28b205b2856a1f0d63d6 Mon Sep 17 00:00:00 2001 From: skv0zsneg Date: Mon, 13 Oct 2025 22:48:15 +0300 Subject: [PATCH 20/20] Add up and down db. --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 966ed1e..f461e50 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,5 +33,9 @@ jobs: poetry config virtualenvs.in-project true poetry run pip install -U pip poetry install + - name: Pull up db + run: make db-up - name: Run tests run: make test + - name: Pull down db + run: make db-down