Status update on newforms-admin branch

2 views
Skip to first unread message

Adrian Holovaty

unread,
Feb 2, 2007, 3:01:09 PM2/2/07
to django-d...@googlegroups.com
The Jan. 31 deadline for merging the newforms-admin has passed, so I
thought I'd give a status update:

I'm really excited about the changes made so far in this branch. In a
nutshell, the way admin options are specified has gotten *much* more
flexible, resulting in easier and more powerful customization of the
Django admin site. A model's "class Admin" now has new hooks, such as
has_add_permission(), that let you define arbitrary behavior. I've
also taken the opportunity to refactor some admin-specific options --
raw_id_admin and prepopulate_from -- so that they're defined in the
"class Admin" rather than in the model fields themselves. To see which
hooks have been implemented, see the class ModelAdmin in
django/contrib/admin/options.py in the newforms-admin branch.

The admin site, using this branch, should be completely functional
according to my tests -- except for "edit inline" fields. If you're
not using "edit_inline" in your models, I encourage you to check out
the branch and take it for a spin, reporting any bugs. Information on
getting the branch is here:
http://code.djangoproject.com/wiki/NewformsAdminBranch

Regarding "edit inline," a couple of decisions need to be made. Ticket
#2248 (http://code.djangoproject.com/ticket/2248) has an interesting
suggestion: you define the inline stuff within the admin class. I'll
copy and paste from that ticket:

class Admin:
inline_models = (
{'model':'Child',
'type':models.TABULAR,
'min_num_in_admin':3,
'max_num_in_admin':20,
'num_extra_on_change':3,
'fields':('name','age',)
},
{'model':'Job',
'type':models.STACKED,
'min_num_in_admin':1,
'max_num_in_admin':3,
'fields':('company','salary',)
}
)

I agree this approach is a huge improvement over the current syntax,
but I wonder whether it can be expanded even more. Instead of
dictionaries, let's use objects:

from django.contrib.admin import TabularInline, StackedInline

# ...

class Admin:
inlines = (
TabularInline('Child', min=3, max=20, extra=3,
fields=('name', 'age')),
StackedInline('Job', min=1, max=3,
fields=('company', 'salary')),
)

This infastructure would let you create your own inline types by
implementing an Inline subclass.

What sort of hooks would an Inline class need? And are there any
thoughts or other ideas/proposals while we're focusing on this?

Adrian

--
Adrian Holovaty
holovaty.com | djangoproject.com

oggie rob

unread,
Feb 2, 2007, 4:02:53 PM2/2/07
to Django developers
On Feb 2, 12:01 pm, "Adrian Holovaty" <holov...@gmail.com> wrote:
> The Jan. 31 deadline for merging the newforms-admin has passed, so I
> thought I'd give a status update:

Kudos on these changes (& newforms, of course). I really like that
admin is now as "legit" as regular forms for public views - very
efficient & flexible. Very DRY!

> from django.contrib.admin import TabularInline, StackedInline
>
> # ...
>
> class Admin:
> inlines = (
> TabularInline('Child', min=3, max=20, extra=3,
> fields=('name', 'age')),
> StackedInline('Job', min=1, max=3,
> fields=('company', 'salary')),
> )
>

Adrian, that is a phenomenal idea. Succinct and powerful.
Just one q: How would you specify the ordering of these displayed
fields vs. others? e.g. can you say 'field1', 'field2', inline[0],
'field3', inline[1] etc.

-rob

Rob Hudson

unread,
Feb 2, 2007, 4:23:46 PM2/2/07
to Django developers
> I agree this approach is a huge improvement over the current syntax,
> but I wonder whether it can be expanded even more. Instead of
> dictionaries, let's use objects:

Woah... inline that is specified by objects that can be subclasses?
I'll have to wrap my head around that one.

Would it be possible to nest inlines? This is one limitation we
sometimes bump up against since our data model spans more than a few
relationships sometimes, and it would be nice to have greater inline
depth.

For example, if you have:

class Kingdom(models.Model):
#


class Admin:
inlines = (

StackedInline('Phylum')
)

class Phylum(models.Model):
kingdom = models.ForeignKey(Kingdom)


class Admin:
inlines = (

StackedInline('Class')
)

class Class(models.Model): # ignore the reserved word :)
phylum = models.ForeignKey(Phylum)


class Admin:
inlines = (

StackedInline('Order')
)
etc...

Could that work and span all those relationships, following the
relationships as it goes.

-Rob

gabor

unread,
Feb 2, 2007, 4:52:01 PM2/2/07
to django-d...@googlegroups.com
Adrian Holovaty wrote:
>
> I've
> also taken the opportunity to refactor some admin-specific options --
> raw_id_admin and prepopulate_from -- so that they're defined in the
> "class Admin" rather than in the model fields themselves.

with this change to the raw_id_admin, what is the recommended approach,
when i want to use a ChangeManipulator (the automatic one generated from
the model), but i want to do it the raw_id_admin way, otherwise the
Manipulator loads in every possibly-related object, when doing the
validation.

up to now i simply added raw_id_admin = True to the model's field, but
now i do not know how it should be handled...i realize that the
new-admin is probably using the newforms... and i do not know how
raw_id_admin is handled there

gabor

sansmojo

unread,
Feb 3, 2007, 4:29:05 AM2/3/07
to Django developers
I could also see a use for this. Also, it would be nice to specify
that at least one instance of the inline object be required. With the
above example, when adding a Kingdom, at least one (or however many
specified) Phylums would be required. A common use for this would be
a person model, with an inline address model, but you want every
person to have at least one address.

Ivan Sagalaev

unread,
Feb 3, 2007, 6:10:14 AM2/3/07
to django-d...@googlegroups.com
gabor wrote:
> up to now i simply added raw_id_admin = True to the model's field, but
> now i do not know how it should be handled...i realize that the
> new-admin is probably using the newforms... and i do not know how
> raw_id_admin is handled there

I was thinking of it a bit... My first solution was to create a custom
manipulator that was switching off those huge <select>s with 'follow'.
In newforms this can be achieved by specifying for ForeignKeys a simpler
widget like HiddenInput. But the main problem with this solution is that
you need to create a new form subclass just to say "I won't display this
field" which is 1) an overkill and 2) doesn't work in cases where form
is created automatically (generic views).

I have a better proposal. We can just defer getting 'choices' for
<select> until 'render' is called. Thus if the field is never displayed
in template it won't hit database. I'm not sure how to do it properly
though... I see two ways now:

- Turn Field.get_choices into a generator. It now actually fetches rows
and wraps them into a list and adds a 'blank' choice.
- Allow a callable in 'choices' param of a Select widget that will be
called on 'render'. It then can be passed as lambda calling form's choices.

I better like first option. What do you think?

James Bennett

unread,
Feb 3, 2007, 2:24:43 PM2/3/07
to django-d...@googlegroups.com
On 2/3/07, Ivan Sagalaev <Man...@softwaremaniacs.org> wrote:
> - Turn Field.get_choices into a generator. It now actually fetches rows
> and wraps them into a list and adds a 'blank' choice.
> - Allow a callable in 'choices' param of a Select widget that will be
> called on 'render'. It then can be passed as lambda calling form's choices.

One suggestion that I kind of like is the ability to pass a QuerySet
to 'choices' and, in that case, have it generate the choices from,
say, the id and __str__ of each object in the QuerySet.

Since it doesn't actually need those values until render time, it
could take advantage of the laziness of QuerySet evaluation to avoid
hitting the DB at all when the field is never shown.

--
"Bureaucrat Conrad, you are technically correct -- the best kind of correct."

Russell Keith-Magee

unread,
Feb 3, 2007, 7:11:51 PM2/3/07
to django-d...@googlegroups.com
On 2/3/07, Adrian Holovaty <holo...@gmail.com> wrote:
>
> Regarding "edit inline," a couple of decisions need to be made. Ticket
> #2248 (http://code.djangoproject.com/ticket/2248) has an interesting
> suggestion: you define the inline stuff within the admin class. I'll
> copy and paste from that ticket:

+1 to the general idea. I've always been concerned about the idea of
having form definitions in the field.

> from django.contrib.admin import TabularInline, StackedInline
>
> # ...
>
> class Admin:
> inlines = (
> TabularInline('Child', min=3, max=20, extra=3,
> fields=('name', 'age')),
> StackedInline('Job', min=1, max=3,
> fields=('company', 'salary')),
> )
>
> This infastructure would let you create your own inline types by
> implementing an Inline subclass.

This is possibly nitpicking on a quick-and-dirty example - but
shouldn't the Inline definitions be tied to a m2m/m2o field name,
rather than the model name?

class Admin:
inlines = {
'children': TabularInline(min=3, max=20, extra=3,


fields=('name', 'age')),

'jobs': StackedInline(min=1, max=3,


fields=('company', 'salary')),
}

That way there are no difficulties if there are multiple m2m realtions
on the same model.

> What sort of hooks would an Inline class need? And are there any
> thoughts or other ideas/proposals while we're focusing on this?

This is possibly what you already have in mind - but Is there an
intersection here between inline fields and recursive form
definitions?

form = form_for_model(Person, fields=('name','age'),
inlines={ 'children': TabularInline(... })

The *Inline classes need not just be data containers - they could
contain all the rendering information for handling their own display,
and the display of the subforms for each related object - and if this
is the case, they can be used by end users on their own forms.

Yours,
Russ Magee %-)

Ivan Sagalaev

unread,
Feb 4, 2007, 8:48:11 AM2/4/07
to django-d...@googlegroups.com
James Bennett wrote:
> One suggestion that I kind of like is the ability to pass a QuerySet
> to 'choices' and, in that case, have it generate the choices from,
> say, the id and __str__ of each object in the QuerySet.

Then you'll loose a 'blank' choice. Current Field.get_choices does a bit
more than just iterating a queryset but as far as I can tell it can
easily be made lazy by turning it into a generator: instead of
concatenating two actual lists just yield them by items.

Adrian Holovaty

unread,
Feb 4, 2007, 1:22:06 PM2/4/07
to django-d...@googlegroups.com
On 2/3/07, Ivan Sagalaev <Man...@softwaremaniacs.org> wrote:
> I have a better proposal. We can just defer getting 'choices' for
> <select> until 'render' is called. Thus if the field is never displayed
> in template it won't hit database. I'm not sure how to do it properly
> though... I see two ways now:
>
> - Turn Field.get_choices into a generator. It now actually fetches rows
> and wraps them into a list and adds a 'blank' choice.
> - Allow a callable in 'choices' param of a Select widget that will be
> called on 'render'. It then can be passed as lambda calling form's choices.
>
> I better like first option. What do you think?

The first option sounds better to me, too. Are you willing to code up a patch?

Ivan Sagalaev

unread,
Feb 5, 2007, 2:56:39 AM2/5/07
to django-d...@googlegroups.com
Adrian Holovaty wrote:
> The first option sounds better to me, too. Are you willing to code up a patch?

OK. I'll post a follow-up here with the ticket number in a day or two.

David Larlet

unread,
Feb 5, 2007, 9:02:42 AM2/5/07
to django-d...@googlegroups.com
2007/2/4, Russell Keith-Magee <freakb...@gmail.com>:

>
> The *Inline classes need not just be data containers - they could
> contain all the rendering information for handling their own display,
> and the display of the subforms for each related object - and if this
> is the case, they can be used by end users on their own forms.
>

I totally agree with this interesting proposition. Forms are not that
hard to htmlize but always take time, with those classes it could be
incredibly fast.

Cheers,
David

Ivan Sagalaev

unread,
Feb 5, 2007, 4:29:10 PM2/5/07
to django-d...@googlegroups.com
Adrian Holovaty wrote:
> The first option sounds better to me, too. Are you willing to code up a patch?

Here it is: http://code.djangoproject.com/ticket/3436

Adrian Holovaty

unread,
Feb 5, 2007, 4:40:16 PM2/5/07
to django-d...@googlegroups.com

Excellent. Thanks, Ivan!

Reply all
Reply to author
Forward
0 new messages