Skip to content

Commit

Permalink
Merge pull request #51 from akodelia/feature/LITE-21102-New-mixin-to-…
Browse files Browse the repository at this point in the history
…allow-sync-items-from-django-admin-site

Mixin to add cqrs sync action on Django Admin
  • Loading branch information
maxipavlovic authored Nov 23, 2021
2 parents e317790 + 06005f6 commit b5412d6
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 0 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,28 @@ class FilteredSimplestModel(MasterMixin, models.Model):
return len(str(self.name)) > 2
```

Django Admin
-----------

Add action to synchronize master items from Django Admin page.

```python
from django.db import models
from django.contrib import admin

from dj_cqrs.admin_mixins import CQRSAdminMasterSyncMixin


class AccountAdmin(CQRSAdminMasterSyncMixin, admin.ModelAdmin):
...


admin.site.register(models.Account, AccountAdmin)

```

* If necessary, override ```_cqrs_sync_queryset``` from ```CQRSAdminMasterSyncMixin``` to adjust the QuerySet and use it for synchronization.


Utilities
---------
Expand Down
52 changes: 52 additions & 0 deletions dj_cqrs/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright © 2021 Ingram Micro Inc. All rights reserved.

from django.utils.translation import gettext_lazy


class CQRSAdminMasterSyncMixin:
"""
Mixin that includes a custom action in AdminModel. This action allows synchronizing
master's model items from Django Admin page,
"""

def get_actions(self, request):
"""
Overriding method from AdminModel class; it is used to include the sync method in
the actions list.
"""
if self.actions is not None:
self.actions = self.actions + ['sync_items']
return super().get_actions(request)

def _cqrs_sync_queryset(self, queryset):
"""
This function is used to adjust the QuerySet before sending the sync signal.
:param queryset: Original queryset
:type queryset: Queryset
:return: Updated queryset
:rtype: Queryset
"""
return queryset

def sync_items(self, request, queryset):
"""
This method synchronizes selected items from the Admin Page.
It is registered as a custom action in Django Admin
"""
items_not_synced = []
for item in self._cqrs_sync_queryset(queryset):
if not item.cqrs_sync():
items_not_synced.append(item)

total = len(queryset)
total_w_erros = len(items_not_synced)
total_sucess = total - total_w_erros
self.message_user(
request,
f'{total_sucess} successfully synced. {total_w_erros} failed: {items_not_synced}',
)

sync_items.short_description = gettext_lazy(
'Synchronize selected %(verbose_name_plural)s via CQRS',
)
24 changes: 24 additions & 0 deletions docs/admin.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Django Admin
====================

Synchronize items
-----------------

Add action to synchronize master items from Django Admin page.

.. code-block:: python
from django.db import models
from django.contrib import admin
from dj_cqrs.admin_mixins import CQRSAdminMasterSyncMixin
class AccountAdmin(CQRSAdminMasterSyncMixin, admin.ModelAdmin):
pass
admin.site.register(models.Account, AccountAdmin)
* If necessary, override ``_cqrs_sync_queryset`` from ``CQRSAdminMasterSyncMixin`` to adjust the QuerySet and use it for synchronization.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ and that's okay for non-critical business transactions.
:caption: Contents:

getting_started
admin
custom_serialization
track_fields_changes
transports
Expand Down
7 changes: 7 additions & 0 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ API Reference
=============


Django Admin
------------

.. autoclass:: dj_cqrs.admin.CQRSAdminMasterSyncMixin
:members: sync_items, _cqrs_sync_queryset


Mixins
------

Expand Down
68 changes: 68 additions & 0 deletions tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Copyright © 2021 Ingram Micro Inc. All rights reserved.

from dj_cqrs.admin import CQRSAdminMasterSyncMixin

from django.contrib import admin

import pytest


class MockAdminModel(CQRSAdminMasterSyncMixin, admin.ModelAdmin):
pass


@pytest.mark.parametrize(
('total_sync', 'total_failed'),
(
(2, 1),
(3, 0),
(0, 3),
(0, 0),
),
)
def test_sync_items_function(total_sync, total_failed, mocker):
model_path = 'dj_cqrs.mixins.MasterMixin'
mock_model = mocker.patch(model_path)
request = None

qs = []
for _ in range(total_sync):
m = mocker.patch(model_path)
m.cqrs_sync.return_value = True
qs.append(m)

failed_items = []
for _ in range(total_failed):
m = mocker.patch(model_path)
m.cqrs_sync.return_value = False
qs.append(m)
failed_items.append(m)

mixin = MockAdminModel(model=mock_model, admin_site=admin.sites.AdminSite())
mixin.message_user = mocker.Mock()
mixin.sync_items(request, qs)

mixin.message_user.assert_called_once_with(
request,
f'{total_sync} successfully synced. {total_failed} failed: {failed_items}',
)


def test_admin_actions_enabled_with_sync_items_action(mocker):
mock_model = mocker.Mock()
request = mocker.patch('django.http.HttpRequest')
mixin = MockAdminModel(model=mock_model, admin_site=admin.sites.AdminSite())
actions = mixin.get_actions(request)

assert 'sync_items' in actions
assert 'delete_selected' in actions


def test_actions_not_enabled(mocker):
mock_model = mocker.Mock()
request = mocker.patch('django.http.HttpRequest')
mixin = MockAdminModel(model=mock_model, admin_site=admin.sites.AdminSite())
mixin.actions = None
actions = mixin.get_actions(request)

assert actions == {}

0 comments on commit b5412d6

Please sign in to comment.