Skip to content

Commit 93ca3dd

Browse files
authored
docs: markdown migration (#894)
1 parent 4b43714 commit 93ca3dd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1954
-0
lines changed

docs/actions/action-form-example.md

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
---
2+
title: Action with form example
3+
description: Actions containing custom form example.
4+
order: 5
5+
---
6+
7+
# Action with form example
8+
9+
Below is an example of an action that will display a form after clicking on the action button on the detail object page.
10+
11+
```python
12+
# admin.py
13+
14+
from django import forms
15+
from django.contrib.auth.models import User
16+
from django.http import HttpRequest
17+
from django.shortcuts import render
18+
from django.urls import reverse_lazy
19+
from django.utils.translation import gettext_lazy as _
20+
21+
from unfold.admin import ModelAdmin
22+
from unfold.decorators import action
23+
from unfold.widgets import UnfoldAdminTextInputWidget, UnfoldAdminSplitDateTimeWidget
24+
25+
26+
class SomeForm(forms.Form):
27+
# It is important to set a widget coming from Unfold
28+
date_start = forms.SplitDateTimeField(label=_("Start"), widget=UnfoldAdminSplitDateTimeWidget)
29+
date_end = forms.SplitDateTimeField(label=_("End"), widget=UnfoldAdminSplitDateTimeWidget)
30+
note = forms.CharField(label=_("Note"), widget=UnfoldAdminTextInputWidget)
31+
32+
# Loads date widget required JS files
33+
class Media:
34+
js = [
35+
"admin/js/vendor/jquery/jquery.js",
36+
"admin/js/jquery.init.js",
37+
"admin/js/calendar.js",
38+
"admin/js/admin/DateTimeShortcuts.js",
39+
"admin/js/core.js",
40+
]
41+
42+
43+
@register(User)
44+
class UserAdmin(ModelAdmin):
45+
actions_detail = ["change_detail_action"]
46+
47+
@action(description=_("Change detail action"), url_path="change-detail-action")
48+
def change_detail_action(self, request: HttpRequest, object_id: int) -> str:
49+
# Check if object already exists, otherwise returs 404
50+
obj = get_object_or_404(User, pk=object_id)
51+
form = SomeForm(request.POST or None)
52+
53+
if request.method == "POST" and form.is_valid():
54+
# Process form data
55+
# form.cleaned_data["note"]
56+
# form.cleaned_data["date_from"]
57+
# form.cleaned_data["date_to"]
58+
59+
messages.success(request, _("Change detail action has been successful."))
60+
61+
return redirect(
62+
reverse_lazy("admin:app_model_change", args=[object_id])
63+
)
64+
65+
return render(
66+
request,
67+
"some/action.html",
68+
{
69+
"form": form,
70+
"object": obj,
71+
"title": _("Change detail action for {}").format(obj),
72+
**self.admin_site.each_context(request),
73+
},
74+
)
75+
```
76+
77+
Template displaying the form. Please note that breadcrumbs are empty in this case but if you want, you can configure your own breadcrumbs path.
78+
79+
```html
80+
{% extends "admin/base_site.html" %}
81+
82+
{% load i18n unfold %}
83+
84+
{% block breadcrumbs %}{% endblock %}
85+
86+
{% block extrahead %}
87+
{{ block.super }}
88+
<script src="{% url 'admin:jsi18n' %}"></script>
89+
{{ form.media }}
90+
{% endblock %}
91+
92+
{% block content %}
93+
<form action="" method="post" novalidate>
94+
<div class="aligned border border-gray-200 mb-8 rounded-md pt-3 px-3 shadow-sm dark:border-gray-800">
95+
{% csrf_token %}
96+
97+
{% for field in form %}
98+
{% include "unfold/helpers/field.html" with field=field %}
99+
{% endfor %}
100+
</div>
101+
102+
<div class="flex justify-end">
103+
{% component "unfold/components/button.html" with submit=1 %}
104+
{% trans "Submit form" %}
105+
{% endcomponent %}
106+
</div>
107+
</form>
108+
{% endblock %}
109+
```

docs/actions/changeform-submitline.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: Changeform submitline actions
3+
description: Changeform submitline actions for detail view.
4+
order: 4
5+
---
6+
7+
# Changeform submitline actions
8+
9+
Submit row actions work a bit differently when compared to other custom Unfold actions. These actions first invoke form save (same as if you hit `Save` button) and then lets you perform additional logic on already saved instance.
10+
11+
```python
12+
# admin.py
13+
14+
from django.contrib.admin import register
15+
from django.contrib.auth.models import User
16+
from django.utils.translation import gettext_lazy as _
17+
from django.http import HttpRequest
18+
from unfold.admin import ModelAdmin
19+
from unfold.decorators import action
20+
21+
22+
@register(User)
23+
class UserAdmin(ModelAdmin):
24+
actions_submit_line = ["changeform_submitline_action"]
25+
26+
@action(
27+
description=_("Changeform submitline action"),
28+
permissions=["changeform_submitline_action"]
29+
)
30+
def changeform_submitline_action(self, request: HttpRequest, obj: User):
31+
"""
32+
If instance is modified in any way, it also needs to be saved, since this handler is invoked after instance is saved.
33+
"""
34+
35+
obj.is_active = True
36+
obj.save()
37+
38+
def has_changeform_submitline_action_permission(self, request: HttpRequest, object_id: Union[str, int]):
39+
pass
40+
```

docs/actions/changeform.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
title: Changeform actions
3+
description: Changeform actions for detail view.
4+
order: 3
5+
---
6+
7+
# Changeform actions
8+
9+
```python
10+
# admin.py
11+
12+
from django.contrib.admin import register
13+
from django.contrib.auth.models import User
14+
from django.shortcuts import redirect
15+
from django.urls import reverse_lazy
16+
from django.utils.translation import gettext_lazy as _
17+
from django.http import HttpRequest
18+
from unfold.admin import ModelAdmin
19+
from unfold.decorators import action
20+
21+
22+
@register(User)
23+
class UserAdmin(ModelAdmin):
24+
actions_detail = ["changeform_action"]
25+
26+
@action(
27+
description=_("Changeform action"),
28+
url_path="changeform-action",
29+
attrs={"target": "_blank"},
30+
permissions=["changeform_action"]
31+
)
32+
def changeform_action(self, request: HttpRequest, object_id: int):
33+
user = User.objects.get(pk=object_id)
34+
user.block()
35+
36+
return redirect(
37+
reverse_lazy("admin:users_user_change", args=(object_id,))
38+
)
39+
40+
41+
def has_changeform_action_permission(self, request: HttpRequest, object_id: Union[str, int]):
42+
pass
43+
```

docs/actions/changelist-row.md

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
title: Changelist row actions
3+
description: Changelist row actions for list view.
4+
order: 2
5+
---
6+
7+
# Changelist row actions
8+
9+
These actions will appear on each row on the changelist page as a dropdown button containing all custom row actions.
10+
11+
```python
12+
# admin.py
13+
14+
from django.contrib.admin import register
15+
from django.contrib.auth.models import User
16+
from django.shortcuts import redirect
17+
from django.urls import reverse_lazy
18+
from django.utils.translation import gettext_lazy as _
19+
from django.http import HttpRequest
20+
from unfold.admin import ModelAdmin
21+
from unfold.decorators import action
22+
23+
24+
@register(User)
25+
class UserAdmin(ModelAdmin):
26+
actions_row = ["changelist_row_action"]
27+
28+
@action(
29+
description=_("Changelist row action"),
30+
permissions=["changelist_row_action"],
31+
url_path="changelist-row-action",
32+
attrs={"target": "_blank"}
33+
)
34+
def changelist_row_action(self, request: HttpRequest, object_id: int):
35+
return redirect(
36+
reverse_lazy("admin:users_user_changelist")
37+
)
38+
39+
def has_changelist_row_action_permission(self, request: HttpRequest):
40+
pass
41+
```

docs/actions/changelist.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: Changelist actions
3+
description: Changelist actions for list view.
4+
order: 1
5+
---
6+
7+
# Changelist actions
8+
9+
These actions will appear at the top of the changelist page as buttons. Please not that these actions are not displayed in the actions dropdown which is provided by default in Django. Changelist action will not reciver any queryset or object ids, because it is meant to be used for general actions for given model.
10+
11+
```python
12+
# admin.py
13+
14+
from django.contrib.admin import register
15+
from django.contrib.auth.models import User
16+
from django.shortcuts import redirect
17+
from django.urls import reverse_lazy
18+
from django.utils.translation import gettext_lazy as _
19+
from django.http import HttpRequest
20+
from unfold.admin import ModelAdmin
21+
from unfold.decorators import action
22+
23+
24+
@register(User)
25+
class UserAdmin(ModelAdmin):
26+
actions_list = ["changelist_action"]
27+
28+
@action(description=_("Changelist action"), url_path="changelist-action")
29+
def changelist_action(self, request: HttpRequest, permissions=["changelist_action"]):
30+
return redirect(
31+
reverse_lazy("admin:users_user_changelist")
32+
)
33+
34+
def has_changelist_action_permission(self, request: HttpRequest):
35+
pass
36+
```

docs/actions/index.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: Actions
3+
order: 4
4+
description: Introduction to Unfold actions
5+
---

docs/actions/introduction.md

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
title: Introduction to actions
3+
order: 0
4+
description: Run custom admin actions from different places.
5+
---
6+
7+
# Actions
8+
9+
It is highly recommended to read the base [Django actions documentation](https://docs.djangoproject.com/en/5.1/ref/contrib/admin/actions/) before reading this section, since Unfold actions are derived from Django actions.
10+
11+
```python
12+
# admin.py
13+
14+
from django.auth.models import User
15+
from django.contrib import admin
16+
from django.db.models import QuerySet
17+
from django.http import HttpRequest
18+
from unfold.admin import ModelAdmin
19+
from unfold.decorators import action # Import @action decorator from Unfold
20+
21+
@admin.register(User)
22+
class UserAdmin(ModelAdmin):
23+
actions_list = ["custom_action"]
24+
25+
@action(description="Custom action")
26+
def custom_action(self, request: HttpRequest, queryset: QuerySet):
27+
pass
28+
```
29+
30+
## Actions overview
31+
32+
Besides traditional actions selected from dropdown, Unfold supports several other types of actions. Following table gives overview of all available actions together with their recommended usage:
33+
34+
| Type | Appearance | Usage |
35+
| -------------- | ------------------------------ | ----------------------------------------------------------------------|
36+
| Global | Changelist - top | General actions for all instances in listing |
37+
| Row | Changelist - each row | Action for one specific instance, executable from listing |
38+
| Detail | Changeform - top | Action for one specific instance, executable from detail |
39+
| Submit line | Changeform - submit line | Action performed during form submit (instance save) |
40+
41+
## For global, row and detail action
42+
43+
All these actions are based on custom URLs generated for each of them. Handler function for these views is basically function based view.
44+
45+
For actions without intermediate steps, you can write all the logic inside handler directly. Request and object ID are both passed to these action handler functions, so you are free to fetch the instance from database and perform any operations with it. In the end, it is recommended to return redirect back to either detail or listing based on where the action was triggered from.
46+
47+
For actions with intermediate steps, it is recommended to use handler function only to redirect to custom URL with custom view. This view can be extended from base Unfold view, to have unified experience.

0 commit comments

Comments
 (0)