Лучший способ моделирования и отображения сложного расписания в Django
Я составляю расписание для своей рабочей группы, в которую входит около 70 человек, выполняющих около 50 различных типов смен. В общей сложности это примерно 300 заданий в неделю. Какие смены требуют покрытия, будет меняться изо дня в день, но будет оставаться постоянным из недели в неделю. Я пытаюсь найти наиболее эффективный способ смоделировать это и отобразить в webapp.
На данный момент у меня есть следующие модели:
class ShiftName(models.Model):
# Each generic shift
...
class Employee(models.Model):
...
class ShiftAssignment(models.Model):
# The actual individual shift coupled with a foreignkey to the employee assigned to that shift
...
Тогда для отображения это будет выглядеть примерно так:
M T W Th F Sat Sun
Shift1 Bob Sally Sally Jim Chris
Shift2 Sally Bob Chris Bob Sally Chris
Shift3 Jim Chris Mark Bob
...
Shift40 Mark Mark Bob Sally
Сейчас мой HTML выглядит следующим образом:
<div>
<div class="row">
<div class="col-2">
<table>
{% for shift in shifts %}
<tr>
<td>{{shift}}</td>
</tr>
{% endfor %}
</table>
</div>
<div class="col-10">
<table>
<tr>
{% for date in dates %}
<td>{{date.date}}</td>
{% endfor %}
</tr>
<tr>
{% for date in dates %}
<td>
<table>
{% for shift in shifts %}
<tr>
<td>{% getassignment date.id shift %}</td>
</tr>
{% endfor %}
</table>
</td>
{% endfor %}
</tr>
</table>
Что я пробовал:
- Generating an "empty" shift assignment even on days when that shift is not needed. This way I can iterate through
{% for shift in ShiftName.objects.all %}
{% if shift.employee is not None %}
{{shift.employee.name}}
{% endif %}
{% endfor %}
Это работает, но требует создания множества дополнительных объектов базы данных, которые я никогда не буду использовать.
- Using a simpletag to identify the assigned employee (shown in the big block of code above). This also works but is very slow because the page has to make ~5000 separate queries to the database on each page load.
Мне кажется, что должен быть более эффективный способ сделать это без генерации кучи пустых объектов? Кто-нибудь может что-нибудь посоветовать?
Разработал гораздо более совершенный метод. Не уверен, что это оптимальный метод, но это огромное улучшение по сравнению с тем, что у меня было.
Я не хотел создавать кучу пустых объектов базы данных только для заполнения пространства, но понял, что могу вместо этого создать список и присвоить пустой элемент этому списку. Таким образом, я избегаю кучи пустых объектов в базе данных, но при этом получаю функциональность заполнителя. Вот как выглядит моя функция создания списка:
shift_assignments = ShiftAssignment.objects.order_by('shiftname__order', 'date').values_list('shiftname__order', 'date__id', 'employee__lastname')
shift_assignment_list = [0 for _ in range(8988)] # 8988 is the distinct shift types * the days in the schedule, which I'm doing 12 week blocks so this is 106 * 84
for shift in shift_assignments:
index = ((shift[0]-1) * 84 + shift[1]) - 1 # Find the location to insert the lastname into the list
shift_assignment_list[index] = shift[2] # Insert the employee name at that index
Итак, вывод моего набора запросов shiftassignments будет выглядеть примерно так ('17', '15', 'Smith'), где 17 - это номер строки, а 15 - номер столбца. Таким образом, чтобы получить точное расположение индекса, учитывающее изменения от 1 индекса до 0 индекса, выглядит следующим образом (shift[0] - 1) * 84 + shift[1] - 1
Тогда я просто прохожусь циклом по моему списку shift_assignment_list, игнорируя пробел, если выход равен 0, в противном случае печатая фамилию сотрудника, и делаю паузу в моем forloop через каждые 84 элемента следующим образом:
<table class="table" style="padding: 0px; margin: 0px;">
<tr style="border-bottom: 1px solid black">
{% for date in dates %}
<td style="min-width: 200px;">{{date.date}}</td>
{% endfor %}
</tr>
<tr>
{% for employee in shift_assignment_list %}
<td style="min-width: 200px">
{% if employee != 0 %}
{{employee}}
{% endif %}
</td>
{% if forloop.counter|divisibleby:84 %}
</tr>
<tr>
{% endif %}
{% endfor %}
</tr>
</table>
И это хорошо работает.
Время загрузки сократилось с 9,4 с до 0,007 с, так что я бы назвал это успехом! Кроме того, сгенерировав этот список один раз, я мог бы сохранить его как константу, а не генерировать его каждый раз заново, что могло бы еще больше сократить время загрузки. Но это, вероятно, излишне, учитывая, как быстро он загружается сейчас.