Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(confirm): confirm page now display previous form
Browse files Browse the repository at this point in the history
confirm page now display previous form for confirmation
Kl0ven committed Oct 17, 2022
1 parent 3005825 commit 00e786e
Showing 8 changed files with 92 additions and 31 deletions.
37 changes: 35 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -139,7 +139,7 @@ Note: `confirmation_fields` apply to both add/change confirmations.
def action1(modeladmin, request, queryset):
# Do something with the queryset

@confirm_action
@confirm_action()
def action2(modeladmin, request, queryset):
# Do something with the queryset

@@ -161,7 +161,7 @@ Action confirmation will respect `allowed_permissions` and the `has_xxx_permissi
class MyModelAdmin(AdminConfirmMixin, DjangoObjectActions, ModelAdmin):
change_actions = ["action1"]

@confirm_action
@confirm_action()
def action1(self, request, object):
# Do something with the object
```
@@ -187,5 +187,38 @@ Action confirmation will respect `allowed_permissions` and the `has_xxx_permissi
# Do something with the object
```

**Chaining tools**

```py
from admin_confirm import AdminConfirmMixin, ActionFormMixin, confirm_action, add_form_to_action
from django_object_actions import DjangoObjectActions
from myapp.form import NoteActionForm

class MyModelAdmin(AdminConfirmMixin, ActionFormMixin, DjangoObjectActions, ModelAdmin):
change_actions = ["action1"]

@add_form_to_action(NoteActionForm)
@confirm_action()
def action1(self, request, object):
# Do something with the object
```
This will chain form and confirmation.
The confirmation page will have the actions & form values displayed.
If you only want the action (same as confirm only), you can pass the following argument

```py
from admin_confirm import AdminConfirmMixin, ActionFormMixin, confirm_action, add_form_to_action
from django_object_actions import DjangoObjectActions
from myapp.form import NoteActionForm

class MyModelAdmin(AdminConfirmMixin, ActionFormMixin, DjangoObjectActions, ModelAdmin):
change_actions = ["action1"]

@add_form_to_action(NoteActionForm)
@confirm_action(display_form=False)
def action1(self, request, object):
# Do something with the object
```

## Development
Check out our [development process](docs/development_process.md) if you're interested.
31 changes: 22 additions & 9 deletions admin_action_tools/admin/confirm_tool.py
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
CONFIRM_ACTION,
CONFIRM_ADD,
CONFIRM_CHANGE,
CONFIRM_FORM,
CONFIRMATION_RECEIVED,
SAVE,
SAVE_ACTIONS,
@@ -392,7 +393,7 @@ def _change_confirmation_view(self, request, object_id, form_url, extra_context)
}
return self.render_change_confirmation(request, context)

def run_confirm_tool(self, func: Callable, request: HttpRequest, queryset_or_object):
def run_confirm_tool(self, func: Callable, request: HttpRequest, queryset_or_object, display_form):
tool_chain: ToolChain = ToolChain(request)
step = tool_chain.get_next_step(CONFIRM_ACTION)

@@ -406,6 +407,12 @@ def run_confirm_tool(self, func: Callable, request: HttpRequest, queryset_or_obj
url = back_url(queryset, self.model._meta)
return HttpResponseRedirect(url)

# FIXME: crud implementation for now
data, metadata = tool_chain.get_tool(CONFIRM_FORM)
form_instance = None
if display_form and data:
form_instance = self.load_form(data, metadata)

# get_actions will only return the actions that are allowed
has_perm = self._get_actions(request).get(func.__name__) is not None

@@ -425,26 +432,32 @@ def run_confirm_tool(self, func: Callable, request: HttpRequest, queryset_or_obj
"submit_action": CONFIRM_ACTION,
"submit_text": "Confirm",
"back_text": "Back",
"form": form_instance,
"readonly": True,
}

# Display confirmation page
return self.render_action_confirmation(request, context)


def confirm_action(func):
def confirm_action(display_form=True):
"""
@confirm_action function wrapper for Django ModelAdmin actions
@confirm_action() function wrapper for Django ModelAdmin actions
Will redirect to a confirmation page to ask for confirmation
Next, it would call the action if confirmed. Otherwise, it would
return to the changelist without performing action.
"""

# make sure tools chain is setup
func = add_finishing_step(func)
def confirm_action_decorator(func):

# make sure tools chain is setup
func = add_finishing_step(func)

@functools.wraps(func)
def func_wrapper(modeladmin: AdminConfirmMixin, request, queryset_or_object):
return modeladmin.run_confirm_tool(func, request, queryset_or_object, display_form)

@functools.wraps(func)
def func_wrapper(modeladmin: AdminConfirmMixin, request, queryset_or_object):
return modeladmin.run_confirm_tool(func, request, queryset_or_object)
return func_wrapper

return func_wrapper
return confirm_action_decorator
Original file line number Diff line number Diff line change
@@ -32,14 +32,19 @@
<li>{{ obj }}</li>
{% endfor %}
</ul>

{% include "include/form.html" %}

<form method="post">{% csrf_token %}
{% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}">
{% endfor %}
<input type="hidden" name="action" value="{{ action }}">


{% include "include/submit_row.html" %}


</form>
{% else %}
<p>{% trans "You don't have permissions to perform action" %} {{ action_display_name }} {% trans 'on' %} {{ opts.verbose_name_plural|capfirst }}</p>
17 changes: 2 additions & 15 deletions admin_action_tools/templates/admin/form_tool/action_form.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "admin/base_site.html" %}
{% load i18n l10n admin_urls static widget_tweaks formatting %}
{% load i18n l10n admin_urls static formatting %}

{% block extrahead %}
{{ block.super }}
@@ -39,21 +39,8 @@
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}">
{% endfor %}

<div>
{% for field in form.visible_fields %}
<div class="form-row field-reference aligned">
{{ field.errors }}

{{ field.label_tag }}
{% render_field field %}

<div class="help">
{{ field.help_text }}
</div>
</div>
{% endfor %}
</div>

{% include "include/form.html" %}
{% include "include/submit_row.html" %}

</form>
21 changes: 21 additions & 0 deletions admin_action_tools/templates/include/form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% load widget_tweaks %}
{% if form %}
<div>
{% for field in form.visible_fields %}
<div class="form-row field-reference aligned">
{{ field.errors }}

{{ field.label_tag }}
{% if readonly %}
{% render_field field|attr:"readonly:readonly" %}
{% else %}
{% render_field field %}
{% endif %}
<div class="help">
{{ field.help_text }}
</div>
</div>
{% endfor %}
</div>

{% endif %}
Original file line number Diff line number Diff line change
@@ -60,10 +60,12 @@ def test_change_form_and_confirm(self):
# Should ask for confirmation of action
self.assertIn(CONFIRM_ACTION, self.selenium.page_source)

self.assertIn("aaaaaa", self.selenium.page_source)

self.selenium.find_element(By.NAME, CONFIRM_ACTION).click()

inv1.refresh_from_db()
self.assertTrue("aaaaaa" in inv1.notes)
self.assertIn("aaaaaa", inv1.notes)

def test_change_form_and_confirm_form_back(self):
shop = ShopFactory()
6 changes: 3 additions & 3 deletions tests/market/admin/inventory_admin.py
Original file line number Diff line number Diff line change
@@ -20,12 +20,12 @@ class InventoryAdmin(AdminConfirmMixin, ActionFormMixin, DjangoObjectActions, Mo
change_actions = ["quantity_up", "add_notes", "add_notes_with_confirmation"]
changelist_actions = ["quantity_down"]

@confirm_action
@confirm_action()
def quantity_up(self, request, obj):
obj.quantity = obj.quantity + 1
obj.save()

@confirm_action
@confirm_action()
def quantity_down(self, request, queryset):
queryset.update(quantity=0)

@@ -35,7 +35,7 @@ def add_notes(self, request, object, form=None):
object.save()

@add_form_to_action(NoteActionForm)
@confirm_action
@confirm_action()
def add_notes_with_confirmation(self, request, object, form=None):
object.notes += f"\n\n{form.cleaned_data['date']}\n{form.cleaned_data['note']}"
object.save()
2 changes: 1 addition & 1 deletion tests/market/admin/shop_admin.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ class ShopAdmin(AdminConfirmMixin, ModelAdmin):
actions = ["show_message", "show_message_no_confirmation"]
search_fields = ["name"]

@confirm_action
@confirm_action()
def show_message(modeladmin, request, queryset):
shops = ", ".join(shop.name for shop in queryset)
modeladmin.message_user(request, f"You selected with confirmation: {shops}")

0 comments on commit 00e786e

Please sign in to comment.