250

my に次のものがあるとします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の範囲を持っています。それぞれに、別の ではなく、その親から選択されたベースが必要です。RatesClientsClientRateCompany's RatesCompany's Rates

を追加するためのフォームを作成するときにClient、選択肢を削除しCompany(ページの [クライアントの追加] ボタンで既に選択されているため)、選択肢をそれにCompany制限したいと思います。RateCompany

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。また、それが管理インターフェイスの外で機能するかどうかもわかりません。

ありがとう。(これはかなり基本的な要求のように思えますが、何かを再設計する必要がある場合は、提案を受け付けています。)

4

9 に答える 9

256

ForeignKey は django.forms.ModelChoiceField で表されます。これは、選択肢がモデル QuerySet である ChoiceField です。ModelChoiceFieldのリファレンスを参照してください。

querysetそのため、フィールドの属性に QuerySet を提供します。フォームの作成方法によって異なります。明示的なフォームを作成すると、フィールドに直接名前が付けられます。

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

デフォルトの ModelForm オブジェクトを使用すると、form.fields["rate"].queryset = ...

これはビューで明示的に行われます。ハッキングはありません。

于 2008-11-15T01:39:26.003 に答える
145

S.Lottの回答に加えて、コメントで言及されているように、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ブログの資料を言い換えています。

于 2009-08-07T13:01:38.733 に答える
49

これは単純で、Django 1.4 で動作します:

class ClientAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(ClientAdminForm, self).__init__(*args, **kwargs)
        # access object through self.instance...
        self.fields['base_rate'].queryset = Rate.objects.filter(company=self.instance.company)

class ClientAdmin(admin.ModelAdmin):
    form = ClientAdminForm
    ....

これをフォーム クラスで指定する必要はありませんが、Django には既に ModelAdmin にこの組み込みメソッドが含まれているため (ドキュメントから)、ModelAdmin で直接指定できます。

ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)¶
'''The formfield_for_foreignkey method on a ModelAdmin allows you to 
   override the default formfield for a foreign keys field. For example, 
   to return a subset of objects for this foreign key field based on the
   user:'''

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "car":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

これを行うためのさらに優れた方法 (たとえば、ユーザーがアクセスできるフロントエンド管理インターフェイスを作成する場合) は、ModelAdmin をサブクラス化し、以下のメソッドを変更することです。最終的な結果は、ユーザーに関連するコンテンツのみを表示するユーザー インターフェイスであり、ユーザー (スーパー ユーザー) はすべてを表示できます。

私は 4 つのメソッドをオーバーライドしました。最初の 2 つは、ユーザーが何も削除できないようにし、管理サイトから削除ボタンを削除します。

3 番目のオーバーライドは、への参照を含むすべてのクエリをフィルター処理します (例では、'user' または 'porcupine' (例として))。

最後のオーバーライドは、モデル内の任意の外部キー フィールドをフィルター処理して、基本的なクエリセットと同じように利用可能な選択肢をフィルター処理します。

このようにして、ユーザーが自分のオブジェクトをいじることができる管理しやすい正面の管理サイトを提示でき、上記で説明した特定の ModelAdmin フィルターを入力することを覚えておく必要はありません。

class FrontEndAdmin(models.ModelAdmin):
    def __init__(self, model, admin_site):
        self.model = model
        self.opts = model._meta
        self.admin_site = admin_site
        super(FrontEndAdmin, self).__init__(model, admin_site)

「削除」ボタンを削除します。

    def get_actions(self, request):
        actions = super(FrontEndAdmin, self).get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

削除許可を防止する

    def has_delete_permission(self, request, obj=None):
        return False

管理サイトで表示できるオブジェクトをフィルターします。

    def get_queryset(self, request):
        if request.user.is_superuser:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()
            return qs

        else:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()

            if hasattr(self.model, ‘user’):
                return qs.filter(user=request.user)
            if hasattr(self.model, ‘porcupine’):
                return qs.filter(porcupine=request.user.porcupine)
            else:
                return qs

管理サイトのすべての外部キー フィールドの選択肢をフィルター処理します。

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if request.employee.is_superuser:
            return super(FrontEndAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

        else:
            if hasattr(db_field.rel.to, 'user'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(user=request.user)
            if hasattr(db_field.rel.to, 'porcupine'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(porcupine=request.user.porcupine)
            return super(ModelAdminFront, self).formfield_for_foreignkey(db_field, request, **kwargs)
于 2013-03-27T19:18:52.140 に答える
25

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)

ここで私の投稿を読んでください

于 2012-04-15T03:53:42.927 に答える
4

フォームを作成しておらず、クエリセットを変更したい場合は、次のようにします。

formmodel.base_fields['myfield'].queryset = MyModel.objects.filter(...)

これは、一般的なビューを使用している場合に非常に便利です!

于 2011-08-08T22:11:15.180 に答える
1

より一般的な方法は、管理クラスで get_form を呼び出すことです。データベース以外のフィールドでも機能します。たとえば、ここでは get_list(request) からいくつかのターミナル項目を選択し、request.user に基づいてフィルタリングするための特殊なケースで使用できるフォームに '_terminal_list' というフィールドがあります。

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
于 2016-08-17T05:42:37.837 に答える