Как выполнить фильтрацию в Django БД на основе расчетов из разных таблиц - PullRequest
0 голосов
/ 07 мая 2020

У меня 3 модели из 3 разных таблицы :

class Profile(models.Model):
    player_name = models.CharField(max_length=150)
    player_surname=models.CharField(max_length=200)

    class Meta:
        db_table='profiles'

class Results_2019(models.Model):
    player_name = models.CharField(max_length=150) 
    first_score=models.DecimalField(decimal_places=2,max_digits=1000)
    second_score=models.DecimalField(decimal_places=2,max_digits=1000)

    class Meta:
        db_table='results_2019'


class Results_2018(models.Model):
    player_name = models.CharField(max_length=150)         
    first_score=models.DecimalField(decimal_places=2,max_digits=1000)
    second_score=models.DecimalField(decimal_places=2,max_digits=1000)

    class Meta:
        db_table='results_2018'

База данных c. Игроки во всех трех моделях имеют одинаковые имена = ['Alex', 'Chandler', 'Zach'], и все они имеют данные, принадлежащие им для всех столбцов (first_score,second_score)

База данных профиля выглядит так:

id player_name    player_surname    

1   Alex           Chapman          
2   Chandler       Wellington           
3   Zach           Berg 

База данных Results_2019 выглядит так:

id  profile_id  player_name   first_score   second_score

1      1           Alex         70            68
2      2           Chandler     60            62
3      3           Zach         90            85

База данных Results_2018 выглядит так:

id  profile_id  player_name     first_score     second_score

1     1              Alex             78                81
2     2              Chandler         56                66
3     3              Zach             97                95

Я хочу произвести расчеты на основе значений из Results_2019 и Results_2018, чтобы получить:

- игроков, у которых рост first_score и second_score в 2019 и 2018 был более 20%, а затем получил имя и фамилия, которые соответствовали этим критериям, и напечатайте в шаблоне.

Приведенная ниже формула критериев, хотя и не работает, я показываю как руководство к тому, чего я хочу достичь.

first_score_growth=((Results_2019.first_score/Results_2018.first_score)*100) 

и

second_score_growth=((Results_2019.second_score/Results_2018.second_score)*100)

Что я мог сделать до сих пор:

Для этой цели я назначил внешний ключ для БД, у меня есть отдельная столбец в mysql prodile_id. Однако даже миграции он не делает.

Что я мог вычислить и отфильтровать результаты из 1 DB таблицы (Results_2019):

total=Results_2019.objects.annotate(
total_score=(F('first_score') + F('second_score'))/2).filter(total_score__gt=80,)

Но это вычисляет общий балл за 2019 год, но не рост .

Также, когда я добавляю внешний ключ в Results_2019, он дает мне такую ​​ OperationalError (1054, «Неизвестный столбец« Results_2019.profile_id »в« списке полей »»)

I был бы признателен за вашу помощь, чтобы разобраться в этом.

Ответы [ 3 ]

1 голос
/ 07 мая 2020

Во-первых, я бы предложил реструктурировать ваши модели так, чтобы результаты были связаны с профилем и чтобы у вас была единственная модель результатов с полем за год. Я вижу, что у вас есть имя игрока в ваших классах результатов, но к нему можно получить доступ через отношение к модели Player, например result.player.firstname. В качестве примечания к моделям обычно обращаются в единственном числе, т.е. «Профиль», а не «Профили».

class Player(models.Model):
    firstname = models.CharField(max_length=150)
    surname = models.CharField(max_length=200)

class Result(models.Model):
    player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name='results')
    year = models.IntegerField() # this is where you would store 2018 or 2019    
    score = models.DecimalField(decimal_places=2, max_digits=1000)

Я также удалил first_score и second_score, чтобы каждый результат имел только одно поле оценки. Таким образом, у вас будет объект Result для каждой оценки и года, что упростит использование аннотаций, агрегатов и фильтров.

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

score1 = Avg('results__score', filter=Q(results__year=2018))
score2 = Avg('results__score', filter=Q(results__year=2019))
player_growth = Player.objects.values().annotate(growth=(score2/score1)*100)

Обратите внимание, что при использовании 'results__score' используется связанное имя, установленное в поле игрока, и для каждого результата, связанного с этим player.

Приведенное выше должно вернуть что-то вроде этого (с использованием фиктивных данных):

{'id': 1, 'firstname': 'John', 'surname': 'Smith', 'growth': Decimal('110.0')}

Изменить: Если вы не можете изменить структуру модели, тогда вот еще кое-что, что будет работать, добавив метод в модель Player. Обратите внимание, вы должны изменить отношение Result на OneToOneField.

def get_first_score_growth(self):
    return self.results_2019.first_score / self.results_2018.first_score * 100

Вы можете вызвать это в шаблоне следующим образом:

{% for player in players %}
    {{ player.get_first_score_growth }}
{% endfor %}
0 голосов
/ 11 мая 2020

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

Если они присутствуют в обоих списках, вы знаете, что они выросли как в 2018, так и в 2019 году *. 1003 *

def myPageView(request):
    GrowthNames = []

    # loop through both tables
    for MyResults in [Results_2018, Results_2019]:

        # Get all objects and calculate growth
        results_list = MyResults.objects.all()
        for result in results_list:
            score1 = result.first_score
            score2 = result.second_score
            growth = (score2 - score1) / score1

            # if growth is > 20%, add it to our list
            if growth > .02:
                GrowthNames.append(results.player_name)


    # Now, we must find which items appear in our list twice (once for 2018, once for 2019)
    DoubleGrowthNames = list(set([x for x in GrowthNames if GrowthNames.count(x) == 2])

    # With our list our names, we can now get their surnames via querying
    FinalNames = []
    for name in DoubleGrowthNames:
        MyProfile = Profile.objects.filter(player_name=name)[0] 
        FinalNames.append(f'{MyProfile.player_name} {MyProfile.player_surname}')

    # Finally, let's return this to the template
    return render(request, 'page.html', {'FinalNames': FinalNames})

На странице. html:

{% for name in FinalNames %}
<div>{{name}}</div>
{% endif %}
0 голосов
/ 07 мая 2020

Структура вашей модели кажется очень сложной. Почему вы составляете разные таблицы на каждый год? Просто добавьте поле year в таблицу результатов, и это должно сделать вашу работу.

Модель

class Result(models.Model):
    player=models.ForeignKeyField(Profiles)

    first_score=models.DecimalField(decimal_places=2,max_digits=1000)
    second_score=models.DecimalField(decimal_places=2,max_digits=1000)
    year=models.CharField(max_length=4)

    class Meta:
        db_table='result'

Теперь вы можете написать свой запрос, чтобы получить желаемые данные.

...