Avendo avuto questo problema e ha trovato due soluzioni definitive per questo ho pensato che vale la pena inviare un'altra risposta.
Questo è un problema con la modalità di transazione predefinita di MySQL. Django apre una transazione all'inizio, il che significa che per impostazione predefinita non verranno visualizzate le modifiche apportate al database.
Dimostrare come questo
eseguire una shell Django nel terminal 1
>>> MyModel.objects.get(id=1).my_field
u'old'
e un altro nel terminale 2
>>> MyModel.objects.get(id=1).my_field
u'old'
>>> a = MyModel.objects.get(id=1)
>>> a.my_field = "NEW"
>>> a.save()
>>> MyModel.objects.get(id=1).my_field
u'NEW'
>>>
Torna al morsetto 1 per dimostrare il problema - abbiamo ancora letto la vecchio valore dal database.
>>> MyModel.objects.get(id=1).my_field
u'old'
Ora nel terminal 1 dimostrano la soluzione
>>> from django.db import transaction
>>>
>>> @transaction.commit_manually
... def flush_transaction():
... transaction.commit()
...
>>> MyModel.objects.get(id=1).my_field
u'old'
>>> flush_transaction()
>>> MyModel.objects.get(id=1).my_field
u'NEW'
>>>
I nuovi dati ora vengono letti
qui è che il codice in un facile da incollare blocco con docstring
from django.db import transaction
@transaction.commit_manually
def flush_transaction():
"""
Flush the current transaction so we don't read stale data
Use in long running processes to make sure fresh data is read from
the database. This is a problem with MySQL and the default
transaction mode. You can fix it by setting
"transaction-isolation = READ-COMMITTED" in my.cnf or by calling
this function at the appropriate moment
"""
transaction.commit()
La soluzione alternativa è cambiare my.cnf per MySQL per cambiare la modalità di transazione predefinita
transaction-isolation = READ-COMMITTED
Si noti che questa è una funzionalità relativamente nuova per Mysql e ha some consequences for binary logging/slaving. Puoi anche inserire questo nel preambolo della connessione django se lo desideri.
Update 3 anni dopo
Ora che Django 1.6 ha turned on autocommit in MySQL questo non è più un problema. L'esempio sopra ora funziona bene senza il codice flush_transaction()
se il tuo MySQL è in REPEATABLE-READ
(impostazione predefinita) o nella modalità di isolamento della transazione READ-COMMITTED
.
Quello che stava succedendo nelle versioni precedenti di Django che veniva eseguito in modalità non autocommit era che la prima istruzione select
apriva una transazione. Poiché la modalità predefinita di MySQL è REPEATABLE-READ
, ciò significa che nessun aggiornamento al database verrà letto dalle successive istruzioni select
: da qui la necessità del codice flush_transaction()
sopra il quale si interrompe la transazione e ne inizia una nuova.
Ci sono ancora dei motivi per cui si potrebbe voler usare l'isolamento della transazione READ-COMMITTED
. Se si dovesse inserire il terminale 1 in una transazione e si desidera visualizzare le scritture dal terminale 2, sarà necessario READ-COMMITTED
.
Il codice flush_transaction()
produce ora un avviso di ritiro in Django 1.6, quindi consiglio di rimuoverlo.
Sembra una soluzione fantastica e facile, ma almeno nella mia versione di Django non funziona. Chiamando MyModel.objects._clone() si ottiene un errore "AttributeError: 'Manager' non ha attributo '_clone'". Posso fare MyModel.objects.all() ._ clone(), ma funziona come prima - non cambia finché non chiamo update(). Sto usando Django 1.2.1. – scippy
Il mio male - dovrebbe essere 'MyModel.objects.all() ._ clone()'. Nel pensarci, si può fare a meno di fare un 'MyModel.objects.all(). Count()' senza '_clone()'. Questo crea una nuova versione dell'oggetto base e dovrebbe darti una nuova versione senza il valore memorizzato nella cache. Cioè, a meno che Django non stia facendo qualcosa di subdolo e portando lo stato con il clone. –
Questa risposta è sbagliata. Chiamare qualsiasi metodo (come 'count()') su un manager clona implicitamente un nuovo queryset, non c'è alcun comportamento di caching implicito dovuto all'identità di Manager e non è necessario inserire una chiamata esterna a '_clone()' o 'all() '. L'intera catena di pensiero è una falsa pista, il vero problema dell'OP è l'isolamento delle transazioni a livello di database, non ha nulla a che fare con i querysets o il caching di livello Django. –