Really, this was a pretty major oversight on my part, but I just now finished added a 'status' field to each entry in my blog app. It's a pretty simple thing in and of itself, I just added a tiny bit of code to my Entry model:
STATUS_CHOICES = (
('dr', 'draft'),
('ac', 'active'),
('ar', 'archive'),
)
status = models.CharField(choices=STATUS_CHOICES, max_length=2, default='dr')
And then a custom manager as well:
class EntryManager(models.Manager):
def active(self):
return self.get_query_set().filter(status='ac')
Remembering, of course, to make sure that manager was actually accessible from the model (in this case by adding 'objects = EntryManager()' to my model class).
Once I did that I was able to change the Entry.objects.all() in my view(s) to Entry.objects.active() and now I'm able to write a post and save it without actually publishing! As I said, something of a bonehead move that I didn't do this in the first place.
Since I was messing around in my code anyway I decided to clean up the admin for my Entry model as well. Since I'm the only one who ever uses the admin for my blog, I hadn't bothered to before, but now I've got a little more info available to me when looking at my entries:
class EntryAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('title',)}
save_on_top = True
list_display = ['title', 'post_date', 'site_list', 'status',]
list_filter = ('sites', 'status',)
So my admin now looks something like this:

A cool thing there is the 'site_list' column. By default, you can't use a ManyToManyField in the list_display for your model. But I've dealt with this before, and it's only 5 lines of code to make this work:
def site_list(self):
if self.sites:
sites = [site.name for site in self.sites.all()]
string = ", ".join(sites)
return string
Good stuff!
One of the Django projects that I've been working on for about a year and and will (fingers crossed) be going live in the very very very near future has involved a lot of modification to Django's admin interface. I plan on writing more about the many, many specific changes that I've made to the interface (without modifying the actual Django codebase, so that the changes can be easily applied by anyone without breaking updates), but to talk about them all at once would make far too long of a post. So I'll be taking them on one at a time.
The change I want to talk about right now involves redirecting the user to the page I want them to be at after they've finished adding or editing an object rather than to that object's model's change_list. Normally, if you're editing an Entry object in the Blog app, when you hit save it will take you to /admin/blog/entry/, which is a list of all the Entry objects in the database. However there are some instances in which this isn't the behavior I want. Once such instance involves model inheritance. Say, for example, you have multiple types of Entries which you've accomodated through multi-table-inheritance. Because the different sub-classes are different models, they all have their own change_lists in the Django admin. But I want to be able to view, edit, and create Entries of all types from one page.
Fortunately, Django makes this fairly easy to accomplish. All that is necessary is to override the appropriate methods in the Entry ModelAdmin. That will end up looking something like this:
class EntryAdmin(admin.ModelAdmin):
def change_view(self, request, object_id, extra_context=None):
result = super(EntryAdminAdmin, self).change_view(request, object_id, extra_context)
if not request.POST.has_key('_addanother') and not request.POST.has_key('_continue'):
result['Location'] = iri_to_uri("/admin/blog/entry/")
return result
The exact same modification should be made to add_view as well, and a nearly identical modification to delete_view though it doesn't need to deal with the _addanother and _continue cases. You can then use the EntryAdmin class for all of your varioud Entry sub-classes, or, if you need some other changes to the admin for different Entries sub-classes you can sub-class EntryAdmin for them. Now, whenever you hit the save button after editing any sort of Entry, it will always take you back to /admin/blog/entry/ rather than /admin/blog/linkentry/ or whatever your other subclasses are. If you want it to only take you back to /admin/blog/entry/ if you're coming from a particular page and otherwise take you to /admin/blog/linkentry/ all you need is to add a GET variable to your url (something like '/admin/blog/linkentry/add/?return_to_main=True') and then check for it in your modified change_view, add_view, and delete_view methods with a request.GET.get('return_to_main', False). I've even used this between objects of different model types to create a 'dashboard' page that allows you to view, and alter the relationships between an object of one type with objects of another type. All that's necessary in a case like that is to pass the id of the object in your GET variable and take that into account when creating your uri. An added benefit of that it makes it easy to auto-fill the ForeignKey field when creating a related object. In such a case you'll also need to keep that GET variable in the URLs down the line in order to maintain compatilibility with the 'Save and add another' and 'Save and continue editing' features. But that's still a simple modification:
class EntryAdmin(admin.ModelAdmin):
def change_view(self, request, object_id, extra_context=None):
result = super(EntryAdminAdmin, self).change_view(request, object_id, extra_context)
other_id = request.GET.get("other_id", None)
if not request.POST.has_key('_addanother') and not request.POST.has_key('_continue'):
if other_id:
result['Location'] = iri_to_uri("/admin/dashboard/%s/") % other_id
return result
elif request.POST.has_key('_continue'):
if other_id:
result['Location'] = iri_to_uri("?other_id=%s" % other_id)
return result
elif request.POST.has_key('_addanother'):
if other_id:
result['Location'] = iri_to_uri("%s?other_id=%s" % (result['Location'], other_id))
return result
return result
But more on that sort of thing in other posts.
I just submitted my first patch to Django! Among other things, this is my first real forray into the inner depths of the Django code. This patch fixes an issues that had been bothering me for quite some time. In Django's admin interface it's possible to specify that a particular field should be automatically filled in with the value(s) you enter in some other field(s). For example, as I typed 'My first Django patch' into the title field of the form I used to write this post, it was automatically filling in a slug field that's being used for the permalink to this post with 'my-first-django-patch'. This is a very useful features and uses just a little bit of javascript to accomplish it. The only problem is that it only works when you're trying to pull information from a text field. Sometimes, however, you might want to pull information from another sort of field, such as a drop-down menu. Previously Django simply wasn't capable of this. With my changes, however, it is able to handle this potentiality quite well.
It's not really a huge patch, just a fairly a little added code to a single javascript method, and there's no guarantee that my patch will every make it's way into the Django code base, but it's still fun to be able to contribute to one of my favorite open source projects ever. The patch, for those that are curious, can be found here, and I've also submitted it to djangosnippets.org here.