مشكلة تتعلق بعدم تحديث علاقات ManyToMany مباشرة بعد الحفظ

StackOverflow https://stackoverflow.com/questions/1925383

سؤال

أواجه مشكلات مع علاقات ManyTomany التي لا يتم تحديثها في نموذج عندما أقوم بحفظه (عبر المسؤول) ومحاولة استخدام القيمة الجديدة ضمن وظيفة متصلة ب post_save إشارة أو داخل save_model من المرتبطة AdminModel.لقد حاولت إعادة تحميل الكائن ضمن هذه الوظائف باستخدام وظيفة GET مع المعرف ..ولكن لا يزال لديه القيم القديمة.

هل هذه مسألة معاملة؟هل هناك إشارة يتم إلقاؤها عندما تنتهي المعاملة؟

شكرًا،

هل كانت مفيدة؟

المحلول

وعند حفظ نموذج عبر أشكال المشرف انها ليست صفقة نووية. يحصل حفظ الهدف الرئيسي الأول (للتأكد من أن لديها PK)، ثم M2M هو على مسح و القيم الجديدة المقرر أن كل ما خرج من النموذج. حتى إذا كنت في إنقاذي () الكائن الرئيسي كنت في نافذة الفرصة التي لم يتم تحديث M2M حتى الان. في الواقع، إذا كنت <م> محاولة أن تفعل شيئا لM2M، وسوف تحصل على محو التغيير بها واضحة (). جريت إلى هذا قبل نحو سنة.

لقد تغيير رمز إلى حد ما من أيام ريفاكتور قبل ORM، ولكنها تتلخص في التعليمات البرمجية في django.db.models.fields.ManyRelatedObjectsDescriptor وReverseManyRelatedObjectsDescriptor. انظروا __set بها __) أساليب (وسترى manager.clear(); manager.add(*value) ذلك واضحة () ينظف كاملة من أية مراجع M2M عن الهدف الرئيسي الحالي في هذا الجدول. الوظيفة () ثم تعيين قيم جديدة.

ومن أجل الإجابة على سؤالك: نعم، هذا هو موضوع الصفقة

هل هناك إشارة القيت عند انتهاء الصفقة؟ اي شيء رسمي، ولكن قراءة على:

وكان هناك موضوع ذي صلة قبل بضعة أشهر و MonkeyPatching كان أسلوب واحد المقترح. شارك غريغوار على MonkeyPatch للحصول على هذا. لم أحاول ذلك، ولكن يبدو أنه يجب أن تعمل.

نصائح أخرى

وعندما تحاول الوصول إلى الحقول ManyToMany في إشارة post_save النموذج، وقد تم بالفعل إزالة الكائنات ذات الصلة، ولن يتم إضافة مرة أخرى حتى بعد انتهاء الإشارة.

لوصول إلى هذه البيانات لديك لادراك التعادل، في طريقة save_related في ModelAdmin الخاص بك. للأسف سيكون لديك أيضا لتشمل التعليمات البرمجية في إشارة post_save لطلبات غير المسؤول التي تتطلب التخصيص.

ويرى: الشبكي: //docs.djangoproject.com/en/1.7/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_related

مثال:

# 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)

وبعد ذلك في إشارات الخاص بك يمكنك جعل نفس التغييرات التي تريدها لتنفيذ على حفظ:

# 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: []

ولدي حل عام لهذا يبدو قليلا أكثر نظافة من جوهر أو حتى باستخدام الكرفس (على الرغم من أنني متأكد من شخص يمكن أن تجد المجالات التي فشل)، الترقيع قرد. أنا في الأساس إضافة أسلوب نظيفة () في المشرف للنموذج الذي يحتوي على العلاقات M2M، وتعيين العلاقات سبيل المثال إلى إصدار cleaned_data. هذا جعل البيانات الصحيحة المتوفرة إلى مثيل ننقذ طريقة، على الرغم من انها ليست "على الكتب" حتى الان. انها محاولة ونرى كيف ستسير الامور:

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])

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

المشكلة: عند التعامل مع M2M نموذج في وظيفة أو استقبال إشارة pre_save، والحصول على محو التغييرات عليها في "تطهير" لاحقة من M2M التي كتبها جانغو.

والحل: في التي تنشرها أو pre_save معالج إشارة، سجل معالج آخر للإشارة m2m_changed على M2M نموذج سيط النموذج الذي تريد تحديث M2M.

يرجى ملاحظة أن هذا المعالج الثاني سيحصل عدة إشارات m2m_changed، وأنه هو المفتاح لاختبار لقيمة الحجج 'العمل' مرت معهم.

وفي هذا المعالج الثاني، تحقق للعمل "post_clear. عندما تتلقى إشارة مع العمل post_clear، تم مسح M2M التي كتبها جانغو ولديك فرصة لمعالجته بنجاح.

مثال:

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)

https://docs.djangoproject.com/en/ 1.5 / المرجع / الإشارات / #-غيرت M2M

ويمكنك العثور على مزيد من المعلومات في هذا الموضوع: جانغو manytomany إشارات

أحد الحلول لتحديث m2m مع تحديث أحد الموديلات الخاصة بك.

Django 1.11 and higher

أولًا، جميع الطلبات عبر لوحة الإدارة هي طلبات ذرية.يمكنك إلقاء نظرة على ModelAdmin:

@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)

السلوك الذي يمكنك ملاحظته أثناء التحديث، عندما لا يتم حفظ التغييرات التي أجريتها باستخدام سجلات m2m، حتى بعد إجرائها بطريقة حفظ أحد نماذجك أو في إشارة، يحدث فقط لأن نموذج m2m يعيد كتابة كافة السجلات بعد الأمر الرئيسي يتم تحديث الكائن.

ولهذا السبب، خطوة بخطوة:

  1. يتم تحديث الكائن الرئيسي.

  2. تم إجراء تغييرات على الكود الخاص بك (في طريقة حفظ أو في إشارة) (يمكنك الاطلاع عليها، فقط ضع نقطة توقف في 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() جميع قيم m2m التي تم وضعها على الصفحة (تقريبًا) ويستبدل جميع سجلات m2m عبر مدير ذي صلة.ولهذا السبب لا يمكنك رؤية تغييراتك في نهاية المعاملة.

هل هناك حل:قم بإجراء التغييرات باستخدام m2m عبر transaction.on_commit.سوف تقوم المعاملة.

لسوء الحظ، الجانب السلبي لهذا الحل - سيتم تنفيذ تغييراتك مع m2m في معاملة منفصلة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top