I just happened to check my Google analytics today and saw that my hits had dropped essentially to zero since October 8th. Turns out that my wsgi file was a little screwed up after my initial attempts to migrate my blog to django-mingus. I have, obviously, fixed it, though I still want to move the blog to mingus and will hopefully get the chance to actually do that soon. Until then, however, at least my blog is up!
Just a quick note: this blog now has Disqus comments. Turns out it's insanely simple to implement, thanks to Arthur Koziel's awesome django-disqus app. Took me no more than five minutes during my lunch break to get everything to it's current state.
One of the reasons that I haven't been blogging lately is that I have a lot of planned changes for the blog. Most importantly, I've been planning on moving it to my Cloud Server on The Rackspace Cloud (née Mosso). I've finally taken care of that, and this post is the first on the new server. If you can see it, that means the DNS changes have propagated.
Now that I've done this I have a number of other changes planned. I'm going to completely overhaul the backend to the blog. In the course of the migration I upgrade from Django 1.0.2 to Django 1.1. Nothing else has changed yet, but I intend to also revamp the templates, add some fancy new feature to better integrate with the rest of my online life, and start using django-mingus. One of the more urgent changes I want to make is to switch to Disqus powered comments. Previously I was using django.contrib.comments along with Akismet for spam filtering. Akismet really just hasn't worked at all for me for the past few months, which is why comments are current disabled. Fortunately, Disqus should cut down on the spam by requiring registration without imparting too large an onus on the readers by using a number of common authentication backends like OpenID, Facebook Connect, and Twitter. I'm hoping it will make for a good compromise.
The most important change, of course, is that I intend to actually start writing again. I've got a lot of ideas that I've wanted to write about and simply haven't because either I didn't have the time, I wanted to wait until this migration, or I was just too lazy. Hopefully, that will be changing now!
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!
Since you're reading this there's a decent chance you know this already, but I've made some changes to my blog's styling again. I think it looks much better than it did before and is a little easier on the eyes. It was inspired in large part by the WordPress theme that Mark Shuttleworth uses on his blog. I've definitely decided that I like the simple bordering on minimalistic look, and the solid blue background I was using before just wasn't doing it for me. Plus I even learned some useful CSS tricks that I didn't know before in coding it. I'm planning a similar redesign of the dy/dx tech website in the near future to bring the look of the site a little more up to date as well.
Over to the right, in the sidebar, you may notice a nifty javascript calendar displaying the current month with some days highlighted. It is, in fact, a navigation tool for the blog. The highlighted days are the days with posts. Click on a day, see the posts for that day. If there are no posts for that day, it falls back to the month. If there are no posts for that month, it falls back to the year. Pretty simple stuff, really.
The calendar is implemented with YUI, and the fallback mechanism is, of course, all Django code. The fallback isn't something that's built into Django, sadly, but was relatively easy to implement by just wrapping Django's date-based generic views in some views of my own. Really it's just a simple 'try except' block that catched a 404 error and instead returns a redirect. Not too shabby, if I do say so myself:
from django.template import loader
from django.http import Http404, HttpResponseRedirect
from django.views.generic import date_based
def archive_month(request, year, month, queryset, date_field, month_format='%m', template_name=None, template_loader=loader, extra_context=None, allow_empty=False, context_processor=None, template_object_name='object', mimetype=None, allow_future=False):
try:
return date_based.archive_month(request, year, month, queryset, date_field, month_format, template_name, template_loader, extra_context, allow_empty, context_processor, template_object_name, mimetype, allow_future)
except Http404:
return HttpResponseRedirect("/%s/" % year)
def archive_day(request, year, month, day, queryset, date_field, month_format='%m', day_format='%d', template_name=None, template_loader=loader,extra_context=None, allow_empty=False, context_processor=None, template_object_name='object', mimetype=None, allow_future=False):
try:
return date_based.archive_day(request, year, month, day, queryset, date_field, month_format, day_format, template_name, template_loader, extra_context, allow_empty, context_processor, template_object_name, mimetype, allow_future)
except Http404:
return HttpResponseRedirect("/%s/%s/" % (year, month))
(Also check out nifty syntax highlighting! Based off this snippet from Django snippets, modified to remove the Markdown bits. And by highlighting, I of course mean formatting. I'll be adding fun colors and such to it in the near future.)
I've just finished implementing comment moderation for the blog using Jannis Leidel's fork of James Bennett's django-comment-utils. I've run a few test which seem to indicate that the basic comment moderation functionality is working though, at the moment, I still don't have notification emails going. It was actually rather amazing just how much comment spam this blog got in the past two weeks since it went live on Django without spam protection. But this should significantly cut back on it. It's really great having such a huge body of quality work out there to draw from when building a Django project.
[Edit: Looks like the problem I'm having with email may be on WebFaction's end rather than mine. Nice because it means I probably am doing things right; annoying because it means it's not within my power to fix it. Oh well, a ticket has been submitted and hopefully soon I should be getting email notifications of new comments.]
I've just implemented the first real benefit of having my blog now be Django based instead of WordPress based. Because part of the reason I had wanted to make this move all along was to allow for some close integration between my blog and my business site, when I went about setting up the Django project for the blog I actually just duplicated the project for my business site. Because of this, I'm able to use Django's sites framework to have both project pull from the same database. This means that any information available to one is also available to the other.
Because I used a ManyToManyField to define the relationship between a blog entry and a site, I'm able to specify that a particular entry is related to either or both of the two sites (as well as any future sites that I may decide to add). This entry, for example, is related to both. Thanks to Django's fabulous templating system I was able to effortlessly integrate the blog templates into my business site without every writing a single line of HTML or CSS.
The end result of all this? The business relevant posts on my blog are available not only at joshourisman.com, but now also at dydxtech.com/blog.
How cool is that?
Hot on the heels of having imported the old posts from the defunct WordPress version of my blog, I've now imported the comments as well. Unfortunately this proved a lot trickier than the posts, and many comments very likely did not make it over. Just over 200 did, however, and, for now, I'm going to call that good enough and focus on other things. Any comments left from now on, however, will work just fine thanks to the awesomeness of Django, and it's comments framework which is so incredibly simple to use I don't think it's worthwhile to do any more than simply link to the docs.
Yes, I've managed to import the old posts from my blog! It was pretty easy to do, complicated only by the fact that I initially accidentally pulled the data from a different, older WordPress blog that I deleted some years ago but apparently still had the MySQL databases for.
At any rate, all my posts are now once again accessible, and any old links to them should still work. Comments have not yet been imported, but that's the next step. For now everything should be working as expected, but please let me know if you encounter any errors or problems.
Since last night when I posted the last entry I've added a few things to my new blog. First off, syndication. Anyone who was previously subscribed to my blog through the FeedBurner feed (http://feeds.feedburner.com/joshourisman/lRQx), which should be anyone who was subscribed to it, should hopefully still be subscribed. If not, please let me know either through a comment on this post or via email.
I've also added XMLRPC pinging to it, so every entry I post should be submited via Ping-o-Matic to all the normal places.
Probably less importantly, I've added a Google site search box to my template so you can search my blog, although at the moment it's mostly going to return results from the old blog which are currently unaccessible (but I'm working on that).
Next steps, other than getting my old content imported into the new blog, are Akismet spam filtering for comments, better integration of tagging (which is actually already there, just not in a usable manner), post archives (which are pointless until I get that data imported), and better templates.
As this is all being built entirely from scratch feedback is, of course, both encouraged and welcomed.
As you may recall, I've mentioned a fair number of times in the past that I've been planning to migrate my blog away from WordPress on onto my own custom Django-based solution. Part of the reason for wanting to do this is fun, but also because I'd like to be able to have a little more integration between my blog and my business site.
If you're reading this, you can no doubt tell that something isn't quite right with my blog. Mainly, it looks completely different, this may well be the only post you can see, and it might be lacking a feature or two that it had before. The reason for this is that my hand has been forced. For the past several weeks I've been unable to log into my blog. No, I didn't forget my password, it just won't let me log in (it even says I have the wrong password when I try something else). This simply will not stand.
So I've thrown together a very very quick and barebones blogging app (and when I mean quick, I mean I did this in less than two hours) that at least gives me some ability to keep blogging. I will, of course, be expanding it to add the missing functionality, and I will be working on importing the posts, comments, and such from the old blog (I still have access to the databases, so this shouldn't be a problem). In the mean time I'm planning to set up some redirects so that links to old posts aren't broken.
Hopefully it won't take very long for me to get this new blog up to speed in terms of functionality and design, and to get my old stuff imported into it so I can finally be rid of WordPress entirely. Of course, I do have to prioritize projects for clients, so it may take a bit longer than I'd like.