django-filthyfields 2.1.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_filthyfields-2.1.0/src/django_filthyfields.egg-info → django_filthyfields-2.2.0}/PKG-INFO +2 -2
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/README.md +1 -1
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/pyproject.toml +1 -1
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0/src/django_filthyfields.egg-info}/PKG-INFO +2 -2
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/filthyfields/__init__.py +7 -1
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/filthyfields/_descriptor.c +18 -22
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/filthyfields/filthyfields.py +42 -16
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/LICENSE +0 -0
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/MANIFEST.in +0 -0
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/setup.cfg +0 -0
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/setup.py +0 -0
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/django_filthyfields.egg-info/SOURCES.txt +0 -0
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/django_filthyfields.egg-info/dependency_links.txt +0 -0
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/django_filthyfields.egg-info/requires.txt +0 -0
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/django_filthyfields.egg-info/top_level.txt +0 -0
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/filthyfields/_descriptor.py +0 -0
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/filthyfields/compare.py +0 -0
- {django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/filthyfields/py.typed +0 -0
{django_filthyfields-2.1.0/src/django_filthyfields.egg-info → django_filthyfields-2.2.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-filthyfields
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Tracking dirty fields on a Django model instance.
|
|
5
5
|
License-Expression: BSD-3-Clause
|
|
6
6
|
Project-URL: Homepage, https://github.com/oliverhaas/django-filthyfields
|
|
@@ -33,6 +33,6 @@ Dynamic: license-file
|
|
|
33
33
|
Tracking dirty fields on a Django model instance.
|
|
34
34
|
Dirty means that field in-memory and database values are different.
|
|
35
35
|
|
|
36
|
-
Started as a fork of [django-dirtyfields](https://github.com/romgar/django-dirtyfields) with a rewritten lazy, descriptor-based implementation; since diverged with its own feature set and release cadence. The mixin and method names (`DirtyFieldsMixin`, `get_dirty_fields`, …) are kept from upstream
|
|
36
|
+
Started as a fork of [django-dirtyfields](https://github.com/romgar/django-dirtyfields) with a rewritten lazy, descriptor-based implementation; since diverged with its own feature set and release cadence. The mixin and method names (`DirtyFieldsMixin`, `get_dirty_fields`, …) are mostly kept from upstream.
|
|
37
37
|
|
|
38
38
|
See the [documentation](https://oliverhaas.github.io/django-filthyfields/) for more information.
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
Tracking dirty fields on a Django model instance.
|
|
7
7
|
Dirty means that field in-memory and database values are different.
|
|
8
8
|
|
|
9
|
-
Started as a fork of [django-dirtyfields](https://github.com/romgar/django-dirtyfields) with a rewritten lazy, descriptor-based implementation; since diverged with its own feature set and release cadence. The mixin and method names (`DirtyFieldsMixin`, `get_dirty_fields`, …) are kept from upstream
|
|
9
|
+
Started as a fork of [django-dirtyfields](https://github.com/romgar/django-dirtyfields) with a rewritten lazy, descriptor-based implementation; since diverged with its own feature set and release cadence. The mixin and method names (`DirtyFieldsMixin`, `get_dirty_fields`, …) are mostly kept from upstream.
|
|
10
10
|
|
|
11
11
|
See the [documentation](https://oliverhaas.github.io/django-filthyfields/) for more information.
|
{django_filthyfields-2.1.0 → django_filthyfields-2.2.0/src/django_filthyfields.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-filthyfields
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Tracking dirty fields on a Django model instance.
|
|
5
5
|
License-Expression: BSD-3-Clause
|
|
6
6
|
Project-URL: Homepage, https://github.com/oliverhaas/django-filthyfields
|
|
@@ -33,6 +33,6 @@ Dynamic: license-file
|
|
|
33
33
|
Tracking dirty fields on a Django model instance.
|
|
34
34
|
Dirty means that field in-memory and database values are different.
|
|
35
35
|
|
|
36
|
-
Started as a fork of [django-dirtyfields](https://github.com/romgar/django-dirtyfields) with a rewritten lazy, descriptor-based implementation; since diverged with its own feature set and release cadence. The mixin and method names (`DirtyFieldsMixin`, `get_dirty_fields`, …) are kept from upstream
|
|
36
|
+
Started as a fork of [django-dirtyfields](https://github.com/romgar/django-dirtyfields) with a rewritten lazy, descriptor-based implementation; since diverged with its own feature set and release cadence. The mixin and method names (`DirtyFieldsMixin`, `get_dirty_fields`, …) are mostly kept from upstream.
|
|
37
37
|
|
|
38
38
|
See the [documentation](https://oliverhaas.github.io/django-filthyfields/) for more information.
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
from importlib.metadata import version
|
|
4
4
|
|
|
5
5
|
from filthyfields.compare import normalise_value, raw_compare, timezone_support_compare
|
|
6
|
-
from filthyfields.filthyfields import
|
|
6
|
+
from filthyfields.filthyfields import (
|
|
7
|
+
DirtyFieldsMixin,
|
|
8
|
+
DirtyStateNotCapturedError,
|
|
9
|
+
capture_dirty_state,
|
|
10
|
+
reset_dirty_state,
|
|
11
|
+
)
|
|
7
12
|
|
|
8
13
|
__all__ = [
|
|
9
14
|
"DirtyFieldsMixin",
|
|
15
|
+
"DirtyStateNotCapturedError",
|
|
10
16
|
"capture_dirty_state",
|
|
11
17
|
"normalise_value",
|
|
12
18
|
"raw_compare",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Generated by Cython 3.2.
|
|
1
|
+
/* Generated by Cython 3.2.5 */
|
|
2
2
|
|
|
3
3
|
/* BEGIN: Cython Metadata
|
|
4
4
|
{
|
|
@@ -34,8 +34,8 @@ END: Cython Metadata */
|
|
|
34
34
|
#elif PY_VERSION_HEX < 0x03080000
|
|
35
35
|
#error Cython requires Python 3.8+.
|
|
36
36
|
#else
|
|
37
|
-
#define __PYX_ABI_VERSION "
|
|
38
|
-
#define CYTHON_HEX_VERSION
|
|
37
|
+
#define __PYX_ABI_VERSION "3_2_5"
|
|
38
|
+
#define CYTHON_HEX_VERSION 0x030205F0
|
|
39
39
|
#define CYTHON_FUTURE_DIVISION 1
|
|
40
40
|
/* CModulePreamble */
|
|
41
41
|
#include <stddef.h>
|
|
@@ -2760,7 +2760,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
|
|
|
2760
2760
|
{
|
|
2761
2761
|
PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_klass,0};
|
|
2762
2762
|
const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
|
|
2763
|
-
if (unlikely(__pyx_kwds_len
|
|
2763
|
+
if (unlikely(__pyx_kwds_len < 0)) __PYX_ERR(0, 29, __pyx_L3_error)
|
|
2764
2764
|
if (__pyx_kwds_len > 0) {
|
|
2765
2765
|
switch (__pyx_nargs) {
|
|
2766
2766
|
case 1:
|
|
@@ -2885,7 +2885,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
|
|
|
2885
2885
|
{
|
|
2886
2886
|
PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_fn,0};
|
|
2887
2887
|
const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
|
|
2888
|
-
if (unlikely(__pyx_kwds_len
|
|
2888
|
+
if (unlikely(__pyx_kwds_len < 0)) __PYX_ERR(0, 33, __pyx_L3_error)
|
|
2889
2889
|
if (__pyx_kwds_len > 0) {
|
|
2890
2890
|
switch (__pyx_nargs) {
|
|
2891
2891
|
case 1:
|
|
@@ -3010,7 +3010,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
|
|
|
3010
3010
|
{
|
|
3011
3011
|
PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_fn,0};
|
|
3012
3012
|
const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
|
|
3013
|
-
if (unlikely(__pyx_kwds_len
|
|
3013
|
+
if (unlikely(__pyx_kwds_len < 0)) __PYX_ERR(0, 37, __pyx_L3_error)
|
|
3014
3014
|
if (__pyx_kwds_len > 0) {
|
|
3015
3015
|
switch (__pyx_nargs) {
|
|
3016
3016
|
case 1:
|
|
@@ -3135,7 +3135,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
|
|
|
3135
3135
|
{
|
|
3136
3136
|
PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_fn,0};
|
|
3137
3137
|
const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
|
|
3138
|
-
if (unlikely(__pyx_kwds_len
|
|
3138
|
+
if (unlikely(__pyx_kwds_len < 0)) __PYX_ERR(0, 41, __pyx_L3_error)
|
|
3139
3139
|
if (__pyx_kwds_len > 0) {
|
|
3140
3140
|
switch (__pyx_nargs) {
|
|
3141
3141
|
case 1:
|
|
@@ -3813,7 +3813,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
|
|
|
3813
3813
|
{
|
|
3814
3814
|
PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_value,0};
|
|
3815
3815
|
const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
|
|
3816
|
-
if (unlikely(__pyx_kwds_len
|
|
3816
|
+
if (unlikely(__pyx_kwds_len < 0)) __PYX_ERR(0, 81, __pyx_L3_error)
|
|
3817
3817
|
if (__pyx_kwds_len > 0) {
|
|
3818
3818
|
switch (__pyx_nargs) {
|
|
3819
3819
|
case 1:
|
|
@@ -3917,7 +3917,7 @@ static int __pyx_pw_12filthyfields_11_descriptor_14DiffDescriptor_1__init__(PyOb
|
|
|
3917
3917
|
{
|
|
3918
3918
|
PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_field,&__pyx_mstate_global->__pyx_n_u_deferred_attr,&__pyx_mstate_global->__pyx_n_u_track_mutations,0};
|
|
3919
3919
|
const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_VARARGS(__pyx_kwds) : 0;
|
|
3920
|
-
if (unlikely(__pyx_kwds_len
|
|
3920
|
+
if (unlikely(__pyx_kwds_len < 0)) __PYX_ERR(0, 114, __pyx_L3_error)
|
|
3921
3921
|
if (__pyx_kwds_len > 0) {
|
|
3922
3922
|
switch (__pyx_nargs) {
|
|
3923
3923
|
case 3:
|
|
@@ -5960,7 +5960,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
|
|
|
5960
5960
|
{
|
|
5961
5961
|
PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pyx_state,0};
|
|
5962
5962
|
const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
|
|
5963
|
-
if (unlikely(__pyx_kwds_len
|
|
5963
|
+
if (unlikely(__pyx_kwds_len < 0)) __PYX_ERR(1, 16, __pyx_L3_error)
|
|
5964
5964
|
if (__pyx_kwds_len > 0) {
|
|
5965
5965
|
switch (__pyx_nargs) {
|
|
5966
5966
|
case 1:
|
|
@@ -6103,7 +6103,7 @@ PyObject *__pyx_args, PyObject *__pyx_kwds
|
|
|
6103
6103
|
{
|
|
6104
6104
|
PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_pyx_type,&__pyx_mstate_global->__pyx_n_u_pyx_checksum,&__pyx_mstate_global->__pyx_n_u_pyx_state,0};
|
|
6105
6105
|
const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0;
|
|
6106
|
-
if (unlikely(__pyx_kwds_len
|
|
6106
|
+
if (unlikely(__pyx_kwds_len < 0)) __PYX_ERR(1, 4, __pyx_L3_error)
|
|
6107
6107
|
if (__pyx_kwds_len > 0) {
|
|
6108
6108
|
switch (__pyx_nargs) {
|
|
6109
6109
|
case 3:
|
|
@@ -7890,11 +7890,11 @@ __Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
|
|
|
7890
7890
|
res = PyTuple_New(n);
|
|
7891
7891
|
if (unlikely(res == NULL)) return NULL;
|
|
7892
7892
|
for (i = 0; i < n; i++) {
|
|
7893
|
+
Py_INCREF(src[i]);
|
|
7893
7894
|
if (unlikely(__Pyx_PyTuple_SET_ITEM(res, i, src[i]) < (0))) {
|
|
7894
7895
|
Py_DECREF(res);
|
|
7895
7896
|
return NULL;
|
|
7896
7897
|
}
|
|
7897
|
-
Py_INCREF(src[i]);
|
|
7898
7898
|
}
|
|
7899
7899
|
return res;
|
|
7900
7900
|
}
|
|
@@ -9221,7 +9221,7 @@ bad:
|
|
|
9221
9221
|
}
|
|
9222
9222
|
|
|
9223
9223
|
/* dict_iter */
|
|
9224
|
-
#if
|
|
9224
|
+
#if CYTHON_AVOID_BORROWED_REFS
|
|
9225
9225
|
#include <string.h>
|
|
9226
9226
|
#endif
|
|
9227
9227
|
static CYTHON_INLINE PyObject* __Pyx_dict_iterator(PyObject* iterable, int is_dict, PyObject* method_name,
|
|
@@ -9229,7 +9229,7 @@ static CYTHON_INLINE PyObject* __Pyx_dict_iterator(PyObject* iterable, int is_di
|
|
|
9229
9229
|
is_dict = is_dict || likely(PyDict_CheckExact(iterable));
|
|
9230
9230
|
*p_source_is_dict = is_dict;
|
|
9231
9231
|
if (is_dict) {
|
|
9232
|
-
#if !
|
|
9232
|
+
#if !CYTHON_AVOID_BORROWED_REFS
|
|
9233
9233
|
*p_orig_length = PyDict_Size(iterable);
|
|
9234
9234
|
Py_INCREF(iterable);
|
|
9235
9235
|
return iterable;
|
|
@@ -9258,7 +9258,7 @@ static CYTHON_INLINE PyObject* __Pyx_dict_iterator(PyObject* iterable, int is_di
|
|
|
9258
9258
|
iterable = __Pyx_PyObject_CallMethod0(iterable, method_name);
|
|
9259
9259
|
if (!iterable)
|
|
9260
9260
|
return NULL;
|
|
9261
|
-
#if !
|
|
9261
|
+
#if !CYTHON_AVOID_BORROWED_REFS
|
|
9262
9262
|
if (PyTuple_CheckExact(iterable) || PyList_CheckExact(iterable))
|
|
9263
9263
|
return iterable;
|
|
9264
9264
|
#endif
|
|
@@ -11427,7 +11427,6 @@ __Pyx_CyFunction_get_is_coroutine_value(__pyx_CyFunctionObject *op) {
|
|
|
11427
11427
|
PyList_SET_ITEM(fromlist, 0, marker);
|
|
11428
11428
|
#else
|
|
11429
11429
|
if (unlikely(PyList_SetItem(fromlist, 0, marker) < 0)) {
|
|
11430
|
-
Py_DECREF(marker);
|
|
11431
11430
|
Py_DECREF(fromlist);
|
|
11432
11431
|
return NULL;
|
|
11433
11432
|
}
|
|
@@ -11668,8 +11667,7 @@ __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m)
|
|
|
11668
11667
|
Py_CLEAR(m->func_doc);
|
|
11669
11668
|
Py_CLEAR(m->func_globals);
|
|
11670
11669
|
Py_CLEAR(m->func_code);
|
|
11671
|
-
#if
|
|
11672
|
-
#if PY_VERSION_HEX < 0x030900B1
|
|
11670
|
+
#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API
|
|
11673
11671
|
Py_CLEAR(__Pyx_CyFunction_GetClassObj(m));
|
|
11674
11672
|
#else
|
|
11675
11673
|
{
|
|
@@ -11677,7 +11675,6 @@ __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m)
|
|
|
11677
11675
|
((PyCMethodObject *) (m))->mm_class = NULL;
|
|
11678
11676
|
Py_XDECREF(cls);
|
|
11679
11677
|
}
|
|
11680
|
-
#endif
|
|
11681
11678
|
#endif
|
|
11682
11679
|
Py_CLEAR(m->defaults_tuple);
|
|
11683
11680
|
Py_CLEAR(m->defaults_kwdict);
|
|
@@ -11729,11 +11726,10 @@ static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit,
|
|
|
11729
11726
|
Py_VISIT(m->func_doc);
|
|
11730
11727
|
Py_VISIT(m->func_globals);
|
|
11731
11728
|
__Pyx_VISIT_CONST(m->func_code);
|
|
11732
|
-
#if !CYTHON_COMPILING_IN_LIMITED_API
|
|
11733
11729
|
Py_VISIT(__Pyx_CyFunction_GetClassObj(m));
|
|
11734
|
-
#endif
|
|
11735
11730
|
Py_VISIT(m->defaults_tuple);
|
|
11736
11731
|
Py_VISIT(m->defaults_kwdict);
|
|
11732
|
+
Py_VISIT(m->func_annotations);
|
|
11737
11733
|
Py_VISIT(m->func_is_coroutine);
|
|
11738
11734
|
Py_VISIT(m->defaults);
|
|
11739
11735
|
return 0;
|
|
@@ -12708,8 +12704,8 @@ raise_neg_overflow:
|
|
|
12708
12704
|
#if CYTHON_VECTORCALL
|
|
12709
12705
|
static int __Pyx_VectorcallBuilder_AddArg(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n) {
|
|
12710
12706
|
(void)__Pyx_PyObject_FastCallDict;
|
|
12711
|
-
if (__Pyx_PyTuple_SET_ITEM(builder, n, key) != (0)) return -1;
|
|
12712
12707
|
Py_INCREF(key);
|
|
12708
|
+
if (__Pyx_PyTuple_SET_ITEM(builder, n, key) != (0)) return -1;
|
|
12713
12709
|
args[n] = value;
|
|
12714
12710
|
return 0;
|
|
12715
12711
|
}
|
|
@@ -23,6 +23,10 @@ if TYPE_CHECKING:
|
|
|
23
23
|
NormaliseFunction = tuple[Callable[..., Any], dict[str, Any]]
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
class DirtyStateNotCapturedError(RuntimeError):
|
|
27
|
+
"""Raised when pre-save state is read before any save/asave/capture_dirty_state."""
|
|
28
|
+
|
|
29
|
+
|
|
26
30
|
def _should_track_field(instance: models.Model, field_name: str, field_attname: str | None = None) -> bool:
|
|
27
31
|
"""Apply FIELDS_TO_CHECK / FIELDS_TO_CHECK_EXCLUDE. Accepts both name and attname (e.g. 'fkey'/'fkey_id')."""
|
|
28
32
|
fields_to_check = getattr(instance, "FIELDS_TO_CHECK", None)
|
|
@@ -212,8 +216,15 @@ class DirtyFieldsMixin(models.Model, metaclass=_DirtyMeta):
|
|
|
212
216
|
|
|
213
217
|
@property
|
|
214
218
|
def was_adding(self) -> bool:
|
|
215
|
-
"""Whether the instance was unsaved before the last ``save()`` / ``capture_dirty_state()``.
|
|
216
|
-
|
|
219
|
+
"""Whether the instance was unsaved before the last ``save()`` / ``capture_dirty_state()``.
|
|
220
|
+
|
|
221
|
+
Raises ``DirtyStateNotCapturedError`` if no save or capture has happened yet.
|
|
222
|
+
"""
|
|
223
|
+
if "_was_adding" not in self.__dict__:
|
|
224
|
+
raise DirtyStateNotCapturedError(
|
|
225
|
+
"was_adding read before any save()/asave()/capture_dirty_state() — nothing has been captured yet.",
|
|
226
|
+
)
|
|
227
|
+
return self._was_adding
|
|
217
228
|
|
|
218
229
|
def _dirty_reset_state(self, fields: Iterable[str] | None = None) -> None:
|
|
219
230
|
"""Reset dirty state. ``fields=None`` resets everything; otherwise accepts name or attname."""
|
|
@@ -253,15 +264,12 @@ class DirtyFieldsMixin(models.Model, metaclass=_DirtyMeta):
|
|
|
253
264
|
if name in current_m2m:
|
|
254
265
|
self._original_m2m_state[name] = current_m2m[name]
|
|
255
266
|
|
|
256
|
-
def save(self, *args: Any, **kwargs: Any) -> None:
|
|
257
|
-
|
|
267
|
+
def save(self, *args: Any, dirty_capture: bool = True, dirty_reset: bool = True, **kwargs: Any) -> None:
|
|
268
|
+
if dirty_capture:
|
|
269
|
+
self._dirty_capture_was_dirty()
|
|
258
270
|
super().save(*args, **kwargs)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
async def asave(self, *args: Any, **kwargs: Any) -> None:
|
|
262
|
-
self._dirty_capture_was_dirty()
|
|
263
|
-
await super().asave(*args, **kwargs)
|
|
264
|
-
self._dirty_reset_state(fields=kwargs.get("update_fields"))
|
|
271
|
+
if dirty_reset:
|
|
272
|
+
self._dirty_reset_state(fields=kwargs.get("update_fields"))
|
|
265
273
|
|
|
266
274
|
def refresh_from_db( # ty: ignore[invalid-method-override]
|
|
267
275
|
self,
|
|
@@ -450,20 +458,38 @@ class DirtyFieldsMixin(models.Model, metaclass=_DirtyMeta):
|
|
|
450
458
|
return result
|
|
451
459
|
|
|
452
460
|
def was_dirty(self, check_relationship: bool = False, check_m2m: bool = False) -> bool:
|
|
461
|
+
"""Whether any tracked field was dirty before the last save.
|
|
462
|
+
|
|
463
|
+
Delegates to :meth:`get_was_dirty_fields`; raises ``DirtyStateNotCapturedError``
|
|
464
|
+
if no save or capture has happened yet.
|
|
465
|
+
"""
|
|
453
466
|
return bool(self.get_was_dirty_fields(check_relationship=check_relationship, check_m2m=check_m2m))
|
|
454
467
|
|
|
455
468
|
def get_was_dirty_fields(self, check_relationship: bool = False, check_m2m: bool = False) -> dict[str, Any]:
|
|
456
|
-
"""Fields dirty before the last save (captured by save()/asave()).
|
|
469
|
+
"""Fields dirty before the last save (captured by save()/asave()).
|
|
470
|
+
|
|
471
|
+
Raises ``DirtyStateNotCapturedError`` if no save or capture has happened yet, or
|
|
472
|
+
if ``check_m2m=True`` is requested but ``ENABLE_M2M_CHECK`` was disabled at the
|
|
473
|
+
time of the last capture.
|
|
474
|
+
"""
|
|
457
475
|
if check_m2m and not self.ENABLE_M2M_CHECK:
|
|
458
476
|
raise ValueError("You can't check m2m fields if ENABLE_M2M_CHECK is set to False")
|
|
459
477
|
|
|
460
|
-
if
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
478
|
+
if "_was_dirty_fields" not in self.__dict__:
|
|
479
|
+
raise DirtyStateNotCapturedError(
|
|
480
|
+
"get_was_dirty_fields() called before any save()/asave()/capture_dirty_state() — "
|
|
481
|
+
"nothing has been captured yet.",
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
result = dict(self._was_dirty_fields_rel) if check_relationship else dict(self._was_dirty_fields)
|
|
464
485
|
|
|
465
486
|
if check_m2m:
|
|
466
|
-
|
|
487
|
+
if "_was_dirty_fields_m2m" not in self.__dict__:
|
|
488
|
+
raise DirtyStateNotCapturedError(
|
|
489
|
+
"check_m2m=True but no M2M state was captured — "
|
|
490
|
+
"ENABLE_M2M_CHECK was disabled at the time of the last save/capture.",
|
|
491
|
+
)
|
|
492
|
+
result.update(self._was_dirty_fields_m2m)
|
|
467
493
|
|
|
468
494
|
return result
|
|
469
495
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_filthyfields-2.1.0 → django_filthyfields-2.2.0}/src/django_filthyfields.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|