Skip to content

Commit 2d2a689

Browse files
authored
change README to Markdown to simplify things (#104)
I'm not planning on doing a Sphinx site, so let's use Markdown to make the documentation easier to maintain. Part of #94
1 parent 399affa commit 2d2a689

File tree

4 files changed

+243
-256
lines changed

4 files changed

+243
-256
lines changed

Makefile

+3-4
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,10 @@ resetdb: ## Delete and then recreate the dev sqlite database
5252
python $(MANAGE) migrate --noinput
5353
python $(MANAGE) loaddata sample_data
5454

55-
.PHONY: build
56-
build: ## Build a full set of Docker images
57-
build: build/2.2.6 build/2.1.13 build/2.0.13 build/1.11.25 build/1.10.8 build/1.9.13 build/1.8.18
55+
docker/build: ## Build a full set of Docker images
56+
docker/build: docker/build/2.2.6 docker/build/2.1.13 docker/build/2.0.13 docker/build/1.11.25 docker/build/1.10.8 docker/build/1.9.13 docker/build/1.8.18
5857

59-
build/%:
58+
docker/build/%:
6059
docker build --build-arg DJANGO_VERSION=$* \
6160
-t $(IMAGE):$$(echo "$*" | cut -f 1-2 -d.) .
6261

README.md

+238
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
Django Object Actions
2+
=====================
3+
4+
[![Build Status](https://travis-ci.org/crccheck/django-object-actions.svg?branch=master)](https://travis-ci.org/crccheck/django-object-actions)
5+
6+
If you've ever tried making admin object tools you may have thought, "why can't
7+
this be as easy as making Django Admin Actions?" Well now they can be.
8+
9+
10+
Quick-Start Guide
11+
-----------------
12+
13+
Install Django Object Actions:
14+
15+
```shell
16+
$ pip install django-object-actions
17+
```
18+
19+
Add `django_object_actions` to your `INSTALLED_APPS` so Django can find
20+
our templates.
21+
22+
In your admin.py:
23+
24+
```python
25+
from django_object_actions import DjangoObjectActions
26+
27+
class ArticleAdmin(DjangoObjectActions, admin.ModelAdmin):
28+
def publish_this(self, request, obj):
29+
publish_obj(obj)
30+
publish_this.label = "Publish" # optional
31+
publish_this.short_description = "Submit this article" # optional
32+
33+
change_actions = ('publish_this', )
34+
```
35+
36+
Usage
37+
-----
38+
39+
Defining new &*tool actions* is just like defining regular [admin actions]. The
40+
major difference is the functions for `django-object-actions` will take an
41+
object instance instead of a queryset (see *Re-using Admin Actions* below).
42+
43+
*Tool actions* are exposed by putting them in a `change_actions` attribute in
44+
your `admin.ModelAdmin`. You can also add *tool actions* to the main changelist
45+
views too. There, you'll get a queryset like a regular [admin action][admin actions]:
46+
47+
```python
48+
from django_object_actions import DjangoObjectActions
49+
50+
class MyModelAdmin(DjangoObjectActions, admin.ModelAdmin):
51+
def toolfunc(self, request, obj):
52+
pass
53+
toolfunc.label = "This will be the label of the button" # optional
54+
toolfunc.short_description = "This will be the tooltip of the button" # optional
55+
56+
def make_published(modeladmin, request, queryset):
57+
queryset.update(status='p')
58+
59+
change_actions = ('toolfunc', )
60+
changelist_actions = ('make_published', )
61+
```
62+
63+
Just like admin actions, you can send a message with `self.message_user`.
64+
Normally, you would do something to the object and return to the same url, but
65+
if you return a `HttpResponse`, it will follow it (hey, just like [admin
66+
actions]!).
67+
68+
If your admin modifies `get_urls`, `change_view`, or `changelist_view`,
69+
you'll need to take extra care because `django-object-actions` uses them too.
70+
71+
### Re-using Admin Actions
72+
73+
If you would like a preexisting admin action to also be an *object action*, add
74+
the `takes_instance_or_queryset` decorator to convert object instances into a
75+
queryset and pass querysets:
76+
77+
```python
78+
from django_object_actions import DjangoObjectActions, takes_instance_or_queryset
79+
80+
class RobotAdmin(DjangoObjectActions, admin.ModelAdmin):
81+
# ... snip ...
82+
83+
@takes_instance_or_queryset
84+
def tighten_lug_nuts(self, request, queryset):
85+
queryset.update(lugnuts=F('lugnuts') - 1)
86+
87+
change_actions = ['tighten_lug_nuts']
88+
actions = ['tighten_lug_nuts']
89+
```
90+
91+
[admin actions]: https://docs.djangoproject.com/en/stable/ref/contrib/admin/actions/
92+
93+
### Customizing *Object Actions*
94+
95+
To give the action some a helpful title tooltip, add a
96+
`short_description` attribute, similar to how admin actions work:
97+
98+
```python
99+
def increment_vote(self, request, obj):
100+
obj.votes = obj.votes + 1
101+
obj.save()
102+
increment_vote.short_description = "Increment the vote count by one"
103+
```
104+
105+
By default, Django Object Actions will guess what to label the button
106+
based on the name of the function. You can override this with a `label`
107+
attribute:
108+
109+
```python
110+
def increment_vote(self, request, obj):
111+
obj.votes = obj.votes + 1
112+
obj.save()
113+
increment_vote.label = "Vote++"
114+
```
115+
116+
If you need even more control, you can add arbitrary attributes to the buttons
117+
by adding a Django widget style
118+
[attrs](https://docs.djangoproject.com/en/stable/ref/forms/widgets/#django.forms.Widget.attrs)
119+
attribute:
120+
121+
```python
122+
def increment_vote(self, request, obj):
123+
obj.votes = obj.votes + 1
124+
obj.save()
125+
increment_vote.attrs = {
126+
'class': 'addlink',
127+
}
128+
```
129+
130+
### Programmatically Disabling Actions
131+
132+
You can programmatically disable registered actions by defining your own
133+
custom `get_change_actions()` method. In this example, certain actions
134+
only apply to certain object states (e.g. You should not be able to
135+
close an company account if the account is already closed):
136+
137+
```python
138+
def get_change_actions(self, request, object_id, form_url):
139+
actions = super(PollAdmin, self).get_change_actions(request, object_id, form_url)
140+
actions = list(actions)
141+
if not request.user.is_superuser:
142+
return []
143+
144+
obj = self.model.objects.get(pk=object_id)
145+
if obj.question.endswith('?'):
146+
actions.remove('question_mark')
147+
148+
return actions
149+
```
150+
151+
The same is true for changelist actions with `get_changelist_actions`.
152+
153+
### Alternate Installation
154+
155+
You don't have to add this to `INSTALLED_APPS`, all you need to to do
156+
is copy the template `django_object_actions/change_form.html` some place
157+
Django's template loader [will find
158+
it](https://docs.djangoproject.com/en/stable/ref/settings/#template-dirs).
159+
160+
If you don't intend to use the template customizations at all, don't
161+
add `django_object_actions` to your `INSTALLED_APPS` at all and use
162+
`BaseDjangoObjectActions` instead of `DjangoObjectActions`.
163+
164+
165+
More Examples
166+
-------------
167+
168+
Making an action that links off-site:
169+
170+
```python
171+
def external_link(self, request, obj):
172+
from django.http import HttpResponseRedirect
173+
return HttpResponseRedirect(f'https://example.com/{obj.id}')
174+
```
175+
176+
177+
Limitations
178+
-----------
179+
180+
1. `django-object-actions` expects functions to be methods of the model
181+
admin. While Django gives you a lot more options for their admin
182+
actions.
183+
2. If you provide your own custom `change_form.html`, you'll also need
184+
to manually copy in the relevant bits of [our change form
185+
](./django_object_actions/templates/django_object_actions/change_form.html).
186+
3. Security. This has been written with the assumption that everyone in
187+
the Django admin belongs there. Permissions should be enforced in
188+
your own actions irregardless of what this provides. Better default
189+
security is planned for the future.
190+
191+
192+
Demo Admin & Docker images
193+
--------------------------
194+
195+
You can try the demo admin against several versions of Django with these Docker
196+
images: https://hub.docker.com/r/crccheck/django-object-actions/tags
197+
198+
This runs the example Django project in `./example_project` based on the "polls"
199+
tutorial. `admin.py` demos what you can do with this app.
200+
201+
202+
Development
203+
-----------
204+
205+
Getting started *(with virtualenvwrapper)*:
206+
207+
```shell
208+
# get a copy of the code
209+
git clone [email protected]:crccheck/django-object-actions.git
210+
cd django-object-actions
211+
# set up your virtualenv (with virtualenvwrapper)
212+
mkvirtualenv django-object-actions
213+
# Install requirements
214+
make install
215+
# Hack your path so that we can reference packages starting from the root
216+
add2virtualenv .
217+
make test # run test suite
218+
make quickstart # runs 'make resetdb' and some extra steps
219+
```
220+
221+
This will install whatever the latest stable version of Django is. You
222+
can also install a specific version of Django and
223+
`pip install -r requirements.txt`.
224+
225+
Various helpers are available as make commands. Type `make help` and
226+
view the `Makefile` to see what other things you can do.
227+
228+
229+
Similar Packages
230+
----------------
231+
232+
If you want an actions menu for each row of your changelist, check out [Django
233+
Admin Row Actions](https://github.com/DjangoAdminHackers/django-admin-row-actions).
234+
235+
Django Object Actions is very similar to
236+
[django-object-tools](https://github.com/praekelt/django-object-tools), but does
237+
not require messing with your urls.py, does not do anything special with
238+
permissions, and uses the same patterns as making [admin actions].

0 commit comments

Comments
 (0)