template - python form




如何在Django ModelForm中過濾ForeignKey選項? (5)

ForeignKey由django.forms.ModelChoiceField表示,它是一個ChoiceField,其選擇是模型QuerySet。 請參閱ModelChoiceField的參考。

因此,請將QuerySet提供給字段的queryset屬性。 取決於你的表單是如何構建的。 如果你建立一個明確的表單,你將會有直接命名的字段。

form.rate.queryset = Rate.objects.filter(company_id=the_company.id)

如果您採用默認的ModelForm對象, form.fields["rate"].queryset = ...

這是在視圖中明確完成的。 沒有黑客周圍。

假設我在models.py有以下內容:

class Company(models.Model):
   name = ...

class Rate(models.Model):
   company = models.ForeignKey(Company)
   name = ...

class Client(models.Model):
   name = ...
   company = models.ForeignKey(Company)
   base_rate = models.ForeignKey(Rate)

即有多家Companies ,每家Companies都有一系列RatesClients 。 每位Client的基本Rate應從其母公司Company's Rates ,而非其他Company's Rates

創建用於添加Client的表單時,我想刪除Company選擇(因為已經通過Company頁面上的“添加客戶”按鈕選擇了該選項),並將Rate選擇限制為該Company

我如何在Django 1.0中解決這個問題?

我目前的forms.py文件目前只是樣板文件:

from models import *
from django.forms import ModelForm

class ClientForm(ModelForm):
    class Meta:
        model = Client

views.py也是基本的:

from django.shortcuts import render_to_response, get_object_or_404
from models import *
from forms import *

def addclient(request, company_id):
    the_company = get_object_or_404(Company, id=company_id)

    if request.POST:
        form = ClientForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(the_company.get_clients_url())
    else:
        form = ClientForm()

    return render_to_response('addclient.html', {'form': form, 'the_company':the_company})

在Django 0.96中,我能夠在渲染模板之前通過做類似下面的事情來破解它:

manipulator.fields[0].choices = [(r.id,r.name) for r in Rate.objects.filter(company_id=the_company.id)]

ForeignKey.limit_choices_to似乎很有前途,但我不知道如何傳入the_company.id ,我不清楚這是否可以在Admin界面之外工作。

謝謝。 (這似乎是一個非常基本的要求,但如果我應該重新設計一些我可以接受的建議。)


使用通用視圖來完成此操作,如CreateView ...

class AddPhotoToProject(CreateView):
    """
    a view where a user can associate a photo with a project
    """
    model = Connection
    form_class = CreateConnectionForm


    def get_context_data(self, **kwargs):
        context = super(AddPhotoToProject, self).get_context_data(**kwargs)
        context['photo'] = self.kwargs['pk']
        context['form'].fields['project'].queryset = Project.objects.for_user(self.request.user)
        return context
    def form_valid(self, form):
        pobj = Photo.objects.get(pk=self.kwargs['pk'])
        obj = form.save(commit=False)
        obj.photo = pobj
        obj.save()

        return_json = {'success': True}

        if self.request.is_ajax():

            final_response = json.dumps(return_json)
            return HttpResponse(final_response)

        else:

            messages.success(self.request, 'photo was added to project!')
            return HttpResponseRedirect(reverse('MyPhotos'))

那最重要的部分...

    context['form'].fields['project'].queryset = Project.objects.for_user(self.request.user)

在這裡閱讀我的帖子


所以,我真的試圖理解這一點,但似乎Django仍然沒有使這個非常簡單。 我並不是那麼笨,但我看不到任何(有點)簡單的解決方案。

我發現為了這樣的事情必須重寫管理視圖通常很醜陋,我發現每個例子都從未完全適用於管理視圖。

對於我所製作的模型來說,這是一個普遍的情況,我發現它沒有明顯的解決方案,這令人震驚......

我有這些類:

# models.py
class Company(models.Model):
    # ...
class Contract(models.Model):
    company = models.ForeignKey(Company)
    locations = models.ManyToManyField('Location')
class Location(models.Model):
    company = models.ForeignKey(Company)

這會在設置Admin for Company時產生問題,因為它已為內部合同和位置內聯,並且根據您當前正在編輯的公司,合同的位置m2m選項未被正確過濾。

總之,我需要一些管理選項來做這樣的事情:

# admin.py
class LocationInline(admin.TabularInline):
    model = Location
class ContractInline(admin.TabularInline):
    model = Contract
class CompanyAdmin(admin.ModelAdmin):
    inlines = (ContractInline, LocationInline)
    inline_filter = dict(Location__company='self')

最終,我不會在乎過濾過程是放在基本的公司管理上,還是放在ContractInline上。 (把它放在內聯更有意義,但它使得很難將基礎契約稱為“自我”。)

有沒有人知道像這個急需的捷徑一樣簡單? 當我為這類事情做PHP管理員時,這被認為是基本功能! 事實上,它總是自動的,如果你真的不想要它,必須禁用!


更公開的方法是在Admin類中調用get_form。 它也適用於非數據庫字段。 例如,在這裡,我在表單上有一個名為'_terminal_list'的字段,可以在特殊情況下用於從get_list(request)中選擇多個終端項,然後基於request.user進行過濾。

class ChangeKeyValueForm(forms.ModelForm):  
    _terminal_list = forms.ModelMultipleChoiceField( 
queryset=Terminal.objects.all() )

    class Meta:
        model = ChangeKeyValue
        fields = ['_terminal_list', 'param_path', 'param_value', 'scheduled_time',  ] 

class ChangeKeyValueAdmin(admin.ModelAdmin):
    form = ChangeKeyValueForm
    list_display = ('terminal','task_list', 'plugin','last_update_time')
    list_per_page =16

    def get_form(self, request, obj = None, **kwargs):
        form = super(ChangeKeyValueAdmin, self).get_form(request, **kwargs)
        qs, filterargs = Terminal.get_list(request)
        form.base_fields['_terminal_list'].queryset = qs
        return form

除了S.Lott的回答以及在評論中提到成為ModelForm.__init__ ,還可以通過重寫ModelForm.__init__函數來添加查詢集過濾器。 (這可以很容易地適用於常規形式)它可以幫助重複使用,並保持視圖功能整潔。

class ClientForm(forms.ModelForm):
    def __init__(self,company,*args,**kwargs):
        super (ClientForm,self ).__init__(*args,**kwargs) # populates the post
        self.fields['rate'].queryset = Rate.objects.filter(company=company)
        self.fields['client'].queryset = Client.objects.filter(company=company)

    class Meta:
        model = Client

def addclient(request, company_id):
        the_company = get_object_or_404(Company, id=company_id)

        if request.POST:
            form = ClientForm(the_company,request.POST)  #<-- Note the extra arg
            if form.is_valid():
                form.save()
                return HttpResponseRedirect(the_company.get_clients_url())
        else:
            form = ClientForm(the_company)

        return render_to_response('addclient.html', 
                                  {'form': form, 'the_company':the_company})

如果你有很多模型需要通用的過濾器(通常我聲明了一個抽象的Form類),這對重用很有用。 例如

class UberClientForm(ClientForm):
    class Meta:
        model = UberClient

def view(request):
    ...
    form = UberClientForm(company)
    ...

#or even extend the existing custom init
class PITAClient(ClientForm):
    def __init__(company, *args, **args):
        super (PITAClient,self ).__init__(company,*args,**kwargs)
        self.fields['support_staff'].queryset = User.objects.exclude(user='michael')

除此之外,我只是重申Django的博客資料,其中有許多好的博客資料。







django-forms