Django-tastypie: SHOWアクションとINDEXアクションのさまざまなフィールド

作成日 2010年10月25日  ·  40コメント  ·  ソース: django-tastypie/django-tastypie

みなさん、こんにちは。アクションに応じてリソースに異なるフィールドを表示することは可能ですか。

私が欲しい理由は、リソースに含まれるすべてのhas_manyフィールドでインデックスアクションの負担を回避するためです。

たとえば、著者のインデックスビューでは、彼の名前のみを返したいのですが、ショーアクションでは、すべての書籍のリソースを含めたいと思います。

ありがとう、
ボージャン

documentation feature

最も参考になるコメント

私が使用している簡単な回避策は、 get_detailおよびget_listメソッドをオーバーライドしてそのようなフィールドを編集することです。
これにより、フィールドのデータを実際にフェッチしてバンドルから削除するオーバーヘッドが節約されますが、リソースオブジェクトがすべてのAPI呼び出しで作成されるわけではないように見えるため、このメソッドがスレッドセーフかどうかはわかりません。
誰かがこれについてコメントできれば素晴らしいでしょう。

コードは次のとおりです。

class ArticleResource(BaseModelResource):
    owner = fields.ToOneField(UserResource, 'owner', full=True)

    class Meta:
        resource_name = "articles"

    def get_list(self, request, **kwargs):
        self.fields.pop("comments", None)
        return super(ArticleResource, self).get_list(request, **kwargs)

    def get_detail(self, request, **kwargs):
        self.fields["comments"] = fields.ToManyField(CommentResource, 'comments', full=True)
        return super(ArticleResource, self).get_detail(request, **kwargs)

全てのコメント40件

+1
私はこの機能を強く望んでいます:「詳細フィールド」と「リストフィールド」を分離することができます

+1(#44での複製による)

インデックスアクションでデータのサブセットを簡単に取得できるようにする方法が2つあるように思われます。

  1. full_dehydrateがindexメソッドとgetメソッドに異なるフィールドを使用するようにします。 多分次のようなものです:

メタ:
index_exclude_fields = ['some_m2m_field']

これにより、dbクエリを保存できます。

  1. 追加の「メソッド」パラメータを渡して脱水します。 これにより、インデックスで不要な一部のフィールドをスキップするなどのバンドルのカスタマイズが可能になり、帯域幅をいくらか節約したいと考えています。

1.はキャッシュに影響を与えると評価しませんでした。
どう思いますか?

#48でもだまされましたが、それぞれの問題には価値のある側面があります。

できればこの機能をやめたかったのですが、私はかなり頭がおかしいです。 とにかくより良いファイルサポートのためにそこにある必要があるでしょう、それでそれは対処されなければなりません。 これで1.0をターゲットにします。

これについての私のユースケースをもう少し検討した後、ビューを表示/リストするだけではなく、より柔軟なものとして実装することを提案したいと思います。 私が本当にできる必要があるのは、次のようなクエリ文字列フラグを取ることです。

&shape = [full | simple | none]

そして、出力にユーザーが選択した詳細レベルを反映させます。 これをハックする方法を探しましたが、ModelResourceクラスの作成時にフィールドが含まれる/除外されるため、応答サイクルの後半で使用可能なものを変更する方法を思い付くことができませんでした。 さらに、リクエストオブジェクトは、そのような決定を行いたいプロセスのステップでは使用できません。

+1

この問題に関連しているように思われるので、ここで最初にこの考えに言及しますが、それ自体にスピンオフする必要があるかもしれません。

さまざまなフィールドを表示するメカニズムが開発されている場合、それを認証と統合することもできれば便利です。 より多くの/より少ないフィールドを公開できるかどうかは、ユーザーの権限に依存しますが、非常に強力です。 これがすでに可能である場合、私はそれについての言及を見つけることができませんでした。

+1

+1

+1

+1

+1

+1

+1

リスト/詳細に加えて、メソッドごと(POST、PUT、GET)に表示されるフィールド(フィールドまたは除外のいずれかを使用)を制御できると期待しています。

回避策は次のとおりです。以下の例を参照してください。元のモデルのリソースモデル(例ではUserResource)から何も除外しないため、インデックスビューでいっぱいになります。 サブモデルを含むmodelresourceのdehydrateメソッドに移動し(BlogPostResourceには作成者が含まれます)、バンドルの要素を削除する必要があります。

例:

class BlogPostResource(ModelResource):
     author = fields.ForeignKey(UserResource, 'author', full=True)
     ....
     class Meta:
         ...

def dehydrate(self, bundle):
         del bundle.data['author'].data['field_you_dont_wanna_show_here']
         del bundle.data['author'].data['field_you_dont_wanna_show_here']
         return bundle

したがって、ユーザーを一覧表示する場合でもすべてのフィールドを取得できますが、ブログ投稿を一覧表示する場合は、たとえば、作成者の名前と名前だけを取得できます。

どう思いますか?

非常に汚い回避策の例:list_viewにプロジェクトフィールドを表示しませんが、詳細に表示します。リソース自体にアクセスする必要はなく、ハードコーディングせずにURL自体を取得することは可能ですが、チェックアウトする時間がありませんでした。 :

class CompanyResource(ModelResource):
       """
       Tastypie resource for Company
      """
       projects = fields.ToManyField('api.resources.ProjectResource',
                                  'projects',full=True)
       class Meta:
           queryset = Company.objects.all()
           resource_name = 'companies'

       def dehydrate(self, bundle):
           if bundle.request.path == "/api/v1/companies/":
               del bundle.data['projects']
           return bundle

+1

+1

+1

電車に乗るのもいいのですが、 @ ashwoodsが指摘したことで十分です。 もう少しダイナミックになるように少し変更しました。

    def dehydrate(self, bundle):
        if self.get_resource_uri(bundle) == bundle.request.path:
            print "Detail"

        if self.get_resource_uri(bundle) != bundle.request.path:
            print "Not Detail - Could be list or reverse relationship."

        return bundle

それで、私はこれにもう少し考えを与えて、 @ bmihelacが探していたと思うことを私にやらせることにかなり近い何かを

提供されている例の@ashwoodsを使用して、詳細な応答である場合にのみプロジェクトフィールドを表示したいとします。

class CompanyResource(ModelResource):
    """
    Tastypie resource for Company
    """

    class Meta:
        queryset = Company.objects.all()
        resource_name = 'companies'
        additional_detail_fields = {'projects': fields.ToManyField('api.resources.ProjectResource', 'projects',full=True)}

    def dehydrate(self, bundle):
        # detect if detail
        if self.get_resource_uri(bundle) == bundle.request.path:
            # detail detected, include additional fields
            bundle = self.detail_dehydrate(bundle)

        return bundle

    # detail_dehydrate is basically full_dehydrate
    # except we'll loop over the additional_detail_fields
    # and we won't want to do the dehydrate(bundle) at the end
    def detail_dehydrate(self, bundle):
        """
        Given a bundle with an object instance, extract the information from it
        to populate the resource.
        """
        # Dehydrate each field.
        # loop over additional_detail_fields instead
        #for field_name, field_object in self.fields.items():
        for field_name, field_object in self._meta.additional_detail_fields.items():
            # A touch leaky but it makes URI resolution work.
            if getattr(field_object, 'dehydrated_type', None) == 'related':
                field_object.api_name = self._meta.api_name
                field_object.resource_name = self._meta.resource_name

            bundle.data[field_name] = field_object.dehydrate(bundle)

            # Check for an optional method to do further dehydration.
            method = getattr(self, "dehydrate_%s" % field_name, None)

            if method:
                bundle.data[field_name] = method(bundle)

        # dehydrating the bundle will create an infinite loop
        #bundle = self.dehydrate(bundle)
        return bundle

+1、今のところ@dericcragoからの修正を使用。

+1

+1

#526での部分的な実装ですが、すべてが販売されているかどうかはわかりません。テストやドキュメントが不足しています。

このチケットを見たばかりです...そしてまた、上記のonyxfishによって言及された「形状」アプローチのように...

他の場合に人々が異なる「形」を望んでいた場合に備えて、#526の私の解決策は少し制限されていると思っていました...

脱水後にフィールドを削除するという提案に...私の全体的な理由は、そもそも値を計算しないようにすることです。

ただし、条件付きで詳細を追加できるdetail_dehydrateフックのアイデアが気に入っています。

テストとドキュメントの両方を含む2つの可能な実装が利用可能であるように見えます。 私は#569で1つ書きましたが、#538も同様の機能を実行します( use_inは呼び出し可能である可能性があるため、#538ではもう少し柔軟性があります)。 私の実装では、この機能を制御するためにmeta属性を追加しています(これは現在のfields属性と一致しています)が、#538はフィールドに属性を追加します。 どちらも有効であるように思われますが、どちらに進むかについての設計上の決定にすぎません。 メタへの追加は私には一貫しているようで、一部のフィールドは自動的に生成でき、初期化パラメーターを変更できない場合があるため、使いやすくなっています。 もう1つの方法は、両方のプルリクエストを組み合わせて、 use_inパラメーターをmeta属性に基づいて自動的に設定できるようにすることですが、これによりAPIが必要以上に複雑になるようです。 関連するプルリクエストを指摘してくれた@issackellyに感謝します。

[私が#538の背後にある最初の原因だったので、チャイムを鳴らしました。それは私の#526のクリーンアップでした]
非常に理にかなっています...メタアプローチは、実際、ModelResourceなどの除外リストでゲル化します...

別のチケットで述べたように、このような「単純な」ソリューションは、1.0リリースにはIMHOで十分です...「クライアントが選択可能な「形状」」のようなより複雑なソリューションは、後のリリースではおそらく望ましいでしょう。 。

@funkybob同意しました。確かに、より複雑なソリューションがクライアント側から役立つでしょうが、1.0がリリースされる前に使用を開始できるように、この機能をできるだけ早く含めるとよいでしょう。

実際に本番アプリケーションでPRを使用しているので、#538が提供するコールバックの柔軟性が本当に気に入っています。 パーミッションに基づいて実行時にリソースを非表示にする必要があるユースケースがいくつかあります。

これは、#569を使用している私には不可能です。

こんにちは、何かニュースはありますか? そのPRは人生をとても楽にしてくれます、ありがとう!

@dericcragoが提供するハックで行く

+1

私が使用している簡単な回避策は、 get_detailおよびget_listメソッドをオーバーライドしてそのようなフィールドを編集することです。
これにより、フィールドのデータを実際にフェッチしてバンドルから削除するオーバーヘッドが節約されますが、リソースオブジェクトがすべてのAPI呼び出しで作成されるわけではないように見えるため、このメソッドがスレッドセーフかどうかはわかりません。
誰かがこれについてコメントできれば素晴らしいでしょう。

コードは次のとおりです。

class ArticleResource(BaseModelResource):
    owner = fields.ToOneField(UserResource, 'owner', full=True)

    class Meta:
        resource_name = "articles"

    def get_list(self, request, **kwargs):
        self.fields.pop("comments", None)
        return super(ArticleResource, self).get_list(request, **kwargs)

    def get_detail(self, request, **kwargs):
        self.fields["comments"] = fields.ToManyField(CommentResource, 'comments', full=True)
        return super(ArticleResource, self).get_detail(request, **kwargs)

+1

別の回避策は、詳細ビューとリストビューに異なるリソースを用意することです。

from tastypie.resources import ModelResource
from django.contrib.auth.models import User

# detail will show everything except password
class UserResourceDetail(ModelResource):
    class Meta:
        queryset = User.objects.all()
    excludes = ('password',)
    resource_name = 'user'

# list will only show username & date_joined (and exclude password)
class UserResource(UserResourceDetail):
    class Meta(UserResourceDetail.Meta):
        fields = ('username', 'date_joined')
    get_detail = UserResourceDetail().get_detail

# ... register & use UserResource

+1

@dnozay回避策の+1

+1 @dnozay 、すごい

get_resource_uriをユーザー詳細ビューで正しく機能させる場合は、 UserResourceを定義した後で、以下を追加する必要があることに注意してください。

UserResourceDetail.get_resource_uri = UserResource().get_resource_uri

それ以外の場合、 resource_uriはすべての詳細応答で空になります。

おそらく関連:#1265

このページは役に立ちましたか?
0 / 5 - 0 評価