After a wonderful Django meetup here at PyCon (Django: the framework
that buys you pizza), a bunch of us spun off into a little ad-hoc
sprint to talk about improvements to the Django admin.
This email summarizes our plan. It's quite long, mostly because it's
an attempt to distill both where we're going and why we're going
there. It's mostly intended as a record of our plans that we can refer
back to in the future, but it's presented here in case anyone wants to
comment, but note that we've already hashed out a *bunch* of alternate
solutions -- in-person debate works must better than in-ASCII. Adrian
and I (and the other developers here) are pretty much in agreement
about this stuff, so please try to keep critcism to serious concerns
as opposed to syntax or sugar.
To recap: we've been talking about ways of moving the admin
declaration *out* of the model. That's never been a semantically
correct place for the declaration (the admin's a view!), and now that
the newforms-admin allows so much more customization, it's going to
get pretty unwieldy.
To start, here's a simple model with an admin declaration as it stands
right now::
from django.db import models
from django.contrib.auth.models import User
class Story(models.Model):
reporter = models.ForeignKey(User)
title = models.CharField()
body = models.TextField()
class Admin:
list_display = ["title", "reporter"]
search_fields = ["title"]
So: first step is to remove the admin from the model. That means of
course we need another way to indicate that models show up in the
admin. Here's how::
from django.db import models
from django.contrib import admin
from django.contrib.auth.models import User
class Story(models.Model):
reporter = models.ForeignKey(User)
title = models.CharField()
body = models.TextField()
admin.site.register(Story)
Of course, we still want some admin options::
class Story(models.Model):
reporter = models.ForeignKey(User)
title = models.CharField()
body = models.TextField()
admin.site.register(Story,
search_fields = ["title"],
list_display = ["title", "reporter"],
)
OK, but we're still in the model, and one goal is decoupling the admin
from the model. So, we should instead move the admin declaration into
another file,
``admin.py``::
from myapp.models import Story
from django.contrib import admin
admin.site.register(Story,
search_fields = ["title"],
list_display = ["title", "reporter"],
)
Note that either option will be supported, but the second option --
putting admin declarations in ``admin.py`` -- will be the recommended
usage. As an added bonus: if you put the admin declaration in a
seperate file and *don't* put ``django.contrib.admin`` in
``INSTALLED_APPS``, the admin code will never get loaded into memory.
Lower memory footprint == GOOD.
Now, one of the coolest parts of the newforms admin work is that you
can easily override methods of the admin class to change the behavior
of the admin. Let's do that for story. For example, here's how we
could prevent users from editing any stories except their own (in
``myapp.admin.py``)::
from django.contrib import admin
from myapp.models import Story
class StoryAdmin(admin.ModelAdmin):
def has_change_permission(self, request, object):
return request.user == object.reporter
admin.site.register(Story, StoryAdmin)
(As the branch stands right now it's a little more difficult since
``has_change_permission`` gets ``object_id``, not ``object``, but
Adrian's going to change it for convenience.)
We can also give options to ``StoryAdmin``::
admin.site.register(Story, StoryAdmin, list_display=["title", "reporter"])
Or give them in the admin class::
class StoryAdmin(admin.ModelAdmin):
list_display = ["title", "reporter"]
def has_change_permission(self, request, object):
return request.user == object.reporter
So, formally, ``admin.site.register`` can be called in these ways::
admin.site.register(Model)
admin.site.register(Model, **options)
admin.site.register(Model, AdminClass)
admin.site.register(Model, AdminClass, **options)
admin.site.register(iterable)
admin.site.register(iterable, **options)
admin.site.register(iterable, AdminClass)
admin.site.register(iterable, AdminClass, **options)
admin.site.unregister(Model)
admin.site.unregister(iterable)
(the ``admin.site.register(iterable)`` form lets you easily register
multiple models in one fell swoop.)
The final part of the equation is the ability to define mutliple admin
sites. ``admin.site`` itself will actually be an instance of an
``AdminSite`` class. Briefly, this will let you create mutliple admin
sites will different behavior (including an admin that's not tied to
the auth system, for example). Here's how you'd register models in
multiple sites::
from django.contrib import admin
from myapp.adminsite import second_admin_site
admin.site.register(...)
second_admin_site.register(...)
Where ``myapp.adminsite`` would have, roughly::
class MyAdminSite(admin.AdminSite):
def login(self, request):
...
def logout(self, request):
...
def index(self, request):
...
def urls(self, request):
...
def change_password(self, request):
...
def reset_password(self, request):
...
second_admin_site = MyAdminSite()
(This API's a little sketchy, but it'll get filled out).
Finally, the URLconf. For the default case (a single admin)::
('^admin/', include(admin.site.urls()))
And for multiple admins::
('^admin/', include(admin.site.urls()))
('^admin2/', include(second_admin_site.urls()))
Finally, on a somewhat related note, this refactoring will including
the moving of the admin docs to a seperate contrib app.
Jacob
> Finally, on a somewhat related note, this refactoring will including
> the moving of the admin docs to a seperate contrib app.
cool - cant wait
--
regards
kg
http://lawgon.livejournal.com
http://nrcfosshelpline.in/web/
I don't completely understand some of this yet, but on the whole it
looks like a good idea.
It feels like it will make a bunch of the internal code a bit easier to
separate, and hence understand and maintain, too. We have a lot of code
that works with admin features under django/db/models/ that often just
complicates reading the code (and makes it harder to find when
debugging).
Malcolm
sounds very good to me. Among other things, these changes should make
it much easier to implement two use cases that were always said to be
outside of the scope of admin, despite being -- at least in my opinion
-- very useful and common.
1. give some staff members permission to view instances in the admin
list view (and perhaps in a new "admin object_detail") without the
permission to change them. If I understand it correctly, this is
precisely what has_change_permission will be used for.
2. limit the list of instances a user sees in the admin list view.
Perhaps something like this:
class StoryAdmin(admin.ModelAdmin):
[...]
def get_list_queryset(self, request):
if request.user.is_superuser:
return Story.objects.all()
else:
return Story.objects.filter(reporter=request.user)
With these two changes admin suddenly gets a lot more useful: I could
use admin in a lot of places that now require writing my own, simpler
admin-like interfaces.
-mk
This feature alone will solve so many of the past problems I've faced
using the admin contrib. When this gets implemented it will truly feel
like one can take full control of the way the admin behaves.
Other than that, separating the admin options from the model
declaration is a welcome improvement as well... I remember it feeling
really wrong when i first started using django.
Now for the inevitable question and probably irritating ;) question:
when are these changes scheduled to be included in the django tin?
In any case, keep up the good work guys!
regards,
Simon
On Feb 25, 7:51 am, "Jacob Kaplan-Moss" <jacob.kaplanm...@gmail.com>
wrote:
are there any plans to update/change the html/css-stuff?
e.g., edit_inline is not displayed correctly.
thanks,
patrick
Is there going to be any facility for defining additional admin views
besides the current add, change, and delete?
Nate
I'm guessing sometime between "tomorrow" and "when Perl 6 ships".
With any luck it'll be closer to the former :)
Jacob
edit_inline, max_num_in_admin, ...
an so on. Have you made any decision where this should be set in
future or if this will be left in the model?
Tom
Edit-inline is also going to be moved out into the admin declaration,
and all the num_in_admin stuff just going to go away (to be replaced
with an interface that doesn't rely on that crutch).
We haven't fully worked out the new syntax for edit-inline, but it'll
be pretty similar in "spirit" to what we've already worked out.
Jacob
Edit-inline was also useful outside of the admin, in manipulators. Are
there any plans to have newforms handling this or this will become a
purely admin functionality?
I'm working on it at the moment and barring any objections from
Adrian, yes, something similar to edit-inline will be available
outside of the admin.
Joseph
Nicolas
On Feb 25, 2:51 am, "Jacob Kaplan-Moss" <jacob.kaplanm...@gmail.com>
wrote:
> Howdy folks --
>
> After a wonderful Django meetup here at PyCon (Django: the framework
> that buys you pizza), a bunch of us spun off into a little ad-hoc
> sprint to talk about improvements to the Djangoadmin.
>
> This email summarizes our plan. It's quite long, mostly because it's
> an attempt to distill both where we're going and why we're going
> there. It's mostly intended as a record of our plans that we can refer
> back to in the future, but it's presented here in case anyone wants to
> comment, but note that we've already hashedouta *bunch* of alternate
> solutions -- in-person debate works must better than in-ASCII. Adrian
> and I (and the other developers here) are pretty much in agreement
> about this stuff, so please try to keep critcism to serious concerns
> as opposed to syntax or sugar.
>
> To recap: we've been talking about ways of moving theadmin
> declaration *out* of the model. That's never been a semantically
> correct place for the declaration (theadmin'sa view!), and now that
> the newforms-adminallows so much more customization, it's going to
> get pretty unwieldy.
>
> To start, here's a simple model with anadmindeclaration as it stands
> right now::
>
> from django.db importmodels
> from django.contrib.auth.modelsimport User
>
> class Story(models.Model):
> reporter =models.ForeignKey(User)
> title =models.CharField()
> body =models.TextField()
>
> classAdmin:
> list_display = ["title", "reporter"]
> search_fields = ["title"]
>
> So: first step is to remove theadminfrom the model. That means of
> course we need another way to indicate thatmodelsshow up in theadmin. Here's how::
>
> from django.db importmodels
> from django.contrib importadmin
> from django.contrib.auth.modelsimport User
>
> class Story(models.Model):
> reporter =models.ForeignKey(User)
> title =models.CharField()
> body =models.TextField()
>
> admin.site.register(Story)
>
> Of course, we still want someadminoptions::
>
> class Story(models.Model):
> reporter =models.ForeignKey(User)
> title =models.CharField()
> body =models.TextField()
>
> admin.site.register(Story,
> search_fields = ["title"],
> list_display = ["title", "reporter"],
> )
>
> OK, but we're still in the model, and one goal is decoupling theadmin
> from the model. So, we should instead move theadmindeclaration into
> another file,
> ``admin.py``::
>
> from myapp.modelsimport Story
> from django.contrib importadmin
>
> admin.site.register(Story,
> search_fields = ["title"],
> list_display = ["title", "reporter"],
> )
>
> Note that either option will be supported, but the second option --
> puttingadmindeclarations in ``admin.py`` -- will be the recommended
> usage. As an added bonus: if you put theadmindeclaration in a
> seperate file and *don't* put ``django.contrib.admin`` in
> ``INSTALLED_APPS``, theadmincode will never get loaded into memory.
> Lower memory footprint == GOOD.
>
> Now, one of the coolest parts of the newformsadminwork is that you
> can easily override methods of theadminclass to change the behavior
> of theadmin. Let's do that for story. For example, here's how we
> could prevent users from editing any stories except their own (in
> ``myapp.admin.py``)::
>
> from django.contrib importadmin
> from myapp.modelsimport Story
> multiplemodelsin one fell swoop.)
>
> The final part of the equation is the ability to define mutlipleadmin
> sites. ``admin.site`` itself will actually be an instance of an
> ``AdminSite`` class. Briefly, this will let you create mutlipleadmin
> sites will different behavior (including anadminthat's not tied to
> the moving of theadmindocs to a seperate contrib app.
>
> Jacob