Frage

Ich habe Probleme mit ManyToMany Beziehungen, die nicht aktualisiert werden in einem Modell, wenn ich speichere (über das Admin) und versuchen Sie den neuen Wert in einem verwenden Funktion an das Signal post_save oder innerhalb des von save_model die zugehörige AdminModel. Ich habe versucht, das Objekt innerhalb dieser Funktionen nachzuladen durch die Verwendung von erhalten Funktion mit der id .. aber es hat immer noch die alten Werte.

Ist das eine Transaktion Problem? Gibt es ein Signal ausgelöst wird, wenn die Transaktion endet?

Danke,

War es hilfreich?

Lösung

Wenn Sie ein Modell über Admin-Formulare speichern ist es nicht eine atomare Transaktion. Das Hauptziel gespeichert wird zuerst (um sicherzustellen, hat es eine PK), dann ist die M2M ist gelöscht und die neuen Werte auf, was auch immer kamen die Form heraus. Also, wenn Sie in der save () des Hauptobjekts Sie in einem Fenster der Gelegenheit sind, wo der M2M wurde noch nicht aktualisiert hat. In der Tat, wenn Sie versuchen etwas zu dem M2M zu tun, bekommt die Änderung durch die klare weggewischt (). Ich lief in das vor etwa einem Jahr.

Der Code hat sich etwas von den Pre-ORM refactor Tage gewechselt, aber es läuft darauf hinaus, Code in django.db.models.fields.ManyRelatedObjectsDescriptor und ReverseManyRelatedObjectsDescriptor nach unten. Schauen Sie sich ihre __set __ () Methoden und Sie werden manager.clear(); manager.add(*value) Das clear () komplett säubert alle M2M Referenzen für die aktuelle Hauptaufgabe in dieser Tabelle sehen. Die add () setzt dann die neuen Werte.

So Ihre Frage zu beantworten:. Ja, das ist eine Transaktion Ausgabe

Gibt es ein Signal ausgelöst wird, wenn die Transaktion endet? Nichts offizielles, aber lesen Sie weiter:

Es war ein verwandter Thread vor ein paar Monate und MonkeyPatching wurde ein Verfahren vorgeschlagen. Grégoire hat einen MonkeyPatch dafür. Ich habe es nicht ausprobiert, aber es sieht aus wie es funktionieren soll.

Andere Tipps

Wenn Sie versuchen, die ManyToMany Felder im post_save Signal des Modells zugreifen zu können, haben die entsprechenden Objekte bereits entfernt worden und werden erst wieder aufgenommen werden, nachdem das Signal beendet ist.

Um auf diese Daten zugreifen, müssen Sie im save_related Methode in der Modeladmin binden. Leider werden Sie auch den Code in dem post_save Signal für Nicht-Admin-Anfragen umfassen müssen, die individuell gestaltet werden muß.

siehe: https: //docs.djangoproject.com/en/1.7/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_related

Beispiel:

# admin.py
Class GroupAdmin(admin.ModelAdmin):
    ...
    def save_related(self, request, form, formsets, change):
        super(GroupAdmin, self).save_related(request, form, formsets, change)
        # do something with the manytomany data from the admin
        form.instance.users.add(some_user)

Dann in Ihre Signale können Sie die gleichen Änderungen vornehmen, dass Sie auf einem speichern ausgeführt werden soll:

# signals.py
@receiver(post_save, sender=Group)
def group_post_save(sender, instance, created, **kwargs):
    # do somethign with the manytomany data from non-admin
    instance.users.add(some_user)
    # note that instance.users.all() will be empty from the admin: []

Ich habe eine allgemeine Lösung für dieses Problem, die ein bisschen sauberer als Affe-Patchen des Kerns oder sogar mit Sellerie scheint (obwohl ich sicher, dass jemand bin Bereiche könnte finden, wo es nicht). Im Grunde ich einen sauberen () -Methode in der Admin für das Formular hinzufügen, die die m2m Beziehungen haben, und legen Sie die Instanz Beziehungen zur cleaned_data Version. Dies macht die richtigen Daten zur Verfügung der Instanz speichert Methode ist, auch wenn es „auf den Büchern“ noch nicht ist. Versuchen Sie es und sehen, wie es geht:

def clean(self, *args, **kwargs):
    # ... actual cleaning here
    # then find the m2m fields and copy from cleaned_data to the instance
    for f in self.instance._meta.get_all_field_names():
        if f in self.cleaned_data:
            field = self.instance._meta.get_field_by_name(f)[0]
            if isinstance(field, ManyToManyField):
                setattr(self.instance,f,self.cleaned_data[f])

Siehe http: / /gterzian.github.io/Django-Cookbook/signals/2013/09/07/manipulating-m2m-with-signals.html

Problem: Wenn Sie die m2m eines Modells in einem Beitrag oder pre_save Signalempfänger manipulieren, erhalten Sie Ihre Änderungen in der anschließenden ‚Clearing‘ weggewischt der m2m von Django.

Lösung: In Sie pre_save Signal-Handler, registrieren Sie einen anderen Handler zum m2m_changed Signal auf dem M2M-Vermittler Modell des Modells, dessen m2m möchten Sie Update veröffentlichen oder.

Bitte beachten Sie, dass dieser zweite Handler mehr m2m_changed Signale empfangen wird, und es ist der Schlüssel zum Test für den Wert der ‚Aktion‘ Argumente übergaben zusammen mit ihnen.

In diesem zweiten Handler, überprüfen Sie die ‚post_clear‘ Aktion. Wenn Sie ein Signal mit der post_clear Aktion zu erhalten, hat die m2m von Django gelöscht wurde und Sie haben eine Chance, es zu erfolgreich zu manipulieren.

ein Beispiel:

def save_handler(sender, instance, *args, **kwargs):
    m2m_changed.connect(m2m_handler, sender=sender.m2mfield.through, weak=False)


def m2m_handler(sender, instance, action, *args, **kwargs):
    if action =='post_clear':
        succesfully_manipulate_m2m(instance)


pre_save.connect(save_handler, sender=YouModel, weak=False)

finden Sie unter https://docs.djangoproject.com/en/ 1.5 / ref / Signale / # m2m-changed

Sie können weitere Informationen zu diesem Thema finden: Django ManyToMany Signale

Eine der Lösungen zur Aktualisierung m2m, zusammen mit einem Ihrer Modelle zu aktualisieren.

Django 1.11 and higher

Zunächst einmal alle Anfragen über Admin-Panel sind atomar. Sie können Modeladmin aussehen:

@csrf_protect_m
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
    with transaction.atomic(using=router.db_for_write(self.model)):
        return self._changeform_view(request, object_id, form_url, extra_context)

@csrf_protect_m
def delete_view(self, request, object_id, extra_context=None):
    with transaction.atomic(using=router.db_for_write(self.model)):
        return self._delete_view(request, object_id, extra_context)

Das Verhalten, das Sie bei der Aktualisierung beobachten können, wenn Änderungen, die Sie mit m2m Aufzeichnungen gemacht wurden nicht gespeichert, auch nachdem Sie sie in einem Speichermethode eines Ihrer Modelle oder in einem Signal gemacht, geschieht nur, weil m2m Form alle Datensätze neu geschrieben nachdem das Hauptobjekt aktualisiert wird.

Aus diesem Grunde, Schritt für Schritt:

  1. Das Hauptziel aktualisiert wird.

  2. Ihr Code (in einem Speichermethode oder in einem Signal) vorgenommenen Änderungen (man sie anschaut kann, stellen nur einen Haltepunkt in Modeladmin):

 def save_related(self, request, form, formsets, change):
     breakpoint()
     form.save_m2m()
     for formset in formsets:
         self.save_formset(request, form, formset, change=change)
  1. form.save_m2m () nimmt alle m2m Werte, die auf einer Seite platziert wurden (grob gesprochen) und ersetzen Sie alle m2m Aufzeichnungen über einen verbundenen Manager. Deshalb sollten Sie Ihre Änderungen nicht am Ende einer Transaktion sehen.
  

Es gibt eine Lösung: machen Sie Ihre Änderungen mit m2m über   transaction.on_commit. transaction.on_commit Ihre Änderungen vornehmen   nach form.save_m2m (), wenn die Transaktion festgeschrieben wird.

Leider ist der Nachteil dieser Lösung -. Ihre Änderungen mit m2m werden in einer separaten Transaktion ausgeführt werden

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top