Friday, September 23, 2011

Profiles: Breaking Normalization

In the summer of 2010 I either saw this pattern or cooked it up myself. It is specific to the Django profiles system and helps me get around some of the limitations/features of django.contrib.auth. I like to do it on my own projects because it makes so many things (like performance) so much simpler. The idea is to replicate some of the fields and methods on the django.contrib.auth.model.User model in your user profile(s) objects. I tend to do this usually on the email , first_name , last_name fields and the get_full_name method. Sometimes I also do it on the username field, but then I also ensure that the username duplication is un-editable in any context.

Sure, this breaks normalization, but the scale of this break is tiny. Duplicating four fields each with a max of 30 characters for a total of 120 characters per record is nothing in terms of data when you compare to avoiding the mess of doing lots of profile-to-user joins on very large data sets.

One more thing, I've found that most users don't care about or for the division between their accounts and profiles. They are more than happy with a single form, and if they aren't, well you can still use this profile model to build both account and profile forms.

Alright, enough talking, let me show you how my Profile models tend to look:

from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext_lazy as _

class Profile(models.Model):
    """ Normalization breaking profile model authored by Daniel Greenfeld """
    
    user = models.OneToOneField(User)
    email = models.EmailField(_("Email"), help_text=_("Never given out!"), max_length=30)
    first_name = models.CharField(_("First Name"), max_length=30)
    last_name = models.CharField(_("Last Name"), max_length=30)

    # username field notes:
    #     used to improve speed, not editable! 
    #     Never changed after original auth.User and profiles.Profile creation!
    username = models.CharField(_("User Name"), editable=False) 

    def save(self, **kwargs):
        """ Override save to always populate changes to auth.user model """
        user_obj = User.objects.get(username=self.user.username)        
        user_obj.first_name = self.first_name
        user_obj.last_name = self.last_name
        user_obj.email = self.email
        user_obj.is_active = self.is_active        
        user_obj.save()
        super(Profile,self).save(**kwargs)

    def get_full_name(self):
        """ Convenience duplication of the auth.User method """
        return "{0} {1}".format(self.first_name, self.last_name)

    @models.permalink
    def get_absolute_url(self):
        return ("profile_detail", (), {"username": self.username})

    def __unicode__(self):
        return self.username

All of this is good, but you have to be careful with emails. Django doesn't let you duplicate existing emails in the django.contrib.auth.model.User model so we want to catch that early and display an elegant error message. Hence this Profile form:

from django import forms
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

from profiles.models import Profile

class ProfileForm(forms.ModelForm):
    """ Email validation form authored by Daniel Greenfeld """
        
    def clean_email(self):
        """ Custom email clean method to make sure the user doesn't use the same email as someone else"""
        email = self.cleaned_data.get("email", "").strip()

        if User.objects.filter(email=email).exclude(username=self.instance.user.username):
            self._errors["email"] = self.error_class(["%s is already in use in the system" % email])
            return ""            

        return email

    class Meta:
        
        fields = (
                    'first_name',
                    'last_name',
                    'email',
                    )
        model = Profile

Wednesday, September 14, 2011

History of my most used shell commands

I ran this a few years back and I'm running it again today.

What is interesting is that compared to the older history, git has replaced svn, pip has replaced easy_install, and virtualenv has now completely subsumed buildout. Oh, how the mighty have fallen!
$ history | awk '{a[$2]++ } END{for(i in a){print a[i] " " i}}'|sort -rn |head -n 20

209 git
123 python
34 ls
31 mate
18 cd
14 pwd
9 hg
8 touch
7 rm
6 cp
5 pip
5 mv
5 django-admin.py
4 mkvirtualenv
3 mysql
3 mkdir
3 bash
2 deactivate
2 add2virtualenv
1 workon

Tuesday, September 13, 2011

Quick conferences report: Presentations

My lovely FiancĂ©e, Audrey Roy, was invited to be the opening keynote speaker at both PyCon Australia on Diversity in Python (video) and PyCon New Zealand on Python on the Web.

As for me, I managed to get talks into both of those conferences AND DjangoCon US. I co-presented on three of them, and I share all credit for success with my cohorts. The talks I gave at the conferences were (I'll post videos when they get up):

Confessions of Joe Developer (PyCon Australia, DjangoCon US)
The genesis of this talk was as a lightning talk at I gave at the Hollywood Hackathon. It is a talk about admitting that us mere mortals need to ask questions, take notes, and follow good practices in general. I gave it again at LA Django this summer, extending it to a full length talk complete with lots of technical content. At PyCon Australia I toned down the technical content because I was nervous, and while the response was positive, it  could have been much better. So for DjangoCon I ramped up the tech-talk and it worked much better. I've now given the talk 4 times, and I'm leaning towards retiring it.
 

Python Worst Practices (PyCon New Zealand)
This talk grew out of a SoCal Piggies lightning talk which I gave for the purpose of humor. Often we as Python developers are smug in the clarity of the language that we don't realize just how easily we can obfuscate code. In fact, I contend that Python is fully capable of a code obfuscation contest. This talk rejects a lot of crazy practices I've either done myself or had to debug from other people's work. For New Zealand I added a ton of content and tested things pretty diligently. The variable naming pages stumped some people I really respect and I was quite happy with that result.
 

Django Packages Thunderdome (co-presented with Audrey Roy, DjangoCon US)
Audrey did most of the work for this presentation. In this talk I helped review a horde of Django Packages across 7 different categories. It was nerve wracking because every part of our talk would get judged - but Audrey kept things really positive and made it clear we were providing constructive criticism. I think she got her message across to most people, and more importantly, it got a lot of people thinking about what ought to be normal community standards. I'll probably blog on those community thoughts and statements later, but I think Audrey (with help from me) accomplished what she aimed to do.

View more presentations from Audrey Roy

Advanced Django Form Usage (co-presented with Miguel Araujo)
Some time ago Miguel befriended me and helped resurrect the django-uni-form project. He graciously agreed to help me present on Django Forms and we decided to make the talk as sophisticated as possible. Previous Django form talks have been good, but focused on the fundamentals and we wanted to do something really different. This talk was hard because Miguel and I were on opposite sides of the planet, so we did a lot of github pull/pushes. In both doing research and presenting Miguel did an unbelievably good job and I hope he does more of this in the future. The response was extremely positive and I'm certain that our plan of getting our notes/work/transcript into Django core is well on it's way.
 

Ultimate Django Tutorial Workshop (DjangoCon US)
I got about 10 professional Django experts in a room, including Django core developers, and had them help me coach nearly 20 people through a modified version of the Django tutorial. Students seemed to learn tons, lots of socializing happened thanks to some happy accidents, and the experts got a chance to really see where the Django tutorial needs work. PyLadies organizer Esther Nam spent her sprint days working on something that ties the slides into the Django Tutorial - and for now I'm holding off on sharing my work until she says her work is done.

Summary
These were amazing opportunities to speak and will hopefully make a difference. I wouldn't have traded all of this for the world. It was a lot of work, and I doubt I'll ever go quite at this pace again. My plan is to do fewer talks and make them much better.

Sunday, September 4, 2011

Responses to Github is my resume

Shortly after I posted Github is my resume the responses started coming in. They seemed to fill these categories:

"Github is a portfolio, not a resume!"

I think this is rather valid, being a much more accurate description of the role that Github and other social coding sites are having in getting developer jobs these days.  Two of the more choice responses in this category were posts by Gini Trapini and Andy Lester.

"In X years of hiring, I've never requested source code along with the resume!"

This comment raised the issue that personality, location, writing skills, etc were important. I agree that being able to not annoy your team into losing productivity is important, but it doesn't negate the frequent desire to be able to review the work of potential hires. Ignore the code at your own risk.

"Using only binary for calculations, how many ping pong balls fit in your car?"

A couple people said they prefer to ask programming questions or challenging problems in interviews to seeing portfolios of code. Personally, I think a few programming questions are okay but in my opinion 'challenging problems' all too often means sticking your interviewees with puzzles and trick questions that all too often have nothing to do with the day-to-day work of being a developer.