例
関連モデルのデータを使用してオブジェクトに注釈を付けようとしています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.datetime
action=='list'
action=='retrieve'
これにより、私は何か間違ったことをdatetime.datetime
していて、ドライバーから取得するはずの生のデータを取得していないと思います (Django のバグの可能性がはるかに低いというよりも)。
私は何をすべきか?N+1 は問題外です。これがアプリのメインのオペレーター ビューになり、datetime フィールドは実際には2つの一対多の関係の深さになります。