2016-09-24 6 views
0

Рассмотрите систему бронирования номеров. У вас могут быть модели здания, пола, комнаты, а также бронирование. Мы даем комнате название основано на его здании и пол:Django обмен мнениями между связанными предметами

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 
     ) 

Это крайне неэффективно, когда вы делаете сотни из них (например, список администратора, большие отчеты и т.д.), поэтому я принял в письменной форме менеджеров:

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() 
      ), 
     ) 

И что работает. Он делает все, что я хотел. Это быстро, и я переработал __str__, чтобы сделать if hasattr(self, 'roomname'): return self.roomname, прежде чем он сделает ужасный многострочный построитель строк.

Но теперь вместо этого у меня есть Бронирование. Каждый экземпляр бронирования связан с одной комнатой. Есть много случаев, когда перечислять заказы, я на самом деле также перечисляю имена комнат.

Что я сделал это написать 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() 
      ), 
     ) 

Но что, черт возьми? Я повторяюсь. Django - все о DRY, и здесь я копирую и вставляю огромную грязную аннотацию. Это отвратительно.

Мой вопрос ... Есть ли другой способ?

+0

это выглядит, как вы пытаетесь заставить вид на модель - если вам нужен метод, который возвращает полный «хлебных крошек» экземпляра, вы могли бы просто написать метод (по каждой модели), которая добавляет свой имя на корневой части родительского элемента - это будет относиться к базовому классу - зачем использовать аннотации запросов? – MarZab

+0

, если вам действительно нужно отредактировать набор запросов, я думаю, что вы могли бы также построить аннотацию, используя этот метод. – MarZab

+0

Если имя комнаты используется повсюду, почему бы просто не добавить его как столбец db? Комбинация номера с номером/зданием не похожа на то, что часто менялось бы, если вообще когда-либо. – serg

ответ

0

При написании этого у меня была идея, я мог бы написать метод, позволяющий передавать префикс для связи с комнатой. Таким образом, я мог бы назвать его от всего, что было связано с комнате, и было бы получить правильный материал:

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() 
     ) 

И в BookingManager я могу только аннотацию на RoomManager.roomname('room__')

Это чище, и я буду использовать это в другом месте, но это не очень умно.

0

Как насчет чего-то подобного?

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 
     ) 
+0

Я думаю, что я просто обратился к этому в комментариях к вопросу, но в частности, мне нужен только полный адрес пространства, я просто хочу его в двух местах (из набора запросов пробелов и набора запросов для заказов), поэтому я могу перечислить их, не делая массивных присоединяется или сотни тысяч (серьезно) рекурсивных запросов.Аннотации - это то, как я уверен, что это правильно для моего использования, проблема заключается в совместном использовании этой аннотации между моделями. – Oli

+0

А, да, я понимаю сейчас. Хорошо, что ваш менеджер - это хороший способ повторного использования этого, но что касается производительности, у @serg есть хорошая точка - кеширование данных намного лучше, чем создание базы данных для работы (потребуется немного недействительности кэша, но я думаю редактирование не так часто) - дисковое пространство дешевле, чем вычислительная мощность. – MarZab