Officially Lucky

Django, Python, Programming, Web 2.0, The Social Graph, Fashion, Chicago. A whole mixed up bag of stuff.

by Clint Ecker

Django question of the day: two apps, relating them

16 April 2008

Filed under Django, Programming

Here’s a quick question to the Django people following my blog, and one I think will be a huge help to individuals attempting to perform something similar.

I’m currently working on a project that could make use of Nathan Borror’s excellent django-basic-places and django-basic-people applications.

I’m loving them so far, and getting a lot done. However, one thing that I would like to do is allow for a Person to be associated as an “owner” of a place. If I were writing all of these as a cohesive application, I would add an ForeignKey field to the Place model and link it up to the Person model.

Those apps, however, should be standalone and not linked and kept as generic as they are. So how can I link these two models in my project? Should I create some sort of intermediary model that relate the two? Is that too much of a hack?

Is there some mechanism I’m just plain overlooking?

49 Comments

#1. James Bennett posted this 3 months ago.

The mechanism you’re overlooking is the fact that sometimes, even though you might have multiple applications, they can and logically will have dependencies on each other. They should, in that case, be distributed in a single package — e.g., we distribute Ellington as a single package, containing a bunch of apps, some of which have dependencies on each other — and it’s perfectly OK to do so.

Remember: in Python (and hence in Django) you should organize things in ways that make sense case by case, rather than trying to find a universal ideology to follow. So an overarching package containing sub-packages — each one a Django application — which accomplish parts of the goal of the package as a whole is the logical thing to do.

#2. Rob Hudson posted this 3 months ago.

I’d do what you mentioned… create another model that ties Places and People together. It’s the same as if you had a ManyToManyField on them but you’re doing it manually rather than letting Django handle it.

The model would just be something like:

class PeoplePlaces(models.Model):
    person = models.ForeignKey(People)
    place = models.ForeignKey(Place)

I’m guessing that won’t tie together in the admin as nice as if you had used a ManyToManyField, but you could enter this model’s admin page and be able to select the two.

I’ve seen something like this “manual m2m” suggested as a way to have a m2m table with additional fields on the m2m itself.

-Rob

#3. Malcolm Tredinnick posted this 3 months ago.

Hmmm … fun question. Whilst I agree in general with James, it’s probably not necessarily always appropriate to add dependencies if you’re using third-party apps. I tend to dislike modifying third-party sources, so in this case, I would probably go with Rob’s style of solution.

If each place can only have one owner, you can even use a OneToOneField for the Place link, which might make working with it a little easier. You probably want to wait for queyrset-refactor to hit trunk before doing that, since somebody rewrote some of the internals of OneToOneField in that branch and there may be subtle difference (nothing major, though).

(Really cool, crazy thought for people with way too much free time: you could write a custom field that acted like a many-to-many but where you could specify which model it was projected onto. So it would appear as if there was a many-to-many on the Person model in the Python code. Since many-to-many fields don’t add columns to their source model, that’s possible as it doesn’t require any database change, so works with third-party database tables.)

#4. Clint Ecker posted this 3 months ago.

Thanks for the comments guys.

@James: I agree in principle, but I have the same reservations as Malcolm. I really don’t want to go mucking around in those 3rd party apps. If I start going too wild, it makes it difficult to svn up sometimes.

I’ll probably end up going the route Rob suggested, but I’m still open to more elegant solutions that keep me from having to modify Nathan’s applications.

#5. Malcolm Tredinnick posted this 3 months ago.

So I was thinking about this a bit today as I went about my menial existence. You know, I actually had crap to do today… I really didn’t need this extra thing to ponder!. I was contemplating what the best hope for a “nice” solution would be, if there we weren’t restricted by actual existing code. It seems like this probably means “not having to write anything beyond an attribute access to get the owner for a place”.

Modifying the original apps isn’t nice. Maybe adding a column to the database tables of the original apps is possible (so you can then somehow inject a ForeignKey into the Place model), but it feels hacky. But if you don’t really mind that there’s an extra database table involved, providing you don’t have to worry about it, there’s another solution (soon appearing in a trunk checkout near you!) — model inheritance. Subclass the Place model and add a ForeignKey on the subclass — in fact, that’s all that will be on the subclass.

The downside is that any existing functions that return Place objects will still return Places, not the subclass. You can access the subclass with one attribute access, though, so if you don’t need to do that too often, the extra database query won’t kill you. However, I suspect you will normally be using the class directly and you can therefore use your subclass instead of Place and it will only cost one database query to get the Place and the owner.

This led to another thought: that there’s a whole bunch of functions which might benefit from accepting a class to act upon in this new model-inheritance world. Such a function would, e.g., query Place by default, but you could pass in the new CliffPlace class and it would use that and return objects of that type instead.

#6. Clint Ecker posted this 3 months ago.

Excellent idea, Malcolm. I’ll see if I can work it out! :)

#7. Eric Florenzano posted this 2 months, 4 weeks ago.

Malcolm: your idea in response #3 (if I’m interpreting it correctly) is what’s addressed in ticket #6095 in the Django tracker.

#8. kevin posted this 2 months, 4 weeks ago.

just an idea … if partials were supported in django, one could create a Person:partial class and define the additional properties, methods, etc.

then the Person and Person:partial class are “merged” when referenced and result in one Person class with all the properties, methods, etc defined in both files.

just an idea.

Add a comment

You may use Markdown syntax in your comment, HTML will be removed. By posting a comment here, you are agreeing to the terms of my comment policy.



Note: These comments are filtered through Akismet and are subject to moderation. Your post may not show up immediately if the system deems you sufficiently spammy :)

by Clint Ecker

tech journalist, web developer, cyclist, and chicagophile.

RSS Feeds

Search

A Django site.

©2008 Clint Ecker <me@clintecker.com>