Dynami c просмотров в Django ORM - лучший подход? - PullRequest
0 голосов
/ 07 февраля 2020

Немного предыстории

Я подумываю перестроить существующий Laravel веб-сайт с Django. Это веб-сайт, который позволяет обмениваться данными тестов от компонентов движителей беспилотных летательных аппаратов. Некоторые тесты выполняются при одновременном тестировании нескольких двигателей и воздушных винтов, что означает, что батарея будет находиться под нагрузкой нескольких двигателей, но это также означает, что поток воздуха от одного воздушного винта влияет на данные, полученные на другом воздушном винте. Это означает, что данные физически связаны. Вот пример . Прямо сейчас я пытаюсь структурировать этот проект так, чтобы он позволял использовать будущие функции, и посмотреть, подходит ли Django ORM.

Упрощенные Django модели

class Benchmark(models.Model):
    title = models.CharField()
    private = models.BooleanField(default=False)
    hide_torque = models.BooleanField(default=False)


class AbstractComponent(models.Model):
    brand = models.CharField()
    name = models.CharField()

    class Meta:
        abstract = True


class Motor(AbstractComponent):
    shaft_diameter_mm = models.FloatField()


class Propeller(AbstractComponent):
    diameter_in = models.FloatField()


class Battery(AbstractComponent):
    capacity_kwh = models.FloatField()


class Powertrain(models.Model):
    benchmark = models.ForeignKey(Benchmark, on_delete=models.CASCADE, related_name='powertrains')
    motor = models.ForeignKey(Motor, on_delete=models.CASCADE)
    propeller = models.ForeignKey(Propeller, on_delete=models.CASCADE, blank=True, null=True)
    battery = models.ForeignKey(Battery, on_delete=models.CASCADE, blank=True, null=True)


class DerivedDataManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset()\
            .annotate(electrical_power_w=F('voltage_v') * F('current_a'))\
            .annotate(mechanical_power_w=F('torque_nm') * F('rotation_speed_rad_per_s'))\
            .annotate(motor_efficiency=F('mechanical_power_w') / F('electrical_power_w'))


class DataSample(models.Model):
    powertrain = models.ForeignKey(Powertrain, on_delete=models.CASCADE, related_name='data')
    time_s = models.FloatField()
    voltage_v = models.FloatField(blank=True, null=True)
    current_a = models.FloatField(blank=True, null=True)
    rotation_speed_rad_per_s = models.FloatField(blank=True, null=True)
    torque_nm = models.FloatField(blank=True, null=True)
    thrust_n = models.FloatField(blank=True, null=True)

    objects = models.Manager()
    derived = DerivedDataManager()

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['powertrain', 'time_s'], name='unique temporal sample')
        ]

Вопрос

Мне удалось добавить «производные» измерения, например, electric_power_w, в каждую строку данных, но я понятия не имею, как добавить производные измерения, объединяющие данные нескольких трансмиссий в рамках одного и того же теста:

Если предположить, что 3 силовых агрегата, каждый со своими данными по напряжению и току, как мне это сделать:

Total_power = powertrain1.power + powertrain2.power + powertrain3.power

для каждой отдельной метки времени (time_s)? Общая мощность имеет смысл только в том случае, если сумма производится для одновременно взятых выборок.

Цель

Без загрузки всех данных базы данных в Django я бы в конечном итоге захотел чтобы получить 5 лучших тестов с точки зрения максимальной общей мощности, принимая во внимание некоторые бизнес-логики c:

  • тесты, помеченные как частные, автоматически исключаются (до тех пор, пока не войдет аутентификация)
  • эталоны, которые предпочитают скрывать крутящий момент, автоматически делают данные крутящего момента вместе со всеми полученными значениями механической мощности и КПД двигателя * от 1047 * до Нет.

Я хотел бы воссоздать эту таблицу , но с добавленными дополнительными столбцами, такими как 'максимальная тяга' и т. Д. c ... Эта таблица разбита на страницы из самой базы данных.

1 Ответ

0 голосов
/ 10 февраля 2020

Хмммм, это довольно сложно ориентироваться. Я собираюсь начать с добавления следующего метода модели аннотации:

from django.db.models import Sum

Полагаю, тогда можно было бы добавить:

.annotate(total_power=Sum(electrical_power_w))

Но я думаю, что проблема заключается в том, что каждый строка в вашей DerivedDataManager queryset представляет одну DataSample, которая, в свою очередь, ссылается на одну Powertrain через поле ForeignKey.

Было бы лучше сделать это в бизнес-логи c слой, сгруппированный по UUID трансмиссии (вам нужно добавить это к вашей Powertrain модели - см. https://docs.djangoproject.com/en/3.0/ref/models/fields/#uuidfield, чтобы узнать, как это использовать. Затем, поскольку вы сгруппировались, вы можете применить аннотация Sum к queryset.

Итак, я думаю, вы хотите перейти по этому пути:

DataSample.objects.order_by(
    'powertrain'
).aggregate(
    total_price=Sum('electrical_power_w')
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...