6

मैं पोस्टग्रेएसक्यूएल 9.2.14 के साथ Django v1.9.4 का उपयोग कर रहा हूं। निम्नलिखित मॉडल के साथ:Django में जेनेरिक ForeignKey कैसे पार करें?

from django.db import models 
from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey 
from django.contrib.contenttypes.models import ContentType 

class Foo(models.Model): 
    content_type = models.ForeignKey(ContentType) 
    object_id = models.PositiveIntegerField() 
    bar = GenericForeignKey('content_type', 'object_id') 

class Bar(models.Model): 
    foos = GenericRelation(Foo, related_query_name='bars') 
    class Meta: 
     abstract = True 

class BarX(Bar): 
    name = models.CharField(max_length=10, default='bar x') 

class BarY(Bar): 
    name = models.CharField(max_length=10, default='bar y') 

मेरी समस्या प्रदर्शित करने के लिए कुछ उदाहरणों बनाएँ:

>>> bar_x = BarX.objects.create() 
>>> bar_y = BarY.objects.create() 
>>> foo1 = Foo.objects.create(bar=bar_x) 
>>> foo2 = Foo.objects.create(bar=bar_y) 
>>> foo1.bar.name 
u'bar x' 
>>> foo2.bar.name 
u'bar y' 

मैं Django में GFK पार नहीं कर सकते, फिल्टर करने के प्रयास में संदेश जोड़ने के लिए सुझाव के साथ एक अपवाद को जन्म देती है GenericRelation। लेकिन सामान्य प्रश्न का उपयोग, संबंधित क्वेरी नाम bars के माध्यम से, भरोसेमंद काम नहीं कर रहा है। उदाहरण के लिए:

>>> [foo.bar.name for foo in Foo.objects.all()] 
[u'bar x', u'bar y'] # in a pure python loop, it's working 
>>> Foo.objects.filter(bar__name='bar x') 
FieldError: Field 'bar' does not generate an automatic reverse relation and therefore cannot be used for reverse querying. If it is a GenericForeignKey, consider adding a GenericRelation. 
>>> Foo.objects.values_list('bars__name', flat=1) 
[None, u'bar y'] # but why None is returned in here? 
>>> Foo.objects.filter(bars__name='bar x') 
[] # why no result here? 
>>> Foo.objects.filter(bars__name='bar y') 
[<Foo: Foo object>] # but this one works? 

मैं क्या गलत कर रहा हूं?


चेतावनी भविष्य पाठकों के लिए ध्यान दें: Templating related_query_nameGenericRelation पर Django 1.9 पर ठीक से काम नहीं करता।

#25354 के लिए fix के बाद, Django 1.10 related_query_name now supports app label and class interpolation using the '%(app_label)s' and '%(class)s' strings में जोड़ा गया था।

यदि आप Django 1.10 पर हैं, तो आप आगे बढ़ सकते हैं और GenericRelation को सार बेस क्लास पर रख सकते हैं और इसे related_query_name='%(app_label)s_%(class)s' जैसे सबक्लास में विशिष्टता सुनिश्चित करने के लिए टेम्पलेट डाल सकते हैं।

उत्तर

9

सामान्य रूप से, इस दिशा में GenericForeignKey को जिस तरह से आप कोशिश कर रहे हैं उसमें पार करना संभव नहीं है। GenericForeignKey आपके आवेदन में किसी भी मॉडल को इंगित कर सकता है, न केवल Bar और इसके उप-वर्ग। इसी कारण से, Foo.objects.filter(bar__somefield='some value') इस समय आपके पास क्या लक्षित मॉडल नहीं है, और इसलिए यह कहना असंभव है कि मॉडल को लक्षित करने वाले फ़ील्ड क्या हैं। वास्तव में, ऐसी क्वेरी करने के दौरान डेटाबेस तालिका को शामिल करने का कोई तरीका नहीं है - यह Foo.content_type के मान के आधार पर कोई भी तालिका हो सकती है।

यदि आप जुड़ने में सामान्य संबंध का उपयोग करना चाहते हैं, तो आपको उस रिश्ते के दूसरे छोर पर GenericRelation परिभाषित करना होगा। इस तरह आप Django को यह जान सकते हैं कि यह किस मॉडल को दूसरी तरफ देखना है। आप

Foo.objects.filter(bar_x__name='bar x') 
Foo.objects.filter(bar_y__name='bar y') 

हालांकि,:

उदाहरण के लिए, यदि आप अपने BarX और BarY मॉडल इस तरह बना सकते हैं:

class BarX(Bar): 
    name = models.CharField(max_length=10, default='bar x') 
    foos = GenericRelation(Foo, related_query_name='bar_x') 

class BarY(Bar): 
    name = models.CharField(max_length=10, default='bar y') 
    foos = GenericRelation(Foo, related_query_name='bar_y') 

आप ऐसा करते हैं, तो आप प्रश्नों निम्नलिखित की तरह प्रदर्शन कर सकते हैं एक लक्ष्य मॉडल चुनने के लिए। यह एक सीमा है कि आप वास्तव में किसी भी तरह से पार नहीं कर सकते हैं; प्रत्येक डेटाबेस में शामिल होने की आवश्यकता है कि पहले से कौन सी टेबल चालू होती है।

आप पूरी तरह BarX दोनों और BarY लक्ष्य के रूप में अनुमति देने के लिए की जरूरत है, तो आप एक Q अभिव्यक्ति का उपयोग करते हुए स्पष्ट रूप से आपकी क्वेरी फिल्टर में उन दोनों को सूचीबद्ध करने के लिए सक्षम होना चाहिए:

Foo.objects.filter(Q(bar_x__name='bar x') | Q(bar_y__name='bar y')) 
+0

ठीक तो यह एक हो रहा है एसक्यूएल में शामिल होने के कारण सीमा। लेकिन मेरे प्रश्न के उदाहरणों के लिए, डीजेंगो ने प्रश्नों को क्यों अनुमति दी? गलत परिणामों को वापस करने के बजाय, इसे अपवाद नहीं उठाया जाना चाहिए? – wim

+0

हाँ, यह एक बग जैसा दिखता है, मुझे नहीं लगता कि यह आपको ऐसे प्रश्न करने की अनुमति दे सकता है। क्या आप शायद उन प्रश्नों से उत्पन्न प्रश्न को अपने प्रश्न में जोड़ सकते हैं? – koniiiik

+1

मैंने आपके समाधान के साथ थोड़ा सा खेला और एक और दिलचस्प बात पाई, एक अद्वितीय 'related_query_name' के साथ प्रत्येक सबक्लास पर 'जेनेरिकरेलेशन' फ़ील्ड रखना आवश्यक नहीं हो सकता है। विशिष्टता सुनिश्चित करने के लिए आप इसे मूल श्रेणी और टेम्पलेट पर 'related_query_name = '% (app_label) s _% (class) s'' जैसे छोड़ सकते हैं। – wim

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