Django-tastypie: Verschiedene Felder für SHOW- und INDEX-Aktionen

Erstellt am 25. Okt. 2010  ·  40Kommentare  ·  Quelle: django-tastypie/django-tastypie

Hallo zusammen, ist es möglich, je nach Aktion unterschiedliche Felder für Ressourcen anzuzeigen.

Ich möchte, dass die Indexaktion nicht mit allen has_many-Feldern belastet wird, die die Ressource enthält.

Zum Beispiel möchte ich in der Indexansicht des Autors nur seinen Namen zurückgeben, aber für die Show-Aktion möchte ich alle Bücherressourcen einschließen.

Danke,
Bojan

documentation feature

Hilfreichster Kommentar

Eine einfache Problemumgehung, die ich verwende, besteht darin, die Methoden get_detail & get_list zu überschreiben, um solche Felder zu bearbeiten.
Dies spart den Aufwand für das tatsächliche Abrufen der Daten für das Feld und das anschließende Löschen aus dem Bundle, aber ich bin mir nicht sicher, ob diese Methode threadsicher ist, da die Resource-Objekte anscheinend nicht bei jedem API-Aufruf erstellt werden.
Es wäre toll, wenn sich jemand dazu äußern könnte.

Hier ist der Code:

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)

Alle 40 Kommentare

+1
Ich hoffe sehr auf dieses Feature: "Detailfeld" und "Listenfeld" können getrennt werden

+1 (über Duplikat bei #44)

Es scheint mir, dass es zwei Dinge gibt, die das Abrufen einer Teilmenge von Daten in Indexaktionen erleichtern könnten:

  1. Lassen Sie full_dehydrate verschiedene Felder für Index- und Get-Methoden verwenden. Vielleicht sowas wie:

Meta:
index_exclude_fields = ['some_m2m_field']

Dies würde es uns ermöglichen, einige DB-Abfragen zu speichern.

  1. Übergeben Sie einen zusätzlichen 'Methode'-Parameter, um zu dehydrieren. Dies würde eine Anpassung des Bundles ermöglichen, z. B. das Überspringen einiger Felder, die im Index nicht benötigt werden, und wir möchten etwas Bandbreite sparen.

Ich habe nicht bewertet, 1. würde Auswirkungen auf das Caching haben.
Was denken Sie?

Auch in #48 getäuscht, aber jede Ausgabe hat lohnende Aspekte.

Ich wollte diese Funktion abbrechen, wenn ich könnte, aber ich bin weit unterlegen. Es muss sowieso für eine bessere Dateiunterstützung da sein, also muss es behoben werden. Werde hier 1.0 anvisieren.

Nachdem ich meinen Anwendungsfall dafür etwas genauer untersucht habe, möchte ich vorschlagen, dass es flexibler implementiert wird als nur Show-/Listenansichten. Was ich wirklich tun muss, ist ein Abfragestring-Flag wie folgt:

&shape=[voll|einfach|keine]

und lassen Sie die Ausgabe den ausgewählten Detaillierungsgrad des Benutzers widerspiegeln. Ich habe nach einer Möglichkeit gesucht, dies zu hacken, aber da die Felder beim Erstellen der ModelResource-Klasse eingeschlossen/ausgeschlossen werden, konnte ich keine Möglichkeit finden, die später im Antwortzyklus verfügbaren Elemente zu ändern. Außerdem ist das Anforderungsobjekt in den Prozessschritten, in denen ich eine solche Feststellung treffen möchte, nicht verfügbar.

+1

Ich werde diesen Gedanken hier zuerst erwähnen, da er für dieses Problem relevant zu sein scheint, aber er muss möglicherweise in seinen eigenen ausgegliedert werden.

Wenn ein Mechanismus zum Anzeigen verschiedener Felder entwickelt wird, wäre es nützlich, wenn dieser auch in die Authentifizierung integriert werden könnte. In der Lage zu sein, mehr/weniger Felder verfügbar zu machen, hängt von den Berechtigungen des Benutzers ab, wäre sehr mächtig. Falls dies bereits möglich ist, konnte ich keine Erwähnung finden.

+1

+1

+1

+1

+1

+1

+1

Ich würde erwarten, in der Lage zu sein, die angezeigten Felder (mit Feldern oder Ausschlüssen) zusätzlich zur Liste / zum Detail nach einer Methode (POST, PUT, GET) zu steuern.

Hier ist eine Problemumgehung, siehe das Beispiel unten: Sie schließen nichts aus dem Ressourcenmodell des ursprünglichen Modells (UserResource im Beispiel) aus, damit es in seiner Indexansicht vollständig angezeigt wird. Sie müssen in die dehydrate-Methode der modelresource gehen, die Ihr Untermodell enthält (die BlogPostResource enthält den Autor) und einfach die Elemente des Bundles löschen.

Beispiel:

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

Wenn Sie also die Benutzer auflisten möchten, erhalten Sie immer noch alle Felder, aber wenn Sie Blogposts auflisten, können Sie beispielsweise nur den Vor- und Nachnamen des Autors abrufen.

Was denken Sie?

Beispiel für eine sehr, sehr schmutzige Problemumgehung: Zeigt keine Projektfelder in list_view an, aber in detail_view, ohne auf die Ressource selbst zugreifen zu müssen, die URL selbst ohne Hardcoding zu erhalten sollte möglich sein, hatte aber keine Zeit, sie auszuchecken :

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

Ich werde auch an Bord springen , es wäre schön, einen Zug zu haben, aber was durchzubringen . Ich habe es leicht modifiziert, um etwas dynamischer zu sein.

    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

Also habe ich mir etwas mehr Gedanken gemacht und mir etwas einfallen lassen, das mir ziemlich nahe kommt, das zu tun, wonach nach gesucht hat.

Angenommen, wir möchten das @ashwoods nur anzeigen, wenn es sich um eine Detailantwort handelt:

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, vorerst mit Fix von

+1

+1

Teilimplementierung in # 526, ich bin mir nicht sicher, ob ich von allem verkauft bin und es fehlen Tests/Dokumente.

Habe gerade dieses Ticket gesehen ... und auch wie der oben von Onyxfish erwähnte "Shape" -Ansatz ...

Dachte, meine Lösung in #526 sei etwas eingeschränkt, falls die Leute in anderen Fällen andere "Formen" wollten ...

zu den Vorschlägen, Felder nach dem Entwässern zu entfernen ... mein einziger Grund ist es, die Berechnung der Werte von vornherein zu vermeiden.

Die Idee für den detail_dehydrate-Hook, um das bedingte Hinzufügen weiterer Details zu ermöglichen, gefällt mir jedoch.

Es sieht so aus, als wären zwei mögliche Implementierungen verfügbar, die sowohl Tests als auch Dokumente enthalten. Ich habe eine in #569 geschrieben und #538 führt auch eine ähnliche Funktionalität durch (#538 ermöglicht etwas mehr Flexibilität, da use_in ein Callable sein kann). Meine Implementierung fügt meta Attribute hinzu, um diese Funktionalität zu steuern (die mit dem aktuellen fields Attribut übereinstimmt), während #538 ein Attribut zu Feldern hinzufügt. Beides scheint gültig zu sein, nur eine Designentscheidung, in welche Richtung es gehen soll. Das Hinzufügen zum Meta erscheint mir konsistent und ist einfacher zu verwenden, da einige Felder automatisch generiert werden können und das Ändern ihrer Initialisierungsparameter möglicherweise nicht möglich ist. Eine andere Alternative wäre, beide Pull-Requests zu kombinieren und zuzulassen, dass der Parameter use_in basierend auf dem Attribut meta automatisch gesetzt wird. Dies scheint jedoch die API komplexer zu machen als nötig. Danke an @issackelly für den Hinweis auf entsprechende Pull Requests.

[einzustimmen, da ich die ursprüngliche Ursache für #538 war, es ist eine Bereinigung meiner #526]
Macht sehr viel Sinn ... der Meta-Ansatz würde in der Tat mit der Ausschlussliste für ModelResource usw.

Wie ich in einem anderen Ticket sagte, wäre eine "einfache" Lösung wie diese IMHO für ein 1.0-Release ausreichend... .

@funkybob Einverstanden, sicherlich wäre die komplexere Lösung von Clientseite aus hilfreich, aber es wäre schön, diese Funktionalität so schnell wie möglich aufzunehmen, damit sie vor der Veröffentlichung von 1.0 verwendet werden kann.

Ich verwende die PR tatsächlich in einer Produktionsanwendung, und ich mag die Flexibilität des Rückrufs von #538 wirklich. Ich habe einige Anwendungsfälle, in denen ich eine Ressource zur Laufzeit basierend auf der Berechtigung ausblenden muss.

Das wäre mir mit #569 nicht möglich

Hallo, irgendwelche Neuigkeiten? Diese PR macht das Leben so einfacher, danke!

Geh mit Hack von @dericcrago

+1

Eine einfache Problemumgehung, die ich verwende, besteht darin, die Methoden get_detail & get_list zu überschreiben, um solche Felder zu bearbeiten.
Dies spart den Aufwand für das tatsächliche Abrufen der Daten für das Feld und das anschließende Löschen aus dem Bundle, aber ich bin mir nicht sicher, ob diese Methode threadsicher ist, da die Resource-Objekte anscheinend nicht bei jedem API-Aufruf erstellt werden.
Es wäre toll, wenn sich jemand dazu äußern könnte.

Hier ist der Code:

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

Eine andere Problemumgehung besteht darin, unterschiedliche Ressourcen für Detail- und Listenansichten bereitzustellen:

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

+1 für @dnozay-Problemumgehung

+1 @dnozay , toll

Beachten Sie, dass Sie Folgendes hinzufügen müssen, nachdem UserResource definiert wurde, damit get_resource_uri korrekt mit der Benutzerdetailansicht funktioniert.

UserResourceDetail.get_resource_uri = UserResource().get_resource_uri

Andernfalls ist resource_uri in allen Detailantworten leer.

Möglicherweise verwandt: #1265

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen