2009-12-01 25 views
61

में एक इनलाइन प्रपत्र तर्क मॉडल की है में चुनिंदा में विकल्प है:सीमा विदेशी कुंजी व्यवस्थापक

  • एक Building है कई Rooms
  • एक Room उदाहरण के लिए, एक और Room (एक कोठरी के अंदर हो सकता है --ForeignKey 'स्वयं') पर
  • एक Room केवल एक ही इमारत में एक और Room अंदर हो सकता है (इस मुश्किल हिस्सा है)

यहाँ कोड मैं है:

#spaces/models.py 
from django.db import models  

class Building(models.Model): 
    name=models.CharField(max_length=32) 
    def __unicode__(self): 
     return self.name 

class Room(models.Model): 
    number=models.CharField(max_length=8) 
    building=models.ForeignKey(Building) 
    inside_room=models.ForeignKey('self',blank=True,null=True) 
    def __unicode__(self): 
     return self.number 

और:

#spaces/admin.py 
from ex.spaces.models import Building, Room 
from django.contrib import admin 

class RoomAdmin(admin.ModelAdmin): 
    pass 

class RoomInline(admin.TabularInline): 
    model = Room 
    extra = 2 

class BuildingAdmin(admin.ModelAdmin): 
    inlines=[RoomInline] 

admin.site.register(Building, BuildingAdmin) 
admin.site.register(Room) 

इनलाइन वर्तमान इमारत में केवल कमरे प्रदर्शित करेगा (जो मैं क्या चाहते हैं)। समस्या यह है कि inside_room ड्रॉप डाउन के लिए, यह कमरे की मेज (अन्य इमारतों में शामिल) के सभी कमरों को प्रदर्शित करता है।

rooms की इनलाइन में, मैं जो वर्तमान building (इमारत रिकॉर्ड वर्तमान में मुख्य BuildingAdmin रूप से परिवर्तन किया जा रहा) में हैं केवल rooms को inside_room विकल्पों को सीमित करने की जरूरत है।

मैं मॉडल में limit_choices_to के साथ ऐसा करने का कोई तरीका नहीं समझ सकता, और न ही मैं यह समझ सकता हूं कि व्यवस्थापक के इनलाइन फॉर्मेट को ठीक से कैसे ओवरराइड करना है (मुझे लगता है कि मुझे किसी भी तरह से कस्टम इनलाइन फॉर्म बनाना चाहिए , मुख्य रूप के build_id को कस्टम इनलाइन पर पास करें, फिर उसके आधार पर फ़ील्ड के विकल्पों के लिए क्वेरीसेट को सीमित करें - लेकिन मैं अपने सिर को लपेट नहीं सकता कि यह कैसे करें)।

हो सकता है कि इस व्यवस्थापक साइट के लिए बहुत जटिल है, लेकिन यह कुछ ऐसा है जो आम तौर पर उपयोगी होगा की तरह लगता है ...

उत्तर

2

डैनियल, आपके सवाल का संपादन के बाद, उत्तर नहीं दिया गया है - मुझे नहीं लगता कि मैं बहुत मदद मिलेगी ... :-)

मैं सुझाव दे रहा हूं कि आप django व्यवस्थापक में फिट करने के लिए कुछ तर्क देने की कोशिश कर रहे हैं जो आपके विचारों, रूपों और टेम्पलेट्स के समूह के रूप में लागू होने से बेहतर होगा।

मुझे नहीं लगता कि यह InlineModelAdmin को छानने की इस प्रकार की लागू करने के लिए संभव है।

1

मैं स्वीकार करने के लिए, मैं तुम्हें करने के लिए कोशिश कर रहे हैं कि वास्तव में क्या का पालन नहीं किया है, लेकिन मुझे लगता है कि यह काफी जटिल है कि आप व्यवस्थापक के बंद अपनी साइट आधारित नहीं विचार करना चाह सकते हैं।

मुझे लगता है कि सरल व्यवस्थापक अंतरफलक के साथ बाहर शुरू कर दिया एक बार एक साइट बनाई थी, लेकिन अंत में तो अनुकूलित है कि यह व्यवस्थापक की सीमा के भीतर के साथ काम करने के लिए बहुत मुश्किल हो गया हो गया। अगर मैं अभी शुरूआत से शुरू कर दूंगा तो मैं बेहतर रहा होगा - शुरुआत में अधिक काम, लेकिन अंत में बहुत अधिक लचीलापन और कम दर्द। मेरा नियम-थंब होगा यदि आप जो करने की कोशिश कर रहे हैं वह दस्तावेज नहीं है (यानी इसमें व्यवस्थापक विधियों को ओवरराइड करना, व्यवस्थापक स्रोत कोड आदि में peering करना शामिल है) तो आप व्यवस्थापक का उपयोग न करने से शायद बेहतर हो सकते हैं। बस मुझे दो सेंट। :)

4

This question and answer is very similar, and works for a regular admin form

एक इनलाइन के अंदर

- और है कि जहां यह अलग गिर जाता है ...मैं अपनी सीमा में विदेशी कुंजी मूल्य (या मूल्य को पकड़ने के लिए इनलाइन के रिकॉर्ड में से एक के लिए) प्राप्त करने के लिए मुख्य रूप के डेटा पर नहीं जा सकता हूं।

यहां मेरा व्यवस्थापक.py है। मुझे लगता है कि मैं जादू को बदलने के लिए जादू की तलाश में हूँ ???? साथ - अगर मैं एक हार्डकोडेड मूल्य में प्लग (जैसे कि, 1), यह ठीक काम करता है और ठीक से इनलाइन में उपलब्ध विकल्पों को सीमित करता है ...

#spaces/admin.py 
from demo.spaces.models import Building, Room 
from django.contrib import admin 
from django.forms import ModelForm 


class RoomInlineForm(ModelForm): 
    def __init__(self, *args, **kwargs): 
    super(RoomInlineForm, self).__init__(*args, **kwargs) 
    self.fields['inside_room'].queryset = Room.objects.filter(
           building__exact=????)      # <------ 

class RoomInline(admin.TabularInline): 
    form = RoomInlineForm 
    model=Room 

class BuildingAdmin(admin.ModelAdmin): 
    inlines=[RoomInline] 

admin.site.register(Building, BuildingAdmin) 
admin.site.register(Room) 
4

मैं एक fairly elegant solution कि इनलाइन रूपों के लिए अच्छी तरह से काम करता है पाया।

अपने मॉडल है, जहां मैं inside_room क्षेत्र को छानने कर रहा हूँ केवल उपलब्ध है कि एक ही इमारत में हैं वापस जाने के लिए लागू:

#spaces/admin.py 
class RoomInlineForm(ModelForm): 
    def __init__(self, *args, **kwargs): 
    super(RoomInlineForm, self).__init__(*args, **kwargs) #On init... 
    if 'instance' in kwargs: 
    building = kwargs['instance'].building 
    else: 
    building_id = tuple(i[0] for i in self.fields['building'].widget.choices)[1] 
    building = Building.objects.get(id=building_id) 
    self.fields['inside_room'].queryset = Room.objects.filter(building__exact=building) 

असल में, एक 'उदाहरण' कीवर्ड रूप में भेजा जाता है, तो यह है एक मौजूदा रिकॉर्ड इनलाइन में दिखा रहा है, और इसलिए मैं बस उदाहरण से इमारत को पकड़ सकता हूं। यदि कोई उदाहरण नहीं है, तो यह इनलाइन में खाली "अतिरिक्त" पंक्तियों में से एक है, और इसलिए यह इनलाइन के छिपे हुए फॉर्म फ़ील्ड के माध्यम से जाता है जो अंतर्निहित संबंध को मुख्य पृष्ठ पर वापस संग्रहीत करता है, और उस से आईडी मान को पकड़ता है। फिर, यह उस बिल्डिंग_आईडी के आधार पर बिल्डिंग ऑब्जेक्ट को पकड़ लेता है। आखिरकार, अब इमारत हो रही है, हम ड्रॉप डाउन की क्वेरीसेट को केवल प्रासंगिक वस्तुओं को प्रदर्शित करने के लिए सेट कर सकते हैं।

मेरे मूल समाधान से अधिक सुरुचिपूर्ण, जो इनलाइन के रूप में दुर्घटनाग्रस्त हो गया और जला दिया गया (लेकिन काम किया - ठीक है, अगर आप अलग-अलग रूपों के लिए ड्रॉप डाउन भरने के लिए फॉर्म भाग को सहेजने में कोई फर्क नहीं पड़ता):

class RoomForm(forms.ModelForm): # For the individual rooms 
    class Meta: 
mode = Room 
    def __init__(self, *args, **kwargs): # Limits inside_room choices to same building only 
    super(RoomForm, self).__init__(*args, **kwargs) #On init... 
try: 
    self.fields['inside_room'].queryset = Room.objects.filter( 
    building__exact=self.instance.building) # rooms with the same building as this room 
    except:     #and hide this field (why can't I exclude?) 
    self.fields['inside_room']=forms.CharField(#Add room throws DoesNotExist error 
     widget=forms.HiddenInput, 
     required=False, 
     label='Inside Room (save room first)') 

गैर-इनलाइन के लिए, यह कमरा तब से काम करता है जब कमरा पहले से मौजूद था। यदि नहीं, तो यह एक त्रुटि (DoNotExist) फेंक देगा, इसलिए मैं इसे पकड़ूंगा और फिर फ़ील्ड को छुपाऊंगा (चूंकि व्यवस्थापक से कोई रास्ता नहीं था, इसे सही इमारत तक सीमित करने के लिए, क्योंकि पूरा कमरा रिकॉर्ड नया था, और कोई इमारत अभी तक सेट नहीं हुई थी!) ... एक बार जब आप सहेजते हैं, तो यह इमारत को बचाता है और फिर से लोड करने पर यह विकल्प सीमित कर सकता है ...

मुझे सिर्फ एक से विदेशी कुंजी फ़िल्टर को कैस्केड करने का तरीका ढूंढना होगा एक नए रिकॉर्ड में दूसरे को फ़ील्ड - यानी, नया रिकॉर्ड, एक इमारत का चयन करें, और रिकॉर्ड को सहेजने से पहले - यह स्वचालित रूप से अंदरूनी चयन बॉक्स में विकल्पों को सीमित करता है। लेकिन यह एक और दिन के लिए है ...

90

ओबीजे के लिए अस्थायी कंटेनर के रूप में प्रयुक्त अनुरोध उदाहरण। क्वेरीसेट को संशोधित करने के लिए ओवरराइड इनलाइन विधि formfield_for_foreignkey। यह कम से कम django 1.2.3 पर काम करता है।

class RoomInline(admin.TabularInline): 

    model = Room 

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs): 

     field = super(RoomInline, self).formfield_for_foreignkey(db_field, request, **kwargs) 

     if db_field.name == 'inside_room': 
      if request._obj_ is not None: 
       field.queryset = field.queryset.filter(building__exact = request._obj_) 
      else: 
       field.queryset = field.queryset.none() 

     return field 



class BuildingAdmin(admin.ModelAdmin): 

    inlines = (RoomInline,) 

    def get_form(self, request, obj=None, **kwargs): 
     # just save obj reference for future processing in Inline 
     request._obj_ = obj 
     return super(BuildingAdmin, self).get_form(request, obj, **kwargs) 
+1

इस अधिकार ने मुझे बहुत परेशानी बचाई। मुझे विकल्पों को फ़िल्टर करने की आवश्यकता थी, लेकिन एक सत्र चर द्वारा। यह जवाब मुझे कोड की 5 लाइनों के साथ करने देता है। धन्यवाद। –

+3

धन्यवाद दस लाख! वैकल्पिक विकल्प डॉक्स के अनुसार सुपर कॉल करने से पहले kwargs ['queryset'] असाइन करना है: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey – powlo

+0

यह कोड ने मुझे समय के टन भी बचाया। इस – fangsterr

15

इस पोस्ट को पढ़ने और बहुत प्रयोग करने के बाद मुझे लगता है कि मुझे इस प्रश्न का एक निश्चित उत्तर मिला है। चूंकि यह एक डिज़ाइन पैटर्न है जिसका प्रयोग किया जाता है, मैंने इसका उपयोग करने के लिए Mixin for the Django admin लिखा है।

(गतिशील रूप से) विदेशी के फ़ील्ड के लिए क्वेरीसेट को सीमित करना अब LimitedAdminMixin उप-वर्ग के रूप में सरल है और प्रासंगिक फ़िल्टर को वापस करने के लिए get_filters(obj) विधि को परिभाषित करना है। वैकल्पिक रूप से, गतिशील फ़िल्टरिंग की आवश्यकता नहीं होने पर filters संपत्ति व्यवस्थापक पर सेट की जा सकती है।

उदाहरण उपयोग:

class MyInline(LimitedAdminInlineMixin, admin.TabularInline): 
    def get_filters(self, obj): 
     return (('<field_name>', dict(<filters>)),) 

यहाँ, <field_name> FK फ़ील्ड का नाम फ़िल्टर किया जा रहा है और <filters> के रूप में आप सामान्य रूप से उन्हें क्वेरीसमूहों की filter() विधि में निर्दिष्ट करना होगा पैरामीटर की एक सूची है।

+1

धन्यवाद, महान काम करता है! बहुत साफ (और बीटीडब्ल्यू, आपने अपने कोड में कुछ लॉगिंग स्टेटमेंट छोड़े हैं जो कहीं भी नहीं जाते हैं) – Dave

8

आप कुछ कस्टम क्लास बना सकते हैं जो तब मूल उदाहरण के संदर्भ में फॉर्म के साथ पास होंगे।

from django.forms.models import BaseInlineFormSet 
from django.forms import ModelForm 

class ParentInstInlineFormSet(BaseInlineFormSet): 
    def _construct_forms(self): 
     # instantiate all the forms and put them in self.forms 
     self.forms = [] 
     for i in xrange(self.total_form_count()): 
      self.forms.append(self._construct_form(i, parent_instance=self.instance)) 

    def _get_empty_form(self, **kwargs): 
     return super(ParentInstInlineFormSet, self)._get_empty_form(parent_instance=self.instance) 
    empty_form = property(_get_empty_form) 


class ParentInlineModelForm(ModelForm): 
    def __init__(self, *args, **kwargs): 
     self.parent_instance = kwargs.pop('parent_instance', None) 
     super(ParentInlineModelForm, self).__init__(*args, **kwargs) 

वर्ग RoomInline में सिर्फ जोड़ें:

class RoomInline(admin.TabularInline): 
     formset = ParentInstInlineFormset 
     form = RoomInlineForm #(or something) 

अपने फॉर्म में आप अब self.parent_instance को init विधि में उपयोग कर सकते है!

class RoomInlineForm(ParentInlineModelForm): 
    def __init__(self, *args, **kwargs): 
     super(RoomInlineForm, self).__init__(*args, **kwargs) 
     building = self.parent_instance 
     #Filtering and stuff 
+0

इसके लिए धन्यवाद! यह पहला संस्करण है जो मेरे आवेदन के लिए काम करता है और यह भी अच्छा और स्पष्ट है। – Justin

12

वहाँ limit_choices_to ForeignKey विकल्प है कि वस्तु के लिए उपलब्ध व्यवस्थापक विकल्पों को सीमित करने की अनुमति देता है

+2

यह सीमा_choices_to में चलने वाली क्वेरी के रूप में मदद नहीं करता है "पैरेंट क्लास" का कोई संदर्भ नहीं है। हां, यदि मॉडल ए में बी के लिए विदेशी कुंजी है, और सी के लिए भी सी है, और सी के पास बी के लिए विदेशी कुंजी है, और हम यह सुनिश्चित करना चाहते हैं कि ए केवल एक सी को संदर्भित करता है जो ए के समान बी को संदर्भित करता है , क्वेरी को ए-> बी के बारे में पता होना चाहिए, जो यह नहीं करता है। –

2

Django 1.6 में:

parent_instance अब और whatnot

कुछ जैसे विकल्पों फिल्टर करने के लिए इस्तेमाल किया जा सकता

form = SpettacoloForm(instance = spettacolo) 
form.fields['teatro'].queryset = Teatro.objects.filter(utente = request.user).order_by("nome").all() 
+1

क्या आप इस प्रश्न में मौजूद मॉडल के समाधान को अनुकूलित कर सकते हैं? – raratiru

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