TL; DR Dies war ein Fehler in Django 1.7, der in Django 1.8 behoben wurde.
Der Wechsel zu meistern direkt ging und nicht unter einer deprecation Zeit ging, die nicht zu ist überraschend, da die Aufrechterhaltung der Rückwärtskompatibilität hier sehr schwierig gewesen wäre. Überraschender ist, dass das Problem in der 1.8 release notes nicht erwähnt wurde, da der Fix das Verhalten des gerade aktiven Codes ändert.
Der Rest dieser Antwort ist eine Beschreibung, wie ich das Commit mit git bisect run
gefunden habe. Es ist hier für meine eigene Referenz mehr als alles andere, also kann ich hierher zurückkommen, wenn ich jemals wieder ein großes Projekt teilen muss.
Zuerst haben wir einen Django-Klon und ein Testprojekt eingerichtet, um das Problem zu reproduzieren. Ich habe virtualenvwrapper hier verwendet, aber Sie können die Isolierung tun, wie Sie es wünschen.
cd /tmp
git clone https://github.com/django/django.git
cd django
git checkout tags/1.7
mkvirtualenv djbisect
export PYTHONPATH=/tmp/django # get django clone into sys.path
python ./django/bin/django-admin.py startproject djbisect
export PYTHONPATH=$PYTHONPATH:/tmp/django/djbisect # test project into sys.path
export DJANGO_SETTINGS_MODULE=djbisect.mysettings
die folgende Datei erstellen:
# /tmp/django/djbisect/djbisect/models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
class GFKmodel(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
gfk = GenericForeignKey()
class GRmodel(models.Model):
related_gfk = GenericRelation(GFKmodel)
auch diese:
# /tmp/django/djbisect/djbisect/mysettings.py
from djbisect.settings import *
INSTALLED_APPS += ('djbisect',)
Jetzt haben wir ein Arbeitsprojekt, erstellen Sie die test_script.py
mit git bisect run
zu verwenden:
#!/usr/bin/env python
import subprocess, os, sys
db_fname = '/tmp/django/djbisect/db.sqlite3'
if os.path.exists(db_fname):
os.unlink(db_fname)
cmd = 'python /tmp/django/djbisect/manage.py migrate --noinput'
subprocess.check_call(cmd.split())
import django
django.setup()
from django.contrib.contenttypes.models import ContentType
from djbisect.models import GFKmodel, GRmodel
ct = ContentType.objects.get_for_model(GRmodel)
y = GRmodel.objects.create(pk=456)
x = GFKmodel.objects.create(pk=789, content_type=ct, object_id=y.pk)
query1 = GRmodel.objects.values_list('related_gfk', flat=1)
query2 = GRmodel.objects.values_list('related_gfk__pk', flat=1)
print(query1)
print(query2)
print(query1.query)
print(query2.query)
if query1[0] == 789 == query2[0]:
print('FIXED')
sys.exit(1)
else:
print('UNFIXED')
sys.exit(0)
Das Skript muss ausführbar sein, fügen Sie also das Flag mit chmod +x test_script.py
hinzu. Es sollte sich in dem Verzeichnis befinden, in dem Django geklont ist, d. H. /tmp/django/test_script.py
für mich. Dies liegt daran, dass import django
zuerst das lokal ausgecheckte Django-Projekt und nicht irgendeine Version von Site-Paketen abholen sollte.
wurde die Benutzeroberfläche von git bisect, um herauszufinden, entwickelt, bei denen Fehler erschienen, so die üblichen Präfixe von „schlechten“ und „gut“ sind nach hinten, wenn Sie versuchen, wenn ein bestimmte Fehler fest ist, um herauszufinden,. Dies mag etwas auf den Kopf gestellt erscheinen, aber das Testskript sollte mit Erfolg (Rückkehrcode 0) enden, wenn der Fehler vorhanden ist, und es sollte fehlschlagen (mit Rückgabecode ungleich Null), wenn der Fehler behoben ist. Das hat mich ein paar Mal gestolpert!
Also wird dieser Prozess eine automatisierte Suche durchführen, die schließlich das Commit findet, wo der Fehler behoben wurde. Es dauert einige Zeit, denn zwischen Django 1.7 und Django 1.8 gab es viele Commits. Es halbierte 1362 Revisionen, etwa 10 Stufen, und schließlich Ausgabe:
1c5cbf5e5d5b350f4df4aca6431d46c767d3785a is the first fixed commit
commit 1c5cbf5e5d5b350f4df4aca6431d46c767d3785a
Author: Anssi Kääriäinen <[email protected]>
Date: Wed Dec 17 09:47:58 2014 +0200
Fixed #24002 -- GenericRelation filtering targets related model's pk
Previously Publisher.objects.filter(book=val) would target
book.object_id if book is a GenericRelation. This is inconsistent to
filtering over reverse foreign key relations, where the target is the
related model's primary key.
, die genau das begehen ist, wo die Abfrage von einer falschen SQL geändert hat (die Daten aus der falschen Tabelle bekommt)
SELECT "djbisect_gfkmodel"."object_id" FROM "djbisect_grmodel" LEFT OUTER JOIN "djbisect_gfkmodel" ON ("djbisect_grmodel"."id" = "djbisect_gfkmodel"."object_id" AND ("djbisect_gfkmodel"."content_type_id" = 8))
in die korrekte Version:
SELECT "djbisect_gfkmodel"."id" FROM "djbisect_grmodel" LEFT OUTER JOIN "djbisect_gfkmodel" ON ("djbisect_grmodel"."id" = "djbisect_gfkmodel"."object_id" AND ("djbisect_gfkmodel"."content_type_id" = 8))
natürlich aus dem begeht Hash sind wir in der Lage, die Pull-Anforderung und das Ticket leicht auf github zu finden. Hoffentlich kann dies auch einem anderen Tag helfen - die Zweiteilung von Django kann aufgrund der Migrationen schwierig sein!
* Anmerkung: * Django Version ist '(1, 7, 11, 'final', 0)'. Ich kann das in Django 1.8 nicht reproduzieren. – wim
Könnte das bedeuten, dass es eine ist, aber in Django 1.7 entschieden sie sich für 1.8 zu reparieren? – mgilson
Möglich, aber ich suchte hoch und niedrig für die Erwähnung in den Versionshinweisen und konnte es nicht finden. Ich nehme an, 'git bisect' könnte es finden .... – wim