oebfare

newforms-admin Migration and Screencast

The newforms-admin branch has landed! It has been in development for quite some time and is now in trunk. I am extremely excited about it. However, I am a special case since I have been using the branch for quite some and even a committer on the branch. Migration for me has been easy, but I wanted to share that knowledge with everyone else. I have been seeing a lot of people getting stuck on where to begin. The admin documentation is quite pointless for those who have exisiting Django code. That is done on purpose. The newforms-admin branch wiki page has the information you are looking for.

I am going to go in-depth about how to perform this migration. I have done so in the screencast and will also provide a textual version here as well.

Download screencast (61.9 MB, 12:23, 800x600, Apple Animation)

Migrating Your Code to newforms-admin

Let us begin by starting with old-style admin code. Here are two models where we have a model that is edited inline:

class Post(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField(prepopulate_from=("title",))
    pub_date = models.DateField()

    class Admin:
        list_display = ("title", "pub_date")

class Link(models.Model):
    post = models.ForeignKey(Post, edit_inline=models.TABULAR)
    title = models.CharField(max_length=100, core=True)
    url = models.URLField(core=True)

newforms-admin has completely decoupled the admin syntax and options from the models. This is to provide a cleaner and more controllable expierence. It may have been more convenient before, but ultimately it isn't a ton more verbose, but gives you plenty of areas for customization.

The first thing you will need to do is create an admin.py file in your app. This is where your new ModelAdmin subclasses will live. Here is what the above Post model will turn into:

from django.contrib import admin

class PostAdmin(admin.ModelAdmin):
    list_display = ("title", "pub_date")
    prepopulated_fields = {"slug": ("title",)}

The prepopulate_from keyword argument has been translated to prepopulated_fields. Most of the old inner Admin class options have stayed the same, but be sure to make sure.

The last bit that needs to be moved out from the models is the edit_inline and core keyword arguments. You can safely just remove any and all core arguments. They are no longer used. newforms-admin now provides a nice delete checkbox for exisiting instances in inlines. Your admin.py will now look like after changing the edit_inline bit:

from django.contrib import admin

from myproject.myapp.models import Link

class LinkInline(admin.TabularInline):
    model = Link

class PostAdmin(admin.ModelAdmin):
    list_display = ("title", "pub_date")
    prepopulated_fields = {"slug": ("title",)}
    inlines = [
        LinkInline,
    ]

Hooking everything up

Now that you have done a migration of the code, we now need to tie everything together. Another change in newforms-admin is how the URL is routed. Before it worked with an include:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^admin/', include('django.contrib.admin.urls')),
)

That no longer works since django.contrib.admin.urls no longer exists. The new way introduces the notion of individual sites. Those are defined with the AdminSite class. Here is how you would quickly migrate this part of the code:

from django.conf.urls.defaults import *
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/(.*)', admin.site.root),
)

Ok, notice the addition of admin.autodiscover(). This is a simple function that iterates over INSTALLED_APPS and imports the admin.py module of each. The reason for this is because you should always register your models to their ModelAdmin classes for the default AdminSite instance. At the bottom of your admin.py file add:

admin.site.register(Post, PostAdmin)

Based on the example code above, you will also need to import the Post model into scope.

Custom AdminSite instances

There are cases where you may want more than one admin interface that are registered to different models. Or perhaps you would like to override behavior or options of any app that provides ModelAdmin classes. This is very simple. Simply create a project-level admin.py module. Inside it create your own AdminSite class:

from django.contrib import admin

class AdminSite(admin.AdminSite):
    pass

site = AdminSite()

Now, use this instead of the default instance in your urls.py:

from django.conf.urls.defaults import *

from myproject.admin import site

urlpatterns = patterns('',
    (r'^admin/(.*)', site.root),
)

Notice how I have removed admin.autodiscover(). This is no longer needed since we don't really need to be automatically import the app's admin.py modules. It is done in our project-level admin.py, but really doesn't matter since we are not using the default instance. We could if we wanted, but in this case we don't.

Now do all registration for our new AdminSite instance in the project-level admin.py:

from django.contrib import admin

from myproject.myapp.admin import PostAdmin
from myproject.myapp.models import Post

class AdminSite(admin.AdminSite):
    pass

site = AdminSite()

site.register(Post, PostAdmin)

We could even customize the contrib app's ModelAdmin subclasses and provide our own behavior or option overrides:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

from myproject.myapp.admin import PostAdmin
from myproject.myapp.models import Post

class AdminSite(admin.AdminSite):
    pass

site = AdminSite()

class MyUserAdmin(UserAdmin):
    list_display = ("username",)

site.register(User, MyUserAdmin)
site.register(Post, PostAdmin)

That is all I have for now. I will be doing more posts in the future about the additional hooks you can find with AdminSite and ModelAdmin classes. Let me know what you think!


Comments

Great screencast! Just moved my site over to newforms admin. It seems faster too, but I don't know if that's just me imagining it :)

Thanks for all your hard work on this!

Posted by Eric Holscher on Jul 20, 2008 at 6:50 PM

great writeup. love to see more in the near future :)

Posted by kevin on Jul 20, 2008 at 8:02 PM

Freaking, freaking, freaking awesome screencast!

Posted by Eric Florenzano on Jul 21, 2008 at 1:17 AM

Great post -- keep up the good work, Brian :)

Posted by Thomas on Jul 21, 2008 at 10:40 AM

Awesome screencast. Thanks so much for putting this up -- we'll be migrating our site starting tomorrow and this is a perfect way to make sure we're not forgetting anything.

Posted by Ryan Pitts on Jul 21, 2008 at 12:41 PM

Very cool--thanks a lot! On a sidenote, you said you were going to be working on djangocasts.com ... what happened? Whois says it is "pendingDelete" :(

Posted by Mike on Jul 21, 2008 at 2:25 PM

Great work on this walk-through, really makes thing clearer :)

now, just to find what's making my app not to like the nfa :D

Posted by Mislav on Jul 22, 2008 at 2:41 AM

Very helpful! Thanks a lot.

Posted by teem on Jul 22, 2008 at 4:54 AM

Hi there, great tutorial here, it really helped me migrate across...one problem though, and I don't know whether its connected to this, I suspect it is anyway, whenever I am editing in the admin site, and I click on a 'Save' button for instance, I get a 404 error because the admin is trying to redirect me to an address such as http://hostname.com/main.fcgi/admin/b... rather than /admin/blog/entry/2/

Not really sure why this is happening, can anyone help me out?

Posted by Matthew Johnston on Jul 22, 2008 at 11:24 AM

Thank you very much for this ... while I like this new architecture much much much better I was rather confounded to see that admin broke and I was going to have to spend half the day reading docs to get it fixed. You saved me tons of time!

Posted by Vance Dubberly on Jul 22, 2008 at 12:53 PM

terrific stuff, you puttin up here.
I appreciated it very much!!

rolf

Posted by rolf on Jul 23, 2008 at 6:52 AM

Nice!

Posted by Ed Menendez on Jul 23, 2008 at 7:52 PM

There is a DjangoSnippets post that does precisely this. http://www.djangosnippets.org/snippet...

Posted by Suriya Subramanian on Jul 25, 2008 at 1:29 PM

yeah thanks for all your work on Django you rock!!

Posted by bernd on Jul 31, 2008 at 1:40 PM

Thank you so much for the screencast and the cool admin project tip !

Posted by zyegfryed on Aug 2, 2008 at 6:32 AM

Thanks for the great screencast.
Helped me while learning from an old tutorial using the current beta release.
also thanks for all the work you put into twid

Posted by Ray Smith on Aug 19, 2008 at 12:33 AM

Add Your Comment



Entry Details

Published: Jul 20, 2008 at 6:25 PM

© 2007 - 2008 Brian Rosner