Skip to content

Commit

Permalink
Improved docs
Browse files Browse the repository at this point in the history
  • Loading branch information
abondar committed Apr 28, 2024
1 parent 2616f32 commit 94962af
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 47 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ permissions:
id-token: write
pages: write
on:
push:
branches:
- main
release:
types:
- created
jobs:
build:
runs-on: ubuntu-latest
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ Changelog
0.1
===

0.1.3
-----
- Improved docs

0.1.1
-----

- Specified python >=3.11

Expand Down
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ even if there are issues with matching objects in origin location with objects i
It is not used in this example, but you can read more about it in overview section of this documentation.
5. We execute the copy request.
.. code-block:: python
result = Copyist(copy_request).execute_copy_request()
Expand Down
10 changes: 5 additions & 5 deletions django_copyist/copy_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@ class CopyResult:
"""
This is the base class for a copy result, which serves as the output for the Copyist.
:param is_copy_successful: A flag indicating whether the copy operation was successful,
:ivar is_copy_successful: A flag indicating whether the copy operation was successful,
defaults to False.
:type is_copy_successful: bool, optional
:param output_map: A dictionary that contains a mapping of model names to mappings of
:ivar output_map: A dictionary that contains a mapping of model names to mappings of
primary keys in the source and destination databases, defaults to None.
:type output_map: dict, optional
:param ignored_map: A dictionary that contains a mapping of model names to lists of
:ivar ignored_map: A dictionary that contains a mapping of model names to lists of
primary keys of models that were ignored during the copying process, defaults to None.
:type ignored_map: dict, optional
:param set_to_filter_map: A dictionary that contains data of substitutes matched by
:ivar set_to_filter_map: A dictionary that contains data of substitutes matched by
the `SET_TO_FILTER` action. The structure is as follows:
.. code-block:: python
Expand All @@ -90,7 +90,7 @@ class CopyResult:
Defaults to None.
:type set_to_filter_map: dict, optional
:param reason: The reason code, returned if `is_copy_successful` is False, defaults to None.
:ivar reason: The reason code, returned if `is_copy_successful` is False, defaults to None.
:type reason: AbortReason, optional
"""

Expand Down
17 changes: 16 additions & 1 deletion django_copyist/copyist.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ def __repr__(self):
@dataclass
class CopyistConfig:
"""
Root copy config, containing ModelCopyConfig list
This is the root copy configuration that contains a list of ModelCopyConfig.
:ivar list[ModelCopyConfig] model_configs: The list of model configurations.
"""

model_configs: list[ModelCopyConfig]
Expand All @@ -105,6 +107,19 @@ class IgnoreEvaluation:


class Copyist:
"""
The main class responsible for copying Django model instances based on the provided
configuration.
:param CopyRequest copy_request: The copy request object containing the configuration and
input data for the copy operation.
:ivar CopyRequest request: The copy request object containing the configuration
and input data for the copy operation.
:ivar CopyistConfig config: The configuration for the copy operation.
:ivar dict input_data: The input data for the copy operation.
"""

def __init__(self, copy_request: CopyRequest):
self.request = copy_request
self.config = copy_request.config
Expand Down
32 changes: 16 additions & 16 deletions docs/source/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ This way you can first copy all parent models, and then use compound actions to
Closer look at CopyRequest and CopyResult
-----------------------------------------

You probably noticed the :py:attr:`.CopyRequest` and :py:attr:`.CopyResult` classes that are used in the examples above. Let's take a closer look at them.
You probably noticed the :py:class:`~.CopyRequest` and :py:class:`~.CopyResult` classes that are used in the examples above. Let's take a closer look at them.

.. code-block:: python
Expand All @@ -244,28 +244,28 @@ You probably noticed the :py:attr:`.CopyRequest` and :py:attr:`.CopyResult` clas
confirm_write=False,
)
In this example, we create a :py:attr:`.CopyRequest` object.
In this example, we create a :py:class:`~.CopyRequest` object.

:py:attr:`.CopyistConfig` is a class that holds the configuration for the copy process. It takes a list of :py:attr:`.ModelCopyConfig` objects.
It is root config and can have multiple :py:attr:`.ModelCopyConfig` objects if you need to copy several root level models in one request.
:py:class:`~.CopyistConfig` is a class that holds the configuration for the copy process. It takes a list of :py:class:`~.ModelCopyConfig` objects.
It is root config and can have multiple :py:class:`~.ModelCopyConfig` objects if you need to copy several root level models in one request.

:py:attr:`.input_data` is a dictionary that holds the input data for the copy process. It is used to pass data to the copy process. It can be used to pass data that is not present in the original model.

:py:attr:`.confirm_write` is a more confusing one. It is a boolean that tells the copy process if it should write the data even if unmatched or ignored values were discovered during the copy process.

What are unmatched or ignored values? Let's take a look at the :py:attr:`.CopyResult` object.
What are unmatched or ignored values? Let's take a look at the :py:class:`~.CopyResult` object.

:py:attr:`.CopyResult` is an object that holds the result of the copy process.
:py:class:`~.CopyResult` is an object that holds the result of the copy process.

Primarily you should look at attribute :py:attr:`.is_copy_successful`. It is a boolean that tells you if the copy process was successful. If it is `False` you should look at the :py:attr:`.reason` attribute. It is a enum that tells you why the copy process failed.

:py:attr:`.output_map` is a dictionary that holds the mapping of the original model id to the new model id. It can be stored for historical purposes or to be used for UI rendering. This field is populated only on successful copy.

If you copy is unsuccessful, you can look at the :py:attr:`.django_copyist.config.CopyResult.set_to_filter_map` and
:py:attr:`.django_copyist.config.CopyResult.ignored_map` attributes.
If you copy is unsuccessful, you can look at the :py:attr:`~django_copyist.config.CopyResult.set_to_filter_map` and
:py:attr:`~django_copyist.config.CopyResult.ignored_map` attributes.
They are dictionaries that hold the mapping of the original model id
to matched ids on :py:attr:`.django_copyist.config.CopyResult.set_to_filter_map`
and ignored fields on :py:attr:`.SET_TO_FILTER` action or :py:attr:`.django_copyist.config.ModelCopyConfig.ignore_condition` respectively.
to matched ids on :py:attr:`~django_copyist.config.CopyResult.set_to_filter_map`
and ignored fields on :py:attr:`.SET_TO_FILTER` action or :py:attr:`~django_copyist.config.ModelCopyConfig.ignore_condition` respectively.

Why would you use this attributes? Let's see following examples

Expand Down Expand Up @@ -471,17 +471,17 @@ Above example is great and works well, but what if destination project doesn't h
Here we are working with the same config as in the previous example, but now we have `Counterpart` with `external_id` 2 only in the `Project1` and not in the `Project2`.
And it's here where :py:class:`.CopyResult` comes into play. We can see that the copy process failed because the counterpart with `external_id` 2 was not found in the destination project.

By observing the :py:attr:`.django_copyist.config.CopyResult.set_to_filter_map` attribute, we can see that the counterpart with `external_id` 2 was not
By observing the :py:attr:`.CopyResult.set_to_filter_map` attribute, we can see that the counterpart with `external_id` 2 was not
matched.

If it is happening in interactive context, you can prompt user to resolve this issue or accept the fact that some data won't be copied.

If we want to confirm that the copy process should continue regardless, we can set the :py:attr:`.confirm_write` attribute to `True` and pass the :py:attr:`.django_copyist.config.CopyResult.set_to_filter_map` attribute to the :py:attr:`.CopyRequest` object.
If we want to confirm that the copy process should continue regardless, we can set the :py:attr:`.confirm_write` attribute to `True` and pass the :py:attr:`.django_copyist.config.CopyResult.set_to_filter_map` attribute to the :py:class:`~.CopyRequest` object.

.. note::

The :py:attr:`django_copyist.config.CopyRequest.set_to_filter_map` is passed, so that :py:class:`.Copyist` can verify that list of unmatched
values remained the same between copy calls. If it changed, unsuccessful result with reason :py:attr:`.django_copyist.config.AbortReason.DATA_CHANGED_STF` will be returned.
The :py:attr:`.CopyRequest.set_to_filter_map` is passed, so that :py:class:`~django_copyist.copyist.Copyist` can verify that list of unmatched
values remained the same between copy calls. If it changed, unsuccessful result with reason :py:attr:`~.DATA_CHANGED_STF` will be returned.

Set to filter using custom function
------------------------------------
Expand Down Expand Up @@ -600,7 +600,7 @@ You can read more on signature at :protocol:`~.django_copyist.config.SetToFilter
Ignoring models during copy with SET_TO_FILTER
------------------------------------------------

You probably noticed the :py:attr:`.django_copyist.copy_request.CopyResult.ignored_map` attribute in the previous examples.
You probably noticed the :py:attr:`.CopyResult.ignored_map` attribute in the previous examples.
So how exactly it is used?

For example, lets assume you want to have same config as in `SET_TO_FILTER` example, but you want to ignore `Task` model if it can't match all counterparts:
Expand Down Expand Up @@ -1240,7 +1240,7 @@ Also :py:class:`~.PostcopyStep` supports `filter_field_to_input_key` with :py:at
Note on ordering of operations
------------------------------

Now that we are familiar with different steps that :py:class:`~.Copyist` can go through in process,
Now that we are familiar with different steps that :py:class:`~django_copyist.copyist.Copyist` can go through in process,
let's talk about the order of operations.

The order of operations is as follows:
Expand Down
43 changes: 22 additions & 21 deletions docs/source/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Assuming you have following models in your Django app:
And you want to create full copy of company with all nested data, but also want it to be created with different name and address.
In this case you should write following ModelCopyConfig
In this case you should write following :py:class:`~.django_copyist.config.ModelCopyConfig`

.. code-block:: python
Expand Down Expand Up @@ -162,7 +162,7 @@ And then you can execute copy action like this:
With this, all company data should be copied.
That seems like a lot to take in, so let's break it down to what exactly happens here:

1. We define a `ModelCopyConfig` for the `Company` model.
1. We define a :py:class:`~.django_copyist.config.ModelCopyConfig` for the `Company` model.

.. code-block:: python
Expand All @@ -171,9 +171,9 @@ That seems like a lot to take in, so let's break it down to what exactly happens
filter_field_to_input_key={"id": "company_id"},
...
`ModelCopyConfig` is a class that defines how to copy a model. It takes the model class as the first argument and a dictionary that maps the filter field to the input key. This is used to find the object to copy.
:py:class:`~.django_copyist.config.ModelCopyConfig` is a class that defines how to copy a model. It takes the model class as the first argument and a dictionary that maps the filter field to the input key. This is used to find the object to copy.
2. Next we define `field_copy_actions` for the `Company` model.
2. Next we define :py:attr:`.ModelCopyConfig.field_copy_actions` for the `Company` model.
.. code-block:: python
Expand Down Expand Up @@ -213,19 +213,19 @@ That seems like a lot to take in, so let's break it down to what exactly happens
),
...
`field_copy_actions` is a dictionary that maps the field name to a `FieldCopyConfig` object.
:py:attr:`.ModelCopyConfig.field_copy_actions` is a dictionary that maps the field name to a :py:class:`~.FieldCopyConfig` object.
The `FieldCopyConfig` object defines how to copy the field. In this case, we take the `name` and `address` fields from the input data.
The :py:class:`~.FieldCopyConfig` object defines how to copy the field. In this case, we take the `name` and `address` fields from the input data.
`TAKE_FROM_ORIGIN` is a shortcut for creating `FieldCopyConfig` with `CopyActions.TAKE_FROM_ORIGIN` action, which takes value for new object from original object.
:py:attr:`~django_copyist.config.TAKE_FROM_ORIGIN` is a shortcut for creating :py:class:`~.FieldCopyConfig` with :py:attr:`~.CopyActions.TAKE_FROM_ORIGIN` action, which takes value for new object from original object.
We also define how to copy the `projects` and `employees` fields.
We use the `MakeCopy` action to copy the related objects.
`MakeCopy` is a shortcut for creating `FieldCopyConfig` with `CopyActions.MAKE_COPY` action and reference to given model.
Nested `MakeCopy` automatically propagate parent id to child object.
We use the :py:attr:`~.MakeCopy` action to copy the related objects.
:py:attr:`~.MakeCopy` is a shortcut for creating :py:class:`~.FieldCopyConfig` with :py:attr:`CopyActions.MAKE_COPY` action and reference to given model.
Nested :py:attr:`~.MakeCopy` automatically propagate parent id to child object.
3. We define `compound_copy_actions` for the `Company` model.
3. We define :py:attr:`~.ModelCopyConfig.compound_copy_actions` for the `Company` model.
.. code-block:: python
Expand All @@ -242,17 +242,17 @@ Nested `MakeCopy` automatically propagate parent id to child object.
)
...
`compound_copy_actions` is a list of `ModelCopyConfig` objects that define how
:py:attr:`~.ModelCopyConfig.compound_copy_actions` is a list of :py:class:`~.ModelCopyConfig` objects that define how
to copy related objects that are not directly related to the model, or related through multiple relations that need to be created beforehand.
`compound_copy_actions` are executed after all fields are copied.
:py:attr:`~.ModelCopyConfig.compound_copy_actions` are executed after all fields are copied.
In this case, we define how to copy the `Task` model. We take the `name` and `description` fields from the original object. We also define how to copy the `counterparts`, `project`, and `assignee` fields.
`UpdateToCopied` is a shortcut for creating `FieldCopyConfig` with `CopyActions.UPDATE_TO_COPIED` action and reference to given model.
:py:func:`~.UpdateToCopied` is a shortcut for creating :py:class:`~.FieldCopyConfig` with :py:attr:`CopyActions.UPDATE_TO_COPIED` action and reference to given model.
It will search mapping of previously copied objects and update reference to copied object.
4. We create a `CopyRequest` object with the `CopyistConfig` and input data.
4. We create a :py:class:`~.CopyRequest` object with the :py:class:`~.CopyistConfig` and input data.
.. code-block:: python
Expand All @@ -267,24 +267,25 @@ It will search mapping of previously copied objects and update reference to copi
)
...
`CopyRequest` is a class that defines the copy request. It takes the `CopyistConfig` object, input data, and a boolean flag that indicates whether to confirm the write operation.
:py:class:`~.CopyRequest` is a class that defines the copy request. It takes the `CopyistConfig` object, input data, and a boolean flag that indicates whether to confirm the write operation.
`CopyistConfig` is a class that defines the configuration for the copy operation. It takes a list of `ModelCopyConfig` objects.
:py:class:`~.CopyistConfig` is a class that defines the configuration for the copy operation. It takes a list of :py:class:`~.ModelCopyConfig` objects.
`input_data` is a dictionary that contains the input data for the copy operation. It is later used in filtering or `TAKE_FROM_INPUT` actions.
:py:attr:`.CopyResult.input_data` is a dictionary that contains the input data for the copy operation. It is later used in filtering or :py:attr:`~.TAKE_FROM_INPUT` actions.
`confirm_write` is a boolean flag that indicates whether to confirm the write operation,
:py:attr:`.CopyResult.confirm_write` is a boolean flag that indicates whether to confirm the write operation,
even if there are issues with matching objects in origin location with objects in target destination.
It is not used in this example, but you can read more about it in overview section of this documentation.
5. We execute the copy request.
.. code-block:: python
result = Copyist(copy_request).execute_copy_request()
`Copyist` is a class that executes the copy request. It takes the `CopyRequest` object as an argument.
:py:class:`~django_copyist.copyist.Copyist` is a class that executes the copy request. It takes the :py:class:`~.CopyRequest` object as an argument.
`execute_copy_request` method returns `CopyResult` object that contains information about the copy operation. Read more about it in overview section.
:py:attr:`.CopyResult.execute_copy_request` method returns :py:class:`~.CopyResult` object that contains information about the copy operation. Read more about it in overview section.
And like this you have copied the company with all related data and can see and edit configuration in one place.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "django-copyist"
version = "0.1.2"
version = "0.1.3"
description = "Tool for precise and efficient django model copying"
authors = ["abondar"]
license = "Apache-2.0"
Expand Down

0 comments on commit 94962af

Please sign in to comment.