3

このセットアップのどこかにクレイジーなバグがあります。

データベースは Postgres 9.1 であり、既存のものです (Django によって管理されていません)。その中には、1 つのテーブルといくつかのかなり単純なビューが存在し、そのうちの 1 つは、定義されているようにvalid_logins_dow_popularityと呼ばれます。

 =>\d+ valid_logins_dow_popularity
             View "public.valid_logins_dow_popularity"
   Column   |       Type       | Modifiers | Storage | Description
------------+------------------+-----------+---------+-------------
 logins_avg | double precision |           | plain   |
 dow        | double precision |           | plain   |
View definition:
 WITH by_dow AS (
         SELECT valid_logins_over_time.count, date_part('dow'::text, valid_logins_over_time.date) AS dow
           FROM valid_logins_over_time
        )
 SELECT avg(by_dow.count)::double precision AS logins_avg, by_dow.dow
   FROM by_dow
  GROUP BY by_dow.dow
  ORDER BY by_dow.dow;

Django 1.4 では、そのビューをデータソースとして使用する単純なモデルを定義しました。

class ValidLoginsDowPopularity(models.Model):
    class Meta:
        db_table = 'valid_logins_dow_popularity'
        managed = False

    logins_avg = models.FloatField(
                            db_column='logins_avg')
    # Day of Week (dow)
    dow = models.IntegerField(db_column='dow',
                              primary_key=True)

    def __unicode__(self):
        return u"%d : " % (self.dow, self.logins_avg )

DB から直接データを取得すると、1 つの数値セットが得られます。

SELECT "valid_logins_dow_popularity"."logins_avg", "valid_logins_dow_popularity"."dow" 
  FROM "valid_logins_dow_popularity";

    logins_avg    | dow
------------------+-----
 28.8571428571429 |   0
 95.1428571428571 |   1
 91.4285714285714 |   2
           89.625 |   3
 82.6666666666667 |   4
 61.4285714285714 |   5
 28.4285714285714 |   6
(7 rows)

Django モデルを介してデータを取得すると、関連性はやや曖昧ですが、異なる数値セットが得られます。

In [1]: from core.models import *

In [2]: v = ValidLoginsDowPopularity.objects.all()

In [3]: for i in v:
    print "logins_avg : %f | dow : %d" % (i.logins_avg, i.dow)
   ...:
logins_avg : 25.857143 | dow : 0
logins_avg : 85.571429 | dow : 1
logins_avg : 89.571429 | dow : 2
logins_avg : 86.375000 | dow : 3
logins_avg : 83.000000 | dow : 4
logins_avg : 67.000000 | dow : 5
logins_avg : 28.000000 | dow : 6

今日まで、Django が生成する sql を検証しました。psql から直接実行すると、期待される出力が返されます。IntegerField同様に、FloatFieldを使用して Django モデルを試してみましたがDecimalFieldlogins_avg属性についてはすべて同じですが、値が正しくありません。また、Django コードをバイパスし、それが psycopg2 の問題ではないことを確認するための簡単なテスト プログラムも作成しました。

import psycopg2

def main():
    conn_string = "dbname='********' user='*********'"

    conn = psycopg2.connect(conn_string)
    cursor = conn.cursor()

    sql = "select * from valid_logins_dow_popularity"
    cursor.execute(sql)

    for rec in cursor.fetchall():
        print rec

if __name__ == '__main__':
    main()

これは、実行すると正しいエラーが発生するため、psycopg2 は正しいことをしているように見えます。

$ python test_psycopg2.py
(28.8571428571429, 0.0)
(95.1428571428571, 1.0)
(91.4285714285714, 2.0)
(89.625, 3.0)
(82.6666666666667, 4.0)
(61.4285714285714, 5.0)
(28.4285714285714, 6.0)

これはどのように可能ですか?手がかりをいただければ幸いです。Django コードのどこを掘り下げて、どこで問題が発生しているのかを確認できますか? この問題を Django プロジェクトに報告する必要がありますか?

4

1 に答える 1

1

ビューを再定義し、値を double ではなく数値にキャストします。Django モデルではDecimalField、Postgres に一致するnumeric( numeric(15,10)->などDecimalField(max_digits=15, decimal_places=10)) が必要です。

私は、Django とデータベースの間の浮動小数点値でまったく運がなかったことがなく、データベースと通信する他のソフトウェアでも同様の浮動小数点数の問題が発生しました。< numeric->DecimalFieldを実行することは、浮動小数点値がおかしくないことを保証する唯一の方法です。浮動小数点値を固定小数点値に変更することです。

于 2012-08-16T12:57:14.713 に答える