2011-10-17 18 views
33

मैं Django में कुछ मॉडल विरासत स्तर है:सिग्नल के साथ Django मॉडल विरासत का उपयोग कैसे करें?

class WorkAttachment(models.Model): 
    """ Abstract class that holds all fields that are required in each attachment """ 
    work   = models.ForeignKey(Work) 
    added   = models.DateTimeField(default=datetime.datetime.now) 
    views   = models.IntegerField(default=0) 

    class Meta: 
     abstract = True 


class WorkAttachmentFileBased(WorkAttachment): 
    """ Another base class, but for file based attachments """ 
    description  = models.CharField(max_length=500, blank=True) 
    size   = models.IntegerField(verbose_name=_('size in bytes')) 

    class Meta: 
     abstract = True 


class WorkAttachmentPicture(WorkAttachmentFileBased): 
    """ Picture attached to work """ 
    image   = models.ImageField(upload_to='works/images', width_field='width', height_field='height') 
    width   = models.IntegerField() 
    height   = models.IntegerField() 

कई विभिन्न मॉडलों WorkAttachmentFileBased और WorkAttachment से विरासत में मिले हैं। मैं एक सिग्नल बनाना चाहता हूं, जो अनुलग्नक बनने पर माता-पिता के काम के लिए attachment_count फ़ील्ड अपडेट करेगा। यह तर्कसंगत होगा कि यह सोचने के लिए कि मूल प्रेषक (WorkAttachment) के लिए किए गए सिग्नल सभी विरासत वाले मॉडल के लिए भी चलेंगे, लेकिन ऐसा नहीं है।

@receiver(post_save, sender=WorkAttachment, dispatch_uid="att_post_save") 
def update_attachment_count_on_save(sender, instance, **kwargs): 
    """ Update file count for work when attachment was saved.""" 
    instance.work.attachment_count += 1 
    instance.work.save() 

है वहाँ WorkAttachment से विरासत में मिली सभी मॉडलों के लिए यह संकेत काम करने के लिए एक तरह से: यहाँ मेरी कोड है?

अजगर 2.7, Django 1.4 पूर्व अल्फा

पी.एस. मैंने one of the solutions I found on the net की कोशिश की है, लेकिन यह मेरे लिए काम नहीं करता है।

model_classes = [WorkAttachment, WorkAttachmentFileBased, WorkAttachmentPicture, ...] 

def update_attachment_count_on_save(sender, instance, **kwargs): 
    instance.work.attachment_count += 1 
    instance.work.save() 

for model_class in model_classes: 
    post_save.connect(update_attachment_count_on_save, 
         sender=model_class, 
         dispatch_uid="att_post_save_"+model_class.__name__) 

: - यह सोचते हैं आप आधार वर्ग और उपवर्गों है (अस्वीकरण: मैं ऊपर का परीक्षण नहीं किया)

+2

समाधान लिंक – Martin

+3

मैं [वेब अभिलेखागार में समाधान पेज] (http पाया है टूट गया है। archive.org/web/20120715042306/http://codeblogging.net/blogs/1/14)। समाधान की एक कमी है - आपको सभी उप-वर्गों के बाद संकेत घोषित करना चाहिए, अन्यथा यह उन्हें नहीं मिलेगा। – HighCat

उत्तर

17

आप की तरह कुछ की कोशिश कर सकते एक ही ऐप में पैक किया गया। कुछ इस तरह काम करेगा:

from django.contrib.contenttypes.models import ContentType 
content_types = ContentType.objects.filter(app_label="your_app") 
for content_type in content_types: 
    model = content_type.model_class() 
    post_save.connect(update_attachment_count_on_save, sender=model) 
+4

आपके उत्तर के लिए धन्यवाद। यह मुझे समाधान के लिए प्रेरित करता है, जिसे मैंने यहां वर्णित किया है: http://codeblogging.net/blogs/1/14/ –

+0

खुशी है कि मैं मदद कर सकता हूं, अच्छा ब्लॉग पोस्ट। – codeape

+2

बस FYI @SilverLight द्वारा ब्लॉग पोस्ट अब एक वैध लिंक नहीं है। – Fitblip

0

यह भी उपवर्गों की खोज के लिए सामग्री प्रकार उपयोग करना संभव है

39

आप sender निर्दिष्ट बिना कनेक्शन प्रबंधक रजिस्टर कर सकते हैं। और इसके अंदर आवश्यक मॉडल फ़िल्टर करें।

from django.db.models.signals import post_save 
from django.dispatch import receiver 


@receiver(post_save) 
def my_handler(sender, **kwargs): 
    # Returns false if 'sender' is NOT a subclass of AbstractModel 
    if not issubclass(sender, AbstractModel): 
     return 
    ... 

रेफरी: https://groups.google.com/d/msg/django-users/E_u9pHIkiI0/YgzA1p8XaSMJ

+0

यह एक अच्छा समाधान है। – Mikle

+5

यह काम करता है लेकिन इसमें थोड़ी कमी है कि रिसीवर फ़ंक्शन को प्रत्येक मॉडल के लिए बुलाया जाता है जो 'save() 'कहता है। – dhobbs

+1

हां, ऐसा लगता है जैसे यह एक महत्वपूर्ण प्रदर्शन ओवरहेड होगा। –

2

यह समाधान समस्या है जब नहीं सभी मॉड्यूल स्मृति में आयात हल करता है।

def inherited_receiver(signal, sender, **kwargs): 
    """ 
    Decorator connect receivers and all receiver's subclasses to signals. 

     @inherited_receiver(post_save, sender=MyModel) 
     def signal_receiver(sender, **kwargs): 
      ... 

    """ 
    parent_cls = sender 

    def wrapper(func): 
     def childs_receiver(sender, **kw): 
      """ 
      the receiver detect that func will execute for child 
      (and same parent) classes only. 
      """ 
      child_cls = sender 
      if issubclass(child_cls, parent_cls): 
       func(sender=child_cls, **kw) 

     signal.connect(childs_receiver, **kwargs) 
     return childs_receiver 
    return wrapper 
+0

यह http://stackoverflow.com/a/17173716/433570 पर और दोषों के बिना बनाया गया है जो Silverlight ने स्वीकृत उत्तर में उल्लेख किया है। – eugene

6
post_save.connect(my_handler, ParentClass) 
# connect all subclasses of base content item too 
for subclass in ParentClass.__subclasses__(): 
    post_save.connect(my_handler, subclass) 

आपका दिन शुभ हो!

@receiver(post_save) 
def update_attachment_count_on_save(sender, instance, **kwargs): 
    if isinstance(instance, WorkAttachment): 
     ... 

हालांकि, इस हर के रूप में एक महत्वपूर्ण प्रदर्शन भूमि के ऊपर उठाना हो सकता है:

+1

यह सबसे अच्छा जवाब है। –

+0

आपको यह सुनिश्चित करने की ज़रूरत है कि यह संभव है कि सभी संभव सबक्लास परिभाषित किए जाने के बाद यह चलाया जा सके या अन्यथा वे छोड़ जाएंगे (हालांकि, मैंने उस दावे का परीक्षण नहीं किया है, मुझे लगता है कि यह होगा कि क्या होगा)। –

19

सरल समाधान है कि क्या संबंधित उदाहरण एक उपवर्ग है sender पर सीमित नहीं है, लेकिन संकेत हैंडलर में जांच करने के लिए है समय कोई मॉडल सहेजा गया है, उपरोक्त फ़ंक्शन को कॉल किया जाता है।

मुझे लगता है कि मुझे ऐसा करने का सबसे Django-तरीका मिला है: Django के हाल के संस्करण signals.py नामक फ़ाइल में सिग्नल हैंडलर को जोड़ने का सुझाव देते हैं।

your_app/__ init__.py:

default_app_config = 'your_app.apps.YourAppConfig' 

your_app/क्षुधा यहाँ आवश्यक तारों कोड है।py:

import django.apps 

class YourAppConfig(django.apps.AppConfig): 
    name = 'your_app' 
    def ready(self): 
     import your_app.signals 

your_app/signals.py:

def get_subclasses(cls): 
    result = [cls] 
    classes_to_inspect = [cls] 
    while classes_to_inspect: 
     class_to_inspect = classes_to_inspect.pop() 
     for subclass in class_to_inspect.__subclasses__(): 
      if subclass not in result: 
       result.append(subclass) 
       classes_to_inspect.append(subclass) 
    return result 

def update_attachment_count_on_save(sender, instance, **kwargs): 
    instance.work.attachment_count += 1 
    instance.work.save() 

for subclass in get_subclasses(WorkAttachment): 
    post_save.connect(update_attachment_count_on_save, subclass) 

मुझे लगता है कि इस, सभी उपवर्गों के लिए काम करता है क्योंकि वे हर समय YourAppConfig.ready कहा जाता है के द्वारा लोड किया जाएगा (और इस प्रकार signals आयात किया गया है)।

+0

यह शीर्ष उत्तर – spg

+0

अच्छा उत्तर होना चाहिए। ध्यान दें कि 'get_subclassed''' में' परिणाम''' में मूल वर्ग शामिल है, जो इस प्रश्न से मेल खाता है। यदि आपका मूल वर्ग एक अमूर्त मॉडल है, तो आप शुरुआत में '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' 'परिणाम चाहते हैं – biodiv

3

माइकल हेरमैन का समाधान निश्चित रूप से ऐसा करने का सबसे Django-तरीका है। और हाँ यह सभी उप-वर्गों के लिए काम करता है क्योंकि वे तैयार() कॉल पर लोड होते हैं।

मैं प्रलेखन संदर्भ के साथ योगदान करना चाहते हैं:

अभ्यास में, संकेत संचालकों आमतौर पर आवेदन वे से संबंधित हैं की एक संकेत submodule में परिभाषित कर रहे हैं। सिग्नल रिसीवर आपके एप्लिकेशन कॉन्फ़िगरेशन क्लास की तैयार() विधि में जुड़े हुए हैं। यदि आप रिसीवर() सजावट का उपयोग कर रहे हैं, तो बस तैयार() के अंदर सिग्नल सबमिशन आयात करें।

https://docs.djangoproject.com/en/dev/topics/signals/#connecting-receiver-functions

और एक चेतावनी जोड़ें:

तैयार() विधि के परीक्षण के दौरान एक से अधिक बार निष्पादित किया जा सकता है, तो आप दोहराव से अपने संकेतों की रक्षा के लिए चाहते हो सकता है, विशेष रूप से आप अगर परीक्षण के भीतर उन्हें भेजने की योजना बना रहे हैं।

https://docs.djangoproject.com/en/dev/topics/signals/#connecting-receiver-functions

तो तुम कनेक्ट समारोह पर एक dispatch_uid पैरामीटर के साथ डुप्लिकेट संकेतों को रोकने के लिए चाहते हो सकता है।

post_save.connect(my_callback, dispatch_uid="my_unique_identifier") 

इस संदर्भ में मैं करूँगा: // वेब:

for subclass in get_subclasses(WorkAttachment): 
    post_save.connect(update_attachment_count_on_save, subclass, dispatch_uid=subclass.__name__) 

https://docs.djangoproject.com/en/dev/topics/signals/#preventing-duplicate-signals

संबंधित मुद्दे