Displaying only objects without subclasses

Sometimes, the django.contrib.auth User model just doesn’t cut it.

I have bounced around between ways of handling this sorry fact. My production system uses a nasty system of Person-User relationships (where, due to old legacy code, I need to keep the primary keys in sync), to monkey-patching User, using UserProfiles, and subclassing User.

First, a little on the nasty hack I have in place (and how that will affect my choices later on).

My project in work is a rostering system, where not everyone who is a Person in the system needs to be a User. For instance, most managers (who are Users) do not need their staff to be able to log in. However, they themselves must be a Person as well as a User, if they are to be able to log in, but also be rostered on.

Thus, there are many people in the system who are not Users. They don’t have a username, and may not even have an email address. Not that having an email address is that useful in the django User model, as there is no unique constraint upon that.

So, I am currently kind-of using Person as a UserProfile object, but there are Person instances that do not have an associated User, and some of these are required to have an email address, and have first and last names. So, there is lots of duplication across these two tables. Which need to be kept in sync.

The solution I am looking at now moves in the other direction.

A Person is a subclass of User. It has the extra data that we need for our business logic (mobile phone number, company they work for), but I have also monkey-patched User to not require a username. We are moving towards using email addresses for login names anyway, so that isn’t a problem. That has its own concerns (not everyone has a unique email address, but there are workarounds for that).

But not every User will have a Person attached. The admin team’s logins will not (and this will be used to allow them to masquerade as another user for testing and bug-hunting purposes). So, we can’t just ignore the User class altogether and do everything with the Person class.

This is all well and good. I have an authentication backend that will return a Person object instead of a User object (if one matches the credentials). Things are looking good.

Except then I look in the admin interface. And there we have all of the Person objects’ related User objects, in the User table. It would be nice if we only had the ‘pure’ Users in here, and all Person objects were just in their category.

So, I needed a way to filter this list.

Luckily, django’s admin has this capability. In my person/admin.py file, I had the following code:

1 from django.contrib import admin
2 from django.contrib import auth
4 class UserAdmin(auth.admin.UserAdmin):
5     def queryset(self, request):
6         return super(UserAdmin, self).queryset(request).filter(person=None)
8 admin.site.unregister(auth.models.User)
9 admin.site.register(auth.models.User, UserAdmin)

And, indeed, this works.

But then I found another User subclass. Now we needed a type of user that is distinct from Person (they are never rostered, are not associated with a given company, but do log into the system).

I wanted the changes to the admin to be isolated within the different apps, so I needed to be able to get the currently installed UserAdmin class, and subclass that to filter the queryset. So the code becomes (in both admin.py files):

 1 from django.contrib import admin
 2 from django.contrib import auth
 4 BaseUserAdmin = type(admin.site._registry[auth.models.User])
 6 class UserAdmin(BaseUserAdmin):
 7     def queryset(self, request):
 8         return super(UserAdmin, self).queryset(request).filter(foo=None)
10 admin.site.unregister(auth.models.User)
11 admin.site.register(auth.models.User, UserAdmin)

The only difference in the two files is the foo. This becomes whatever this sub-class’s name is. Thus, it is person in the person/admin.py file, and orguser in the orguser/admin.py file.

The next step is to change the backend so that it will automatically downcast the logged in user to their child class. Other people have detailed this in the past: mostly the performance issue vanishes here because we are only looking at a single database query for a single object.

Adobe PDF Workflow under Snow Leopard

My partner is a mad keen Macromedia Freehand user. This is one of the reasons she has been able (and prepared) to stick with her trusty old G4 iMac until now. It is also the reason our brand new iMac won’t be running Lion anytime soon.

So, when we got the new iMac, I had to setup Freehand so it worked. The next thing was to bring across all of her thousands of fonts. Tip: if fonts look jaggy, force the font cache to rebuild and restart.

Finally, we got to the stage where she was trying to create some PDFs. And since Adobe is not always the best OS citizen, we found the old way she used to create them no longer worked under Snow Leopard. Using the system PDF generator resulted in far inferior PDF quality: jaggy fonts, lines within curves.

Now, I get the feeling that the system isn’t at fault here, as it is very capable of creating PDF files of correct quality. Indeed, I was able to get high quality PDFs generated from other programs (and as we’ll see in a minute, even from files generated from Freehand). So, it seems that Freehand ‘knows’ this is a ‘preview’ version, and cuts the quality of data it sends.

Eventually, after much work, I found that creating a PostScript file worked okay, but the page size was incorrect. At this stage I had installed the printer driver for our old Epson Stylus PHOTO EX, which resulted in the print dialog box no longer showing all of the Freehand MX settings.

The final solution was to create an IPP printer, to localhost, that is called Adobe PDF. This is set to use the generic PostScript driver. All of a sudden, we are able to access the Freehand MX advanced settings in the print dialog, and create PostScript files that are the right size, and of suitable quality. She then either uses Preview or Acrobat Distiller to turn these into PDFs.