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

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
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.