Archive for the 'Django' Category
If you're in the area, come check out our next meeting!
I've been using MetaWeblog to allow me to write articles for this blog in Ecto, and it's still working very well. There's a security issue that's worth mentioning, though, which is that MetaWeblog sends authentication information (i.e., your password) over the network without encrypting it. It'd be really nice if it used an MD5 digest scheme or something instead of cleartext, but it doesn't. As I mentioned previously, unfortunately MetaWeblog has several deficiencies and this is one of them. As Jacob Kaplan-Moss points out, this means that unless you encrypt your connection with HTTPS, your password is vulnerable to snooping, so hooking up HTTPS is probably a good idea. Now that I think about it, I suppose the same argument applies anywhere you might POST a password in Django, including the Admin app. So if you don't want the other folks in your internet cafe getting unauthorized access to your website, then I guess you should encrypt the Admin app too. Just because you're paranoid doesn't mean they're not out to get you. :-)
Pictures are more interesting than words... it's cool to represent a person or a web site using an icon instead of just a name. I'm obviously not the first person to realize this; avatars are cropping up on the web in more and more places. I like how Lisa McMillan added favatars to her "hall of fame", for instance. But her comment regarding the fact that technically she's stealing their bandwidth made me think that it'd be courteous to set up a Django app that cached those images, rather than linking straight to them. The extensibility to add features like that is exactly why I'm using Django! Here's a description of how it works. My goal was to provide avatars for users who write comments on my posts. Two styles of avatars are supported: favatars (the favicon from the user's web site) and gravatars (images associated with the users email address). Usage. To insert an avatar, use the "avatar" template tag that takes two parameters: the name of an object, and a size (in pixels). The object is queried for "email" and "url" attributes, which django.contrib.comments doesn't support so I copied the comments app and added them myself. The size attribute works best if it's a multiple of 16 (because that's how big a favicon is) and less than or equal to 80 (because that's how big a gravatar is), so I use 48. Here's what my template looks like: {% for comment in comment_list %} <li class="item" id="comment-{{ comment.id }}"> {% avatar comment 48 %} ...etc... </li> <% endfor %> The object doesn't have to define both email and url attributes... if you just want favatars, then all you need is a url. If an object does have both kinds of avatars, the gravatar gets priority (because they're typically higher res). Also, there are some variables to define in your settings.py to control how the tag operates. Two are required, the rest are optional:
AVATAR_STORAGE_DIR. Required. The name of a directory where the downloaded avatar images may be stored.
And here's the source code: avatar.py Next, I want to use this to support favicons for a blogroll, of course. But one thing at a time. :-) And there are other features that could be added, like pre-fetching images asynchronously or deleting old files from the cache, but I don't get enough traffic to justify things like those.
As I mentioned previously, I wanted to integrate Ecto with my new Django-based blog using the "MetaWeblog" remote procedure call API. With an XMLRPC implementation ready to go, the remaining task is to write MetaWeblog methods and hook them up to my blog model. The model is pretty straightforward; for instance, here are the fields for "Post" object: class Post(models.Model): title = models.CharField(maxlength=200) slug = models.SlugField(prepopulate_from=('title',)) pub_date = models.DateTimeField('date published', blank=True, null=True) create_date = models.DateTimeField('date created', auto_now_add=True, blank=True, null=True) update_date = models.DateTimeField('date updated', auto_now=True, blank=True, null=True) body = models.TextField() tags = models.ManyToManyField(Tag) author = models.ForeignKey(User) status = models.CharField(maxlength=32, choices=STATUS_CHOICES, radio_admin=True, default='Draft') The MetaWeblog API is pretty well documented, but has some features that I'm not quite ready to tackle. For instance, I only have a single user with a single blog, you have to be a "super user" to edit it (no sophisticated authorization scheme yet), and I haven't done media objects (e.g., images) yet either. Most every method takes "username" and "password" parameters, so I wrote an "authenticated" wrapper function to convert them to a user object (and throw an exception if there are any authentication or authorization issues). Then the various methods are short, because the Django database/model API makes things easy. For instance, the method to get information about a blog post (given a post id) is called "getPost". My implementation looks up the post object by id, and then calls a function to turn it into the struct that MetaWeblog requires: @public @authenticated() def metaWeblog_getPost(user, postid): post = Post.objects.get(id=postid) return post_struct(post) def post_struct(post): link = full_url(post.get_absolute_url()) categories = post.tags.all() struct = { 'postid': post.id, 'title': post.title, 'link': link, 'permaLink': link, 'description': post.body, 'categories': [c.name for c in categories], 'userid': post.author.id, } if post.pub_date: struct['dateCreated'] = format_date(post.pub_date) return struct where full_url urljoin's the web site host name ("www.allyourpixel.com") to a URL, and format_date calls xmlrpclib to (you guessed it) turn a date into a string. Here's the whole file: metaweblog.py. It's not a complete application, but hopefully it'll be easy enough to customize for what your doing.
In setting up a blog server, one of my requirements was that I wanted to use a desktop client to write articles, instead of doing it in an HTML form in a web browser. I accomplished that by purchasing Ecto (which I'm using to type this article) and adding a MetaWeblog interface to the server. Here's the first of two articles on how I did it. Today's article talks about the XMLRPC layer, and the second in this series will conclude with the MetaWeblog implementation itself. MetaWeblog is a network API for posting blog articles, and it's one of the ways that Ecto can talk to a Blog server. There are some other common blog APIs, including Typepad, Moveable Type, Blogger, and Atom, and I could have chosen any of them. As I mentioned in a previous post, I strongly considered Atom, because it seems to be the newest contender and claims to address some of MetaWeblog's shortcomings. And it's RESTful, which is elegant. But when I tested Ecto with the Atom API, it didn't seem to support images. I don't know whether that's Ecto's fault or Atom's fault, but it would be a problem for me in any case. So I decided to be pragmatic and go with MetaWeblog for now. MetaWeblog is based on XMLRPC, which (like the name suggests) is a protocol for making remote procedure calls over HTTP. So the first order of business was to add XMLRPC support to Django. I'm not the first person to do that; Amit Upadhyay and Georg "hugo" Bauer have done some good work in this area, and have some excellent ideas about how it ought to fit into Django as you can see from their comments on Django ticket #115. Amit's implementation looked similar to what I had in mind, so I started with his code. Here's what I came up with: xmlrpc.py. The basic idea of XMLRPC is that a client (like Ecto) makes a procedure call (like "publish new post") by POSTing an XML document over HTTP. The web server (running Django, in this case) parses the XML into a method name and an unmarshalled set of arguments. It then locates the method that the client requested, executes it, and marshalls the returned value which is packaged inside an XML document and returned to the client. How does that fit into the Django world-view? It seems to correspond to one entry (per API) in urls.py, because XMLRPC requests aren't sent to different URLs depending on which method you're calling or what object you're manipulating. But it makes sense for methods in an API (such as metaWeblog.newPost or metaWeblog.getCategories) to map to methods in a Python module (like views do) so we need an "XMLRPC dispatch view" to take care of this mapping. You specify the module containing your API methods by passing the name of the module as an argument to the view. For instance, here's a line from my urls.py: (r'^metaweblog/', 'ayp.xmlrpc.view', {'module':'ayp.blog.metaweblog'}), "ayp.xmlrpc.view" is the name of the dispatcher, and when it receives a request, it looks in the "ayp.blog.metaweblog" module for a corresponding method. This makes it slightly different from Amit's version, in a few ways:
I did keep his "public" attribute which lets you declare which functions should be web-accessible, and I added a decorator (Python 2.4 style) for setting it. This is a safety feature so that you don't accidentally expose private methods. Here's an example. Let's say we have an XMLRPC API that contains one method, "arithmetic.add". To implement it, we'd write an arithmetic.py that looks something like from ayp.xmlrpc import public @public def arithmetic_add(a, b): return a+b and then expose it in our urls.py with something like urlpatterns = patterns('', (r'^arithmetic/', 'ayp.xmlrpc.view', {'module':'arithmetic'}), ) If you put that into a running Django server, you can test it from a Python shell: >>> from xmlrpclib import ServerProxy >>> server = ServerProxy("http://localhost:8000/") >>> print server.add(3,4) 7 Good times! Read "MetaWeblog & Django" to see how we apply this to blogging.
When I decided it was time to make a blog, I knew I didn't want something that looked like every other blog out there. I wanted a lot of control and flexbility, because I read that some folks (like Natalie Jost) have found that a "turn-key" blogging system can become frustrating because it gets in your way when you have creative ideas about how you want your site to look and work. I bet I would feel the same, and although I'm no designer, I am a programmer so I wanted something built with a modern framework so I could hack on it and add new features. I love Python so I picked Django, and so far I've found it easy and fun. As Jeff Croft writes, Django comes with some nice built-in functionality for blog-like sites. So far, I've got a captcha for preventing comment spam, and Ecto integration via MetaWeblog. Picking a blog API was a tough choice, and I looked at Atom too of course. But even though MetaWeblog has problems, it seems to let Ecto support images better than Atom does. Anyway, I'm looking forward to doing some social bookmarking integration, i18n, photoblogging, and lots of other nifty things like that. |
|||||||||||||



