-
Notifications
You must be signed in to change notification settings - Fork 204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
resolve #305 abstract models #314
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely clear on how this change is intended to be used.
When implementing a custom TaskResult model, how would you tell django_celery_results to use it instead of the included TaskResult? (I'm assuming this is the goal of this PR)
Well I like the idea and I've some scenarios (multitenant platform) where something like this could help a lot avoiding unnecessary boilerplate code without having to override code from I think I've some idea to solve the issue you describe @AllexVeldman, I will work on that as soon as I can and share it with all of you to see what do you think about it, but for now I will be off for 3 weeks, so I will do it when I'm back. Cheers! |
@diegocastrum are you still on it or is the task up for grabs? |
Hi @hoefling, sorry for not replying you earlier. I still working on it and I'm almost done with it. I already have a working scenario in one of my projects. So I expect to push the code very soon, I will try to do it this week. Thanks for asking! |
Added a `helpers` module into `models` containing the functions `taskresult_model()` and `groupresult_model()`. * `taskresult_model()`: will try to find the custom model using a dotted path defined under the constant `CELERY_RESULTS_TASKRESULT_MODEL` in the settings of the user's project * `groupresult_model()` will try to do the same using under the constant `CELERY_RESULTS_GROUPRESULT_MODEL`. By default if these attributes are not found `django-celery-results` will use the default models (`models.TaskResult` and `models.GroupResult`). Updated database backend in order to use custom models for `TaskResult and `GroupResult` it they're present. Instead to import explicitely the `TaskResult` and the `GroupResult` (default models from `django-celery-results`) we make use of the model helpers to load the right classes, the custom ones if they're present otherwise we use the default ones. Getting data from `task_kwargs` to extend the `task_properties` and be able to store them into the database (using the custom models). First of all we need a way to get data from `task_kwargs` (or somewhere else) just before a `task_result` record is created, evaluate that data and find the right values that will be used to fill the new fields defined in the custom model. So for this purpose we added a settings module to `django-celery-results` which will hold default settings, the first setting that will contain is a function in charge to get a callback from the settings of the user project. This callback will be feeded by the task `task_kwargs`, which will be intercepted in `DatabaseBackend._get_extended_properties` just before the `task_kwargs` are encoded by `encode_content()` method and send it to the `store_result` method from the object manager of `TaskModel` (Custom/Default one). To end, we must to extend the arguments of the `store_result` method from the `TaskResult` Manager adding `**extra_fields` that will make us able to send extra data to the custom model, when it's defined. --- Resolves celery#305 Fixes celery#314
Hi @jose-padin, @AllexVeldman and @hoefling! I already made a commit improving the initial implementation and adding support to use behind the scenes the custom models defined in the project where
This implementation is working right now in one of my projects and so far it seems to work fine. Probably we should add some checks, for example:
Let me know what you think & Cheers! |
@diegocastrum Looks like a viable implementation to me.
Why is defining just 1 or 2 invalid? from what I can see, having only |
# TODO: We assuming that task protocol 1 could be always in use. :/ | ||
extended_props.update( | ||
extend_task_props_callback(getattr(request, 'kwargs', None))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider calling this from _store_result()
so it can be used regardless of the extended result setting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice point! It will be done in the next commit.
django_celery_results/settings.py
Outdated
extend_task_props_callback = get_callback_function( | ||
"CELERY_RESULTS_EXTEND_TASK_PROPS_CALLBACK" | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a default callable returning an empty dict so this is an opt-in.
Currently this is mandatory if extended_result=True
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be done in the next commit
@@ -79,6 +79,10 @@ def _get_extended_properties(self, request, traceback): | |||
# task protocol 1 | |||
task_kwargs = getattr(request, 'kwargs', None) | |||
|
|||
# TODO: We assuming that task protocol 1 could be always in use. :/ | |||
extended_props.update( | |||
extend_task_props_callback(getattr(request, 'kwargs', None))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider passing task_props
and request
(in _store_result()
) to the callback, this gives the developer more context to get whatever additional data it needs to store in the extended model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's clear, It will be done in the next commit.
I'm not entirely sure how the docs are managed but I think this warrants a good description with some examples. |
if callable(callback): | ||
return callback | ||
|
||
extend_task_props_callback = get_callback_function( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding some sanity checks on the return value of the callback.
For example that it complies to the Mapping
protocol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, get_callback_function
is quite generic, and could be used in the future for another purposes, returning different types of data. So far I can see, the sanity checks could be in _store_results()
where extend_task_props_callback()
is called. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm personally not a fan of this generic function, I'd err of the side of caution and make the callback handling as clean and explicit as possible so any errors we need to raise have a clear source.
But this is more of a code-style thing so maybe one of the maintainers (@auvipy) can comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point @AllexVeldman, what do you think @auvipy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AllexVeldman & @auvipy I think I found a proper way to control the callback internally being able to check explicitly that return value.
I just created a new function called get_task_props_extension()
into the settings module in charge to return an empty dict in case that the CELERY_RESULTS_EXTEND_TASK_PROPS_CALLBACK
is undefined and otherwise will check that the return value complies with the Mapping protocol.
Let me know what you think!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
`extend_task_props_callback` moved from `_get_extended_properties` to `_store_result`. Suggested by @AllesVeldman. `extend_task_props_callback` will get the `request` object as first parameter and a copy of `task_props` (avoiding potential manipulation of the original `task_props`) as second paramenter. --- Resolves celery#305 Fixes celery#314
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will also need a rebase
`get_callback_function()` gets a default callback as an arg returning explicitely an empty dict. `get_callback_function()` raises an `ImproperlyConfigured` exception when the callback is not callable. --- Resolves celery#305 Fixes celery#314
Added `get_task_props_extension` to `settings` module which will raise an `ImproperlyConfigured` when the task_props_extension doesn't complies with the Mapping protocol. `DatabaseBackend` will make use of `get_task_props_extension` to update a potential custom model with the custom properties --- Resolves celery#305 Fixes celery#314
`models` module replaced by a `models` package containing an `abstract` module (for abstract models) and a `generic` module (for the default models, previously in the `models` module) --- Resolves celery#305 Fixes celery#314
… module. Added some minor changes. --- Resolves celery#305 Fixes celery#314
Added a `helpers` module into `models` containing the functions `taskresult_model()` and `groupresult_model()`. * `taskresult_model()`: will try to find the custom model using a dotted path defined under the constant `CELERY_RESULTS_TASKRESULT_MODEL` in the settings of the user's project * `groupresult_model()` will try to do the same using under the constant `CELERY_RESULTS_GROUPRESULT_MODEL`. By default if these attributes are not found `django-celery-results` will use the default models (`models.TaskResult` and `models.GroupResult`). Updated database backend in order to use custom models for `TaskResult and `GroupResult` it they're present. Instead to import explicitely the `TaskResult` and the `GroupResult` (default models from `django-celery-results`) we make use of the model helpers to load the right classes, the custom ones if they're present otherwise we use the default ones. Getting data from `task_kwargs` to extend the `task_properties` and be able to store them into the database (using the custom models). First of all we need a way to get data from `task_kwargs` (or somewhere else) just before a `task_result` record is created, evaluate that data and find the right values that will be used to fill the new fields defined in the custom model. So for this purpose we added a settings module to `django-celery-results` which will hold default settings, the first setting that will contain is a function in charge to get a callback from the settings of the user project. This callback will be feeded by the task `task_kwargs`, which will be intercepted in `DatabaseBackend._get_extended_properties` just before the `task_kwargs` are encoded by `encode_content()` method and send it to the `store_result` method from the object manager of `TaskModel` (Custom/Default one). To end, we must to extend the arguments of the `store_result` method from the `TaskResult` Manager adding `**extra_fields` that will make us able to send extra data to the custom model, when it's defined. --- Resolves celery#305 Fixes celery#314
`extend_task_props_callback` moved from `_get_extended_properties` to `_store_result`. Suggested by @AllesVeldman. `extend_task_props_callback` will get the `request` object as first parameter and a copy of `task_props` (avoiding potential manipulation of the original `task_props`) as second paramenter. --- Resolves celery#305 Fixes celery#314
`get_callback_function()` gets a default callback as an arg returning explicitely an empty dict. `get_callback_function()` raises an `ImproperlyConfigured` exception when the callback is not callable. --- Resolves celery#305 Fixes celery#314
Added `get_task_props_extension` to `settings` module which will raise an `ImproperlyConfigured` when the task_props_extension doesn't complies with the Mapping protocol. `DatabaseBackend` will make use of `get_task_props_extension` to update a potential custom model with the custom properties --- Resolves celery#305 Fixes celery#314
It's done @auvipy |
@auvipy or @AllexVeldman Could you give us an idea about when could be this merged? |
I'm not involved in merging/releasing, but I think there is still a bit of documentation missing, maybe extend https://github.com/celery/django-celery-results/blob/master/docs/getting_started.rst with an "Extending the results" part or creating a separate page for it. |
You're definetly right. I'll try to do it asap |
Is this still planned? From a user perspective it would be quite a useful addition to have this |
the remaining part is all about keeping backward compatibility for existing users |
app_label = "django_celery_results" | ||
|
||
|
||
class ChordCounter(models.Model): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why we are not using any abstract model here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no specific reason at the moment. Will be added in the next iteration/update
Thank you @auvipy glad to hear your input and @axsaucedo to show up the interest! I think I could work on this in about 3 or 4 weeks. |
`models` module replaced by a `models` package containing an `abstract` module (for abstract models) and a `generic` module (for the default models, previously in the `models` module) --- Resolves celery#305 Fixes celery#314
… module. Added some minor changes. --- Resolves celery#305 Fixes celery#314
* Fixed import bug
Co-authored-by: Allex <a.veldman@chain-stock.com>
Added a `helpers` module into `models` containing the functions `taskresult_model()` and `groupresult_model()`. * `taskresult_model()`: will try to find the custom model using a dotted path defined under the constant `CELERY_RESULTS_TASKRESULT_MODEL` in the settings of the user's project * `groupresult_model()` will try to do the same using under the constant `CELERY_RESULTS_GROUPRESULT_MODEL`. By default if these attributes are not found `django-celery-results` will use the default models (`models.TaskResult` and `models.GroupResult`). Updated database backend in order to use custom models for `TaskResult and `GroupResult` it they're present. Instead to import explicitely the `TaskResult` and the `GroupResult` (default models from `django-celery-results`) we make use of the model helpers to load the right classes, the custom ones if they're present otherwise we use the default ones. Getting data from `task_kwargs` to extend the `task_properties` and be able to store them into the database (using the custom models). First of all we need a way to get data from `task_kwargs` (or somewhere else) just before a `task_result` record is created, evaluate that data and find the right values that will be used to fill the new fields defined in the custom model. So for this purpose we added a settings module to `django-celery-results` which will hold default settings, the first setting that will contain is a function in charge to get a callback from the settings of the user project. This callback will be feeded by the task `task_kwargs`, which will be intercepted in `DatabaseBackend._get_extended_properties` just before the `task_kwargs` are encoded by `encode_content()` method and send it to the `store_result` method from the object manager of `TaskModel` (Custom/Default one). To end, we must to extend the arguments of the `store_result` method from the `TaskResult` Manager adding `extra_fields` argument that will make us able to send extra data to the custom model, when it's defined. --- Resolves celery#305 Fixes celery#314
`extend_task_props_callback` moved from `_get_extended_properties` to `_store_result`. Suggested by @AllesVeldman. `extend_task_props_callback` will get the `request` object as first parameter and a copy of `task_props` (avoiding potential manipulation of the original `task_props`) as second paramenter. --- Resolves celery#305 Fixes celery#314
`get_callback_function()` gets a default callback as an arg returning explicitely an empty dict. `get_callback_function()` raises an `ImproperlyConfigured` exception when the callback is not callable. --- Resolves celery#305 Fixes celery#314
Added `get_task_props_extension` to `settings` module which will raise an `ImproperlyConfigured` when the task_props_extension doesn't complies with the Mapping protocol. `DatabaseBackend` will make use of `get_task_props_extension` to update a potential custom model with the custom properties --- Resolves celery#305 Fixes celery#314
A new page added to the documentation where we explain how to use this feature. -- issue: celery#305 pull-request: celery#314
for more information, see https://pre-commit.ci
…in `django_celery_results.settings` Fixed a "wrong" description for the `ImproperlyConfigured` exception raised when `task_props_extension` doesn't complies with the Mapping protocol. At this point `task_props_extension` is just a dict that inherits from Mapping, not an explicit instance. Thanks @AllexVeldman Co-authored-by: Allex <a.veldman@chain-stock.com>
Co-authored-by: Allex <a.veldman@chain-stock.com>
Co-authored-by: Allex <a.veldman@chain-stock.com>
Co-authored-by: Allex <a.veldman@chain-stock.com>
- Added new abstract model `AbstractChordCounter` in `abstract.py` for Chord synchronization, including fields for `group_id`, `sub_tasks`, and `count`. - Updated `ChordCounter` model in `generic.py` to inherit from `AbstractChordCounter`. - Moved `group_result` method from `ChordCounter` to `AbstractChordCounter`. - Added helper function `chordcounter_model` in `helpers.py` to return the active `ChordCounter` model. - Updated import statements in `database.py` to use `chordcounter_model` helper function. - Added `ChordCounterModel` attribute to `DatabaseBackend` class, and updated `create` and `get` methods to use `ChordCounterModel` instead of `ChordCounter`. Relates to: celery#305
@auvipy I just rebased the branch and fixed some last minute/minute issues. |
Added support for abstract models. #305