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 Boston MA and I work for a consulting firm specializing in open source technologies.
This space will deal with the work I've participated in using the Django framework to build applications for enterprise clients.
Finally, I hate the word blog and Drupal.
"А сегодня день архивного работника. У вас на сайте есть "Архив"? Можете праздновать! :))"
at 1:49p.m. March 10, 2010 | permalink
"А интересно, сам автор читает комментарии к этому сообщению. Или мы тут сами для себя пишем? :)"
at 4:58a.m. March 9, 2010 | permalink
"Прошу прощения за оффтопик. Вы продаете сквозные ссылки с сайта? Если да, свяжитесь со мной, плз!"
at 8:06p.m. March 8, 2010 | permalink
"Об этом уже писал кто-то из моих ЖЖ-френдов :("
at 10:29a.m. March 8, 2010 | permalink
"У Вас долго загружается блог - видимо, хостинг плоховат"
at 9:41p.m. March 6, 2010 | permalink
"I just discovered <a href=http://bit.ly/bMGrYw>SatelliteTV</a> on my PC! Ultra cheap at only $50 once off to get the software and an account on the Internet. ..."
at 5:20p.m. March 4, 2010 | permalink
"Логотип мне нравится:)"
at 8:47a.m. March 4, 2010 | permalink
"Девушки из твоих грёз на твоём рабочем столе. 1.Полностью бесплатно 2.100% безопасность вашего ПК 3.Новые девушки каждый день <a href=http://blogs.mail.ru/mail/erorulez/6605707A18ACC7D6.html>смотреть стриптиз бесплатно</a> http://blogs.mail.ru/mail/erorulez/6605707A18ACC7D6.html эгоистка стриптиз ..."
at 5:08a.m. March 4, 2010 | permalink
"uh.. strange .."
at 11:54p.m. March 3, 2010 | permalink
"Hi guys, I know this might be a bit off topic but seeing that a bunch of you own websites, where would the best place ..."
at 11:12p.m. March 3, 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
"В Вашей RSS нельзя получать полные тексты записей, что ли?"
at 9:37p.m. March 1, 2010 | permalink
very helpful ! tanks !