(Or: A Twisty Tale of Python Programming)
Fresh on the heels of success, I sailed ever further…
So we’ve got this web app. It’s written in PHP. I didn’t write the thing, but I was able to dive in and get work done pretty quickly anyway. I’m no expert, but I already feel like I can get what I want done in that language. Now, the database structure we currently have is less than flexible, so I decided to have a go at wrenching it around into something more readily expanded to our needs. Something that wouldn’t make every bug I tried to fix into an awful hack. I spent a week thinking it over, drawing things up, and I drew up a plan. Great! Now all I had to do was get started.
Getting started is always the hard part for me. It always seems orders of magnitude more difficult than it ever really is. You might, therefore, be a little puzzled to learn that I abandoned everything I had learned up to this point to give something new a try. You see, Kris likes Python. I am ambivalent, but I’m looking at rewriting everything from scratch as if it isn’t that bad of an idea. The more I learn, the more I see that I want to improve. Prepared transactions. Separation of presentation logic and application logic. Normalized tables. All of this is surely within my reach, it only depended on how far I wanted to reach.
I came up with a compromise: Since our app was working well enough by this point on its own, there was no sense in mucking around with it. I decided to set up a separate development server to build my DB from scratch and test whether it met the design goals or not. This required setting everything up from the bottom up. Well, I reasoned, why don’t I just do it in Python? Databases are portable, I could see how I feel about Python and possibly make my brother happy about the foundations of our application.
OK, now Kris has been through this before but I haven’t. I listened to his stories about all the troubles he had trying to do what I was about to embark on, and I figured, “well, I’m sure I can do it anyway.” And I did, too. I spent a couple days struggling with cPanel, with mostly non-disastrous results, but I didn’t have a working setup yet. Thinking better of it, we bought new hosting to work from. Thank you, apt developers! I mean, package names are always a bitch, but I set that server up in a matter of minutes rather than days.
You might think now is the time I would start coding.
Well, it’s a web app. I have two choices: write everything myself from scratch the hard way, or take advantage of the work someone else has already done. I had to pick a framework. Django left a bad taste in my mouth right away, and I was looking for something that wasn’t going to get in my way too badly. I settled on CherryPy, with Mako for templating. I liked the look of Mako, and CherryPy seemed pretty plain. I let myself get talked into installing Postgresql instead of MySQL. How hard could it be to get another database server going, right? Insert another day or so here (including setting up PhpPgAdmin, and various administrative tasks). On the cPanel server I had gotten this far, but it was a struggle the whole way. It was pretty simple on Ubuntu, but I still didn’t know what I needed to do to make sure it was properly configured (access, etc.). I finally got that all taken care of.
Time to start coding!
Well, I need a DB library for Python first. I like SQL, I’ll just get something simple that lets me execute SQL statements in a safe way and do that. But I wonder why everyone’s so sprung on SQLAlchemy? Let’s see what this is about…. OK, if I want to, I can use SQLAlchemy to execute plain SQL queries, and it seems well known and active, so sure. I can install that. But I know it has this whole other function called ORM (object relational mapping). I couldn’t just write that off without knowing what I was doing, so I read about it. It actually seemed pretty nice, but it was foreign to me. Well, that’s fine, I’ll just learn how to use it. This is a testing sample app anyway, so why not test everything that I’m considering actually using? Makes sense!
It was around that time that I switched to TurboGears. Whether you like or dislike TG, you’re probably facepalming about now, but that’s what I did. I don’t even remember why, really, other than it came with the things I wanted to play with (Mako, SQLAlchemy), seemed to have other useful stuff besides, and seemed to be a well-performing framework. Back to Google it was. Find some instructions, follow them. I got my virtualenv set up, I quickstarted a TurboGears project, ran it, and everything worked great! Except the folder wasn’t in my ftp path. Like a Linux newb, I moved the folder instead of linking it. TG didn’t like that. Nuke and start over. Set up Mercurial. Did you know that there’s no switch for “hg add” to add files recursively? Yeah, I spent a while trying to make it add most of the files in my project folder (but not all). It turns out that “hg add” with no parameters is recursive. Those jokers!
Great, okay. I’ve got Python. I’ve got TurboGears. I’ve got Postgresql. I’ve got Mako. I’ve got SQLAlchemy. I’ve quickstarted my project, it talks to the database, everything works just as it should. Time to start coding!
Now how do I want to go about this? Let’s see, there’s users, groups, and permissions. Users can be in any number of groups, and any number of permissions can be associated with a group. That’s alright, but what I’d really like is to be able to add individual permissions to users. I know how to make junction tables, this should be easy. It takes a while to get oriented, because I don’t know Python, or SQLAlchemy, or TurboGears. I find the file that defines the User class and make my modifications. I also find a property of the Users class called permissions, which compiles the permissions for the groups for the user. Hoping that this is the mechanism used for access, I make an elegant modification: Just before the loop that adds permissions for every group, I initialize the set to Users.permissions – the relationship I defined to junction Permissions with Users. Now it will return the permissions that the user is in as well as the permissions of the groups the user is in. Spent a day on this thing, and the code is finally in a place where I can run it to test!
I get errors, of course. I fix the errors and run it again. More errors. Repeat for a while, but now there’s one error I don’t quite get. Ahh, I tried to define a ‘permissions’ relationship on Users, but it already has a property that does the work of gathering the permissions from the groups for the user and returns them straight away. But ‘permissions’ was a pretty useful property, I’d like to use it when I call Users.permissions – but when I do Users.permissions.add(), I don’t want it to call the method, I want it to assign to the relationship I defined for SQLAlchemy. I spend a while trying to make that work, then eventually give up. I can solve this later, I’ll just rename the property so there’s no conflict and get to testing what I wanted to test in the first place – whether Users.perms (creative new name!) is used to authorize site users.
Of course not.
Okay then, all I have to do is figure out how authorization works and patch in a change to do what I want. That shouldn’t be too bad, but I do need to make sure to learn how to make the necessary changes in my own code, not in the libraries. Do it up nice.
Well, I know how authentication gets done now. And authorization. Also decorators, the meaning of ‘middleware’, various Python data structures, classes, methods, attributes, inheritance, instantiation, namespaces, and the relevant folder hierarchy for my project. I’m sure I left out plenty.
IRC was varying levels of helpful, though I did meet some nice people. I didn’t really ask them the right question, which was, “How do I modify repoze.what to allow permissions on individual users?” – but I didn’t really want to be told that answer. I was learning a great deal about the structure of TG, about Python, about a number of the plugins. It was worth it.
Now that I understand how it all fits together, it’s actually fairly simple to accomplish what I want. It’s even explained in the repoze documentation, if you know where to look. I just didn’t know where to look, and when I skimmed past the relevant parts, I didn’t understand the words they were using and what they meant with regards to what I could do with them. So, yeah. RTFM is the flippant response, but I learn more and better this way. Perhaps you’ll learn something too, from my tale!
Now I can start coding.