관리자의 인라인 양식에서 선택 항목 관리자의 외래 키 선택 제한
모델의 논리는 다음과 가변적입니다.
- A
Building
는 많은Rooms
- A
Room
는 다른 내부에있을 수 있습니다Room
(예 : 'self'에 대한 ForeignKey). - A
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
있는 inside_room
항목으로 만 선택 을 제한해야합니다 .rooms
building
BuildingAdmin
limit_choices_to
모델에서 로 수행하는 방법을 알아낼 수 없으며 관리자의 인라인 폼셋을 올바르게 재정의하는 방법을 정확히 알아낼 수 없습니다 (어떻게 든 사용자 지정 인라인 폼을 만들어야 하고 BUILDING_ID를 기본 양식을 사용자 정의 인라인으로 만든 다음이를 기반으로 그러나이를 수행하는 방법에 대해 머리를 감쌀 수는 없습니다.
아마도 그렇게 할 것입니다.
요청 인스턴스를 obj의 임시 컨테이너로 사용했습니다. 쿼리 셋을 수정하기 위해 인라인 메서드 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)
이 게시물을 읽고 많은 실험을 한 후이 질문에 대한 확실한 답을 것입니다. Django 관리자가 자주 사용되는 디자인 패턴 을 사용하여 Mixin을 작성 합니다.
(동적으로) 외래 키에, 대한 쿼리 필드 세트를 제한하는 [해석] 이제 관련 필터를 반환 LimitedAdminMixin
하는 get_filters(obj)
메서드를 sub-클래 싱 하고 정의하는 것처럼 간단 합니다. 또는 filters
동적 필터링이 필요하지 않은 경우 관리자에서 속성을 수 있습니다.
사용 예 :
class MyInline(LimitedAdminInlineMixin, admin.TabularInline):
def get_filters(self, obj):
return (('<field_name>', dict(<filters>)),)
다음 <field_name>
은 필터링 할 FK 필드의 이름이며
<filters>
일반적으로 filter()
쿼리 세트 메소드에서 지정하는 매개 변수 목록입니다 .
이 limit_choices_to 클래스에 사용할 수있는 관리자의 선택을 제한 할 수 있습니다. 외래 키 옵션은
부모 인스턴스에 대한 참조를 양식에 많이 몇 가지 사용자 지정 클래스를 만들 수 있습니다.
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)
양식에서 이제 init 메소드에서 self.parent_instance에 액세스 할 수 있습니다! 이제 parent_instance를 사용하여 선택 항목과 기타 항목을 필터링 할 수 있습니다.
같은 것 :
class RoomInlineForm(ParentInlineModelForm):
def __init__(self, *args, **kwargs):
super(RoomInlineForm, self).__init__(*args, **kwargs)
building = self.parent_instance
#Filtering and stuff
이 질문과 답변은 매우 유사하며 일반 관리자 양식에서 작동합니다.
인라인 내부와 분리되는 곳은 ... 내 한계에서 필요한 외래 키 값을 위해 기본 양식의 데이터를 얻을 수 없습니다 (또는 인라인의 레코드 중 하나).
여기 내 admin.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)
인라인 양식에 잘 작동 하는 상당히 우아한 솔루션 을 찾았 습니다 .
동일한 건물에있는 방만 반환하는 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)
기본적으로 '인스턴스'키워드가 양식에 전달되면 인라인에 표시되는 기존 레코드이므로 인스턴스에서 건물을 가져올 수 있습니다. 인스턴스가 아니라면 인라인의 빈 "추가"행 중 하나이므로 기본 페이지에 대한 암시 적 관계를 다시 저장하는 인라인의 숨겨진 양식 필드를 통과하고 여기에서 id 값을 가져옵니다. 그런 다음 해당 building_id를 기반으로 건물 개체를 가져옵니다. 마지막으로 건물이 생겼으므로 드롭 다운의 쿼리 셋을 설정하여 관련 항목 만 표시 할 수 있습니다.
인라인으로 충돌 및 구워진 원래 솔루션보다 더 우아합니다. (하지만 개별 양식에 대해 양식을 중간에 저장하여 드롭 다운을 채우는 데 신경 쓰지 않는다면 작동했습니다) :
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)')
인라인이 아닌 경우 방이 이미 존재하면 작동했습니다. 그렇지 않은 경우 오류 (DoesNotExist)가 발생하므로이를 포착 한 다음 필드를 숨길 것입니다 (관리자로부터 전체 방 레코드가 새 것이기 때문에 올바른 건물로 제한 할 방법이 없었기 때문에, 아직 건물이 설정되지 않았습니다!) ... 저장을 누르면 건물이 저장되고 다시로드하면 선택 사항이 제한 될 수 있습니다 ...
새 레코드에서 한 필드에서 다른 필드로 외래 키 필터를 계단식으로 연결하는 방법을 찾아야합니다. 즉, 새 레코드에서 건물을 선택하면 자동으로 inside_room 선택 상자의 선택이 제한됩니다. 저장되었습니다. 하지만 그것은 다른 날입니다 ...
@nogus의 문제는 팝업에 여전히 잘못된 URL이 있습니다. /?_to_field=id&_popup=1
사용자가 팝업에서 잘못된 항목을 선택할 수 있도록합니다.
마침내 작동하게하려면 field.widget.rel.limit_choices_to
dict 를 변경해야했습니다.
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':
building = request._obj_
if building is not None:
field.queryset = field.queryset.filter(
building__exact=building)
# widget changed to filter by building
field.widget.rel.limit_choices_to = {'building_id': building.id}
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)
Daniel이 귀하의 질문을 편집 한 후에도 답변을하지 않았다면-제가별로 도움이되지 않을 것 같습니다 ... :-)
장고 관리자에게 자신의 뷰, 양식 및 템플릿 그룹으로 구현하는 것이 더 나은 논리를 강제로 적용하려고한다고 제안합니다.
그런 종류의 필터링을 InlineModelAdmin에 적용하는 것이 가능하지 않다고 생각합니다.
장고 1.6에서 :
form = SpettacoloForm( instance = spettacolo )
form.fields['teatro'].queryset = Teatro.objects.filter( utente = request.user ).order_by( "nome" ).all()
나는 당신이하려는 일을 정확히 따르지 않았지만 당신의 사이트를 관리자로부터 제외시키는 것을 고려하지 않는 것이 충분히 복잡하다고 생각합니다.
한때 간단한 관리자 인터페이스로 사이트를 구축했지만 너무 커스터마이징되어 관리자의 제약 내에서 작업가 매우 어려워졌습니다. 처음부터 시작 더 나았을 것입니다. 처음에는 더 많은 작업을하지만 결국 훨씬 더 유연하고 고통이 덜합니다. 내 경험 법칙은 당신이하려는 것이 문서화되어 있지 않다면 (즉, 관리 방법을 재정의하고, 관리 소스 코드로 피어링하는 등) 관리자를 사용하지 않는 것이 좋습니다. 나만 2 센트. :)
'ProgramingTip' 카테고리의 다른 글
Swift를 사용하여 로컬 HTML을 UIWebView에로드 (0) | 2020.11.07 |
---|---|
OS X v10.6.8에서 현재 버전의 Ruby (2.2.3)로 업그레이드 할 수 있습니까? (0) | 2020.11.07 |
노드 프레임 워크를 사용하여 간단한 자바 펼쳐보기를 실행하는 동안 오류 발생 (0) | 2020.11.07 |
AsyncTask는 활동이 파괴 된 경우에도 중지되지 않습니다. (0) | 2020.11.07 |
두 날짜의 차이 (초) (0) | 2020.11.07 |