2

関連モデルのデータを使用してオブジェクトに注釈を付けようとしていますDateTimeField

    class Author(models.Model):
            name = models.CharField(max_length=256)

    class Book(models.Model):
        name = models.CharField(max_length=256)
        is_published = models.BooleanField()
        publication_date = models.DateTimeField()
        author = models.ForeignKey(Author, on_delete=models.PROTECT)

    queryset = Author.objects.annotate(
        has_unpublished=Exists(
            Book.objects.filter(
                author=OuterRef('pk'),
                is_published=False))
    ).annotate(
        last_published_date=Max(
            'book__publication_date',
            filter=Q(book__is_published=True)
        )
    )

sqlite3デフォルトのローカルデータベースとMySQL(を使用)を交互に使用しますmysqlclient。SQLite は機能し、MySQL はクラッシュします。

理由は次のとおりです。両方のデータベースのコンパイラ

results = compiler.execute_sql(**kwargs)

次のように、int と string のタプルのリストのリストを返します。

SQLite

<class 'list'>: [[(1, 'Alice', 1, '2017-01-01 01:00:00'), (2, 'Bob', 0, '2019-01-05 13:00:00'), (3, 'Carol', 1, None), (4, 'Dan', 0, None)]]

MySQL

<class 'list'>: [((1, 'Alice', 1, '2017-01-01 01:00:00.000000'), (2, 'Bob', 0, '2019-01-05 13:00:00.000000'), (3, 'Carol', 1, None), (4, 'Dan', 0, None))]

ここで、SQLite バックエンドが想定される datetime フィールドを検出すると、実行時に次のメソッドを使用してコンバーターを生成します。

django.db.backends.sqlite3.operations.DatabaseOperations

    def convert_datetimefield_value(self, value, expression, connection):
        if value is not None:
            if not isinstance(value, datetime.datetime):
                value = parse_datetime(value)
            if settings.USE_TZ and not timezone.is_aware(value):
                value = timezone.make_aware(value, self.connection.timezone)
        return value

これは正常に動作します。

ただし、MySQL バックエンドはこれを行います。

django.db.backends.mysql.operations.DatabaseOperations

    def convert_datetimefield_value(self, value, expression, connection):
        if value is not None:
            value = timezone.make_aware(value, self.connection.timezone)
        return value

その後、Django は<str>タイムゾーンの認識をチェックしようとしてクラッシュします。

環境

上記は、クラッシュを再現するために使用したおもちゃの例です。

実際の本番環境では、MySQL コンパイラは、奇妙なことに、注釈付きの作成者の完全なリストを要求するexecute_sqlとオブジェクトを含む「生の」データのタプルのリストのリストを返し( )、特定の作成者を要求すると文字列を含む同じリストを返します。 1 つ ( )。クエリセットのそれぞれの sql クエリは、単一の WHERE 句の存在によってのみ異なります。datetime.datetimeaction=='list'action=='retrieve'

これにより、私は何か間違ったことをdatetime.datetimeしていて、ドライバーから取得するはずの生のデータを取得していないと思います (Django のバグの可能性がはるかに低いというよりも)。

私は何をすべきか?N+1 は問題外です。これがアプリのメインのオペレーター ビューになり、datetime フィールドは実際には2つの一対多の関係の深さになります。

4

0 に答える 0