django
Django sharing anotations between related Items
Consider a room booking system. You might have a Building, Floor, Room models as well as a Booking. We give the room a name based on its building and floor: class Room(models.Model): number = models.PositiveIntegerField() name = models.CharField(..) floor = models.ForeignKey('Floor') def __str__(self): return '%s, #%d Floor %d, %s' % ( self.name, self.number, self.floor.number, self.floor.building.name ) This is woefully inefficient when you're doing hundreds of them (eg admin list, big reports, etc.) so I've taken to writing managers like this: class RoomManager(models.Manager): def get_queryset(self): return super().get_queryset().annotate( roomname=Concat( 'name', V(', #'), 'number' V(' Floor '), 'floor__number' V(', '), 'floor__building__name', output_field=models.CharField() ), ) And that works. It does everything I wanted it to. It's fast and I've reworked the __str__ to do a if hasattr(self, 'roomname'): return self.roomname before it does the horrendous multi-query string builder. But now on top of this, I have Booking. Each Booking instance is linked to a single room. There are many cases where to list Bookings, I actually also list room names. What I've done is write a BookingManager: class RoomManager(models.Manager): def get_queryset(self): return super().get_queryset().annotate( roomname=Concat( 'room__name', V(', #'), 'room__number' V(' Floor '), 'room__floor__number' V(', '), 'room__floor__building__name', output_field=models.CharField() ), ) But what the hell? I'm repeating myself. Django is all about DRY and here I am copy and pasting a huge messy annotation around. It's disgusting. My question is... Is there another way?
While writing this I had an idea, I could write a method that allowed a prefix to be passed in for the relation to room. That way I could call it from anything that was related to room, and it would get the right stuff: class RoomManager(models.Manager): def get_queryset(self): return super().get_queryset().annotate( roomname=RoomManager.roomname() ) #staticmethod def roomname(prefix=''): return Concat( prefix + 'name', V(', #'), prefix + 'number' V(' Floor '), prefix + 'floor__number' V(', '), prefix + 'floor__building__name', output_field=models.CharField() ) And in the BookingManager I can just annotate on RoomManager.roomname('room__') It's cleaner and I'll use it elsewhere but it doesn't feel very clever.
How about something like this? class Room(models.Model): number = models.PositiveIntegerField() name = models.CharField(..) floor = models.ForeignKey('Floor') def __str__(self): return '%s, %s %s' % ( self.name, self.number, self.floor ) class Floor(models.Model): number = models.PositiveIntegerField() building = models.ForeignKey('Building') def __str__(self): return 'Floor %d, %s' % ( self.number, self.building ) class Building(models.Model): name = models.CharField(...) def __str__(self): return '%s' % ( self.name )
Related Links
Django-REST frameowork restore_object attrs parameters
django tables2 checkbox
How can i get my south migrations to work?
Django, boto, S3 and easy_thumbnails not working in production environment
use reverse in a http response redirect in django
Django template and variable {% if x == “about” %} does not work
Difficulties pushing application to Heroku
Sphinx autodoc not importing modules
Tell Django's model to use as primary key a set of foreign keys
Django replace all occurrences [name] with auctual contact.name
How to store static, but user-generated files on Heroku/Django
Django-South not detecting addition of default value on field
How do i should i get sorl thumbnail to work?
Django - How ModelChoiceField queryset's works?
non-volunteer logout when linking between server and client django apps
Django models and admin panel