TL; DR Questo era un bug in Django 1.7 che è stato corretto in Django 1.8.
Il cambiamento è andato direttamente da padroneggiare e non è andato sotto un periodo di deprecazione, che non è troppo sorprendente dato che mantenere la retrocompatibilità qui sarebbe stato davvero difficile. Più sorprendente è che non è stato menzionato il problema nello 1.8 release notes, poiché la correzione modifica il comportamento del codice attualmente funzionante.
Il resto di questa risposta è una descrizione di come ho trovato il commit utilizzando git bisect run
. È qui per il mio riferimento più di ogni altra cosa, quindi posso tornare qui se ho bisogno di rielaborare nuovamente un grande progetto.
Per prima cosa installiamo un clone di django e un progetto di test per riprodurre il problema. Ho usato virtualenvwrapper qui, ma puoi isolare come preferisci.
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
creare il seguente file:
# /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)
anche questo:
# /tmp/django/djbisect/djbisect/mysettings.py
from djbisect.settings import *
INSTALLED_APPS += ('djbisect',)
Ora abbiamo un progetto di lavoro, creare la test_script.py
da usare con git bisect run
:
#!/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)
Lo script deve essere eseguibile, quindi aggiungere il flag con chmod +x test_script.py
. Dovrebbe trovarsi nella directory in cui è stato clonato Django, ovvero /tmp/django/test_script.py
per me. Questo perché import django
dovrebbe prima prelevare il progetto django verificato a livello locale, non tutte le versioni dai pacchetti del sito.
L'interfaccia utente di bisect git è stato progettato per scoprire dove i bug apparsi, in modo che i consueti prefissi di "cattivo" e "buono" sono indietro quando si sta cercando di scoprire quando un certo bug era fisso. Questo potrebbe sembrare un po 'sottosopra, ma lo script di test dovrebbe uscire con successo (codice di ritorno 0) se il bug è presente, e dovrebbe fallire (con codice di ritorno diverso da zero) se il bug è corretto. Questo mi ha fatto inciampare alcune volte!
git bisect start --term-new=fixed --term-old=unfixed
git bisect fixed tags/1.8
git bisect unfixed tags/1.7
git bisect run ./test_script.py
Quindi questo processo farà una ricerca automatica, che alla fine trova il commit in cui il bug è stato risolto. Ci vuole un po 'di tempo, perché c'erano un sacco di commit tra Django 1.7 e Django 1.8. E 'diviso in due 1362 revisioni, circa 10 gradini, e alla fine di uscita:
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.
Questo è precisamente il commit in cui la query è cambiato da SQL non corretta (che ottiene i dati dalla tabella sbagliata)
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 la versione corretta:
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))
Naturalmente, dalla hash commettere siamo in grado di trovare la richiesta di pull e il biglietto facilmente su GitHub. Speriamo che questo possa aiutare qualcun altro un giorno anche - biseggere Django può essere complicato da configurare a causa delle migrazioni!
* nota: * La versione di Django è '(1, 7, 11, 'finale', 0)'. Non riesco a riprodurre questo in Django 1.8. – wim
Potrebbe significare che è un ma in Django 1.7 che hanno deciso di risolvere per 1.8? – mgilson
Possibile, ma ho cercato in alto e in basso per la menzione nelle note di rilascio e non riuscivo a trovarlo. Suppongo che 'git bisect' possa trovarlo ... – wim