This is a "This is how I solved a problem, I know there is a better way, so someone please tell me" post. Denormalization is something that has gotten a bit of press in the Django community. David Cramer has a great post on a model to handle this and there was some discussion on adding a DenormalisationField to Django core. I ended up needing to use a denormalized field on a project, but still wanted to use managers to handle the related field.
I have a Feed model that is pretty much always retrieved by the user's username. Instead of taking the overhead of the join, we save the username on the model. The Model looks something like this.
class Feed(models.Model):
id = UUIDField(auto=True, primary_key=True)
username = models.CharField(max_length=30)
url = models.URLField(verify_exists=False)
....
You may be saying to yourself that I'm over engineering this bad boy, but there are reasons for everything, so just go with it. Now in the simple case I can use the Feed manger in the view to get the user's feeds like so.
In [3]: from feeds.models import Feed
In [4]: Feed.objects.filter(username='screeley')
Out[4]: []
This is all well and good, but I'd like the ability to use Managers to access the user's feeds on an instance of a User like so.
feeds = user.feeds.all()
Without the foreign key, this just doesn't work. After a period of checking out the Django source to see how they did It I found the ForeignRelatedObjectsDescriptor. It's a little intense and I still have no idea how the __get__ works when setting up the instance, but it gave me a solution.
def add_user_feed_manager(sender, instance, **kwargs):
superclass = Feed._default_manager.__class__
class FeedManager(superclass):
def get_query_set(self):
return superclass.get_query_set(self).filter(
**(self.core_filters))
def add(self, *objs):
for obj in objs:
setattr(obj, 'username', instance.username)
obj.save()
add.alters_data = True
def create(self, **kwargs):
kwargs.update({'username': instance.username})
return super(FeedManager, self).create(**kwargs)
create.alters_data = True
manager = FeedManager()
manager.core_filters = {'username': instance.username,}
manager.model = Feed
setattr(instance, 'feeds', manager)
post_init.connect(add_user_feed_manager, User)
So what does it do? We use the Django built in sign post_init to run this every time an instance of the User model is created. add_user_feed_manager uses the Feed's default manager and adds the username to the core_filters then uses setattr to put the manager on the instance. So here are a few things we can do with this.
In [1]: from django.contrib.auth.models import User
In [2]: user = User.objects.all()[0]
#Create a feed from the user instance
In [3]: user.feeds.create(
url='http://www.screeley.com/rss/latest/')
Out[3]: Feed: - http://www.screeley.com/rss/latest/
#Get all the feeds for a given user
In [4]: user.feeds.all()
Out[4]: [Feed: http://www.screeley.com/rss/latest/]
# Add a field object to the user
In [5]: from feeds.models import Feed
In [6]: f = Feed(
url = 'http://www.djangoproject.com/rss/community/')
In [7]: user.feeds.add(f)
In [8]: user.feeds.all()
Out[8]: [ Feed: http://www.screeley.com/rss/latest/,
Feed: http://www.djangoproject.com/rss/community/]
This met my needs of denormalization and kept in tact a related field manager. Have other people solved this problem and if so, how?
I'm a developer out of San Francisco CA working at a startup.
This space will deal with the work I've participated in using the Django framework to build applications for enterprise clients.
Finally, you should follow me on twitter.
"generic z-pak <a href=http://sefsa.org>buy azithromycin</a>"
at 7:53p.m. Aug. 27, 2010 | permalink
"How do i come up with cash from online gambling? <img>http://shrtn.info/smile/ref.php</img>"
at 2:50a.m. Aug. 25, 2010 | permalink
"http://needman.ru замуж за иностранца <a href=http://needman.ru>знакомства с иностранцами</a>"
at 12:59p.m. May 18, 2010 | permalink
"Yebhewjw <a href="http://yebhewjw.de">yebhewjw</a> http://yebhewjw.de yebhewjw http://yebhewjw.de"
at 11:41p.m. April 29, 2010 | permalink
"Thanks for this, unbelievable our developer has a robots no follow tag on our site, no wonder it wasn't being found by the search engines ..."
at 7:40a.m. March 2, 2010 | permalink
"maybe you are right. but how often robots.txt is actually accessed? and how much overhead there is? I'm curious - quantitatively - how big of ..."
at 7:13p.m. Dec. 12, 2009 | permalink
"Lovely idea! Thanks for sharing. I'm gonna have a closer look at the patch for Django 1.2. This could help switching template engines a lot. ..."
at 9:14a.m. Nov. 2, 2009 | permalink
"That was an inspiring post, I think Drupal is great! how could you hate it so much, Thanks for writing, most people don't bother."
at 11:14a.m. Oct. 28, 2009 | permalink
"@Evgeniy. Yes at: http://code.google.com/p/django-alfresco/"
at 10:42a.m. Oct. 22, 2009 | permalink
"Is this released as an open source project?"
at 1:21a.m. Oct. 22, 2009 | permalink
"Interesting, thanks for the examples that you have shared, these are great... Anyway, thanks for the post"
at 7:55a.m. Oct. 16, 2009 | permalink
"Quite inspiring, looks pretty easy aswell, as you have laid it out in such a way, great work, keep it up Thanks for bringing this ..."
at 10:01a.m. Oct. 8, 2009 | permalink
very helpful ! tanks !