Je suis assez nouveau dans Django restframework, ce que j'essaye maintenant est de renvoyer l'objet avec une clé étrangère.
{ "collection": { "data": { "id": 31, "source_id": "55", "latitude": "24654", "longitude": "454654", "date_created": "2019-02-08T17:10:09.318644Z", "date_modiefied": "2019-02-08T17:10:09.318714Z", "area": "54546", "user": { "id": 1, "name": "Dormy", "date_created": "1992-01-18T03:29:53.388000Z", "date_modiefied": "2018-02-19T05:17:00.164000Z", "serverTime": "", "fcmTokenId": "" } }, "statusCode": 200, "version": "1.0" }
J'utilise def get_queryset (self):
class SyncIndexLastDataViewSet(viewsets.ModelViewSet): serializer_class = LocationDataSerializer def get_queryset(self): userid = self.request.query_params.get('user_id', None) userExist = User.objects.filter(id=userid) if userExist.exists(): # call the original 'list' to get the original response queryset = LocationData.objects.values('source_id').filter(user__id=userid).order_by('-source_id')[:1] lastSourceId = queryset[0]['source_id'] response = {"collection": {"data": lastSourceId,"statusCode": status.HTTP_200_OK,"version":"1.0"}} json = JSONRenderer().render(response) # customize the response data if response is not None: return json else: # return response with this custom representation response = {"collection": {"data": "","statusCode":status.HTTP_404_NOT_FOUND,"version":"1.0","error":"Not found"}} return response
Pour le moment, le résultat est à l'intérieur de la réponse est ci-dessous et immédiatement il lance cette erreur
Mais je veux que cet ensemble de requêtes retourne comme ci-dessous, donc je peux lire ceux valeurs de paire de clés dans Android
class User(models.Model): name = models.CharField(max_length=255,blank=True) date_created = models.DateTimeField(auto_now_add=True) date_modiefied = models.DateTimeField(auto_now=True) area = models.CharField(max_length=255,blank=True) uuid = models.CharField(max_length=255) home = models.CharField(max_length=255,blank=True) work = models.CharField(max_length=255,blank=True) mobileNo = models.CharField(max_length=255,blank=True) email = models.CharField(max_length=255,blank=True) appVersionCode = models.CharField(max_length=255,blank=True) photoUrl = models.CharField(max_length=255,blank=True) serverTime = models.CharField(max_length=255,blank=True) fcmTokenId = models.CharField(max_length=255,blank=True) def __str__(self): return self.name class LocationData(models.Model): user = models.ForeignKey( User, related_name='user', on_delete=models.DO_NOTHING) source_id = models.CharField(max_length=255) latitude = models.CharField(max_length=255) longitude = models.CharField(max_length=255) speed = models.CharField(max_length=255) kms = models.CharField(max_length=255) date_created = models.DateTimeField(auto_now=True) date_modiefied = models.DateTimeField(auto class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = '__all__' class LocationDataSerializer(serializers.ModelSerializer): class Meta: model = LocationData fields = '__all__' depth = 1
Maintenant, l'erreur se produit
AttributeError: Obtenu AttributeError lors de la tentative d'obtention d'une valeur pour le champ
source_id
sur le sérialiseurLocationDataSerializer
. Le champ du sérialiseur peut être nommé de manière incorrecte et ne correspondre à aucun attribut ou clé de l'instanceint
. Le texte de l'exception d'origine était: l'objet 'int' n'a pas d'attribut 'source_id'.
Merci !
4 Réponses :
La réponse à cela dépend du type de vue que vous utilisez, mais l'essentiel est que vous ne le faites pas dans get_queryset
, vous le faites dans la méthode du type de demande.
Par exemple, si vous utilisez un RetrieveAPIView a> vous devez remplacer la méthode retrieve
de RetrieveModelMixin comme ceci:
class MyAPIView(RetrieveAPIView): queryset = MyModel.objects.all() serializer_class = MySerializer def retrieve(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance) data = { "collection": { "data": serializer.data }, "statusCode": 200, "version": "1.0" } return Response(data)
Si vous utilisez autre chose comme un ListAPIView, vous voulez voir ce qui est utilisé par cela dans le et remplacez-la pour envelopper vos données.
La chose principale à réaliser ici est que cela n'a rien à voir avec l'obtention du jeu de requêtes - qui consiste simplement à obtenir des données de la base de données. Il s'agit de transformer les données dans le format correct lors du renvoi d'une réponse. En conséquence, le travail doit être effectué au moment où la réponse est faite.
Que diriez-vous du queryset = LocationData.objects.filter (user__id = userid) .order_by ('- sour ce_id') [: 1]? J'ai besoin de filtrer l'ensemble de requêtes et de fournir ces données à la réponse!
Vous pouvez soit filtrer dans get_queryset
, soit appeler get_queryset
dans la méthode que j'ai mentionnée dans ma réponse et filtrer la valeur qu'elle renvoie.
un exemple serait génial !, je ne trouve aucun indice pour faire ça!
Il existe deux solutions possibles à ce problème. NDevox mentionne déjà comment nous pouvons écraser notre fonction retrive
et obtenir notre réponse attendue. Mais si nous voulons que cela soit fait avec chaque réponse pour chaque point de terminaison api
et si nous procédons de cette façon, nous devons écraser chaque fonction, alors son lourd fardeau et son DRY
nous devons éviter cela autant que possible. Un des moyens possibles d'introduire un middleware ou d'écraser la Réponse
afin que nous puissions obtenir notre réponse générique pour chaque point de terminaison api sans écraser explicitement chaque fonctionnalité.
Comme nous utilisons DRF
ici, nous pouvons ajouter nos propres réponses de retour avec différents types de médias, par exemple pour application / json
.
Nous devons d'abord ajouter dans nos settings.py
from collections import OrderedDict class OurParentViewset(serializer.ModelSerializer): ...... def to_representation(self, instance): data = super(serializers.ModelSerializer, self).to_representation(instance) result = OrderedDict() result['data'] = data result['version'] = '1.0' result['statusCode'] = '2xx' # i am not fully sure how to customize this return result class A(OurParentViewset): ........ class B(OurParentViewset): ........ class C(OurParentViewset): ........
Et dans notre middleware de rendu personnalisé
class A(serializer.ModelSerializer): ........ class B(serializer.ModelSerializer): ........ class C(serializer.ModelSerializer): ........
Référence Lien
Si nous utilisons ModelViewset
, il existe un autre moyen d'y parvenir. Dites que nos vues.py sont comme suit
from rest_framework.renderers import BaseRenderer from rest_framework.utils import json class ApiRenderer(BaseRenderer): def render(self, data, accepted_media_type=None, renderer_context=None): our_response_dict = { 'version': '1.0' 'data': {}, 'message': '', } if data.get('data'): our_response_dict['data'] = data.get('data') if data.get('status'): our_response_dict['statusCode'] = data.get('status') if data.get('message'): our_response_dict['message'] = data.get('message') data = our_response_dict return json.dumps(data)
Notre objectif est d'écraser la fonction to_representation de ModelViewset
et de renvoyer notre résultat personnalisé. Ce sera comme suit
REST_FRAMEWORK = { ... 'DEFAULT_RENDERER_CLASSES': ( 'app_name.renderers.ApiRenderer', # our own render middleware ), ... }
@Tot monsieur, vous ne pouvez pas simplement changer la définition / réponse de quest_set. Comme vous utilisez Implémenter un moteur de rendu personnalisé ici semble être une solution. Vous pouvez demander à votre client Android d'inclure dans l'en-tête Ensuite, composez un moteur de rendu en utilisant la classe Ceci peut ensuite être utilisé lorsque vous avez besoin d'une réponse formatée de cette façon en utilisant l'attribut ModelViewset
, veuillez passer par RetrieveModelMixin
votes
Accept
un moyen d'identifier le client auprès du moteur de rendu. 1 eg# ./formatters/android_format.py
from rest_framework.renderers import JSONRenderer, BaseRenderer
from django.http.multipartparser import parse_header
class AndroidV1FormatRenderer(BaseRenderer):
media_type = 'application/json'
format = 'json'
json_renderer = JSONRenderer()
def android(self, accepted_media_type):
base_media_type, params = parse_header(accepted_media_type.encode('ascii'))
return 'android' in params
def render(self, data, accepted_media_type=None, renderer_context=None):
response = renderer_context['response']
android = self.android(accepted_media_type)
if android:
data = {
"collection": {"data": data},
"statusCode": response.status_code,
"version": "1.0"
}
return json_renderer.render(
wrapped_data, accepted_media_type, renderer_context)
JSONRenderer
pour fournir le format de votre client Android. Accept: application/json; android=true
renderer_classes
de votre APIView
. 2 sup >
Puisque get_queryset ne vous permettra pas de personnaliser les données de réponse. Je décide de prendre la valeur de requête qui est importante pour moi.
http: // localhost / api / users /? user_id = 1 a > -> changé en ... api / users / 1
def retrieve(self, request, *args, **kwargs): """ userid = self.request.query_params.get('user_id', None) """ userid = kwargs.get('pk') userExist = User.objects.filter(id=userid) if userExist.exists(): # call the original 'list' to get the original response queryset = LocationData.objects.values('source_id').filter(user__id=userid).order_by('-source_id')[:1] lastSourceId = queryset[0]['source_id'] response = {"collection": {"data": lastSourceId,"statusCode": status.HTTP_200_OK,"version":"1.0"}} # customize the response data if response is not None: return Response(response) else: # return response with this custom representation response = {"collection": {"data": "","statusCode":status.HTTP_404_NOT_FOUND,"version":"1.0","error":"Not found"}} return response
Donc, quelques conseils. 1) Vous devez mettre à jour votre docstring, actuellement il ne s'agit que d'un exemple de code sans rapport avec la méthode. 2) la réponse ne peut jamais être aucune telle que vous la définissez immédiatement au-dessus, donc si la réponse n'est pas Aucune
est redondant. 3) vous ne devriez vraiment pas renvoyer le code d'état dans le corps de la réponse. Vous devez définir le statut dans la réponse comme return Response (response, status = 404)
.
Oui, pris soin de toutes les choses énoncées !. Merci
Voulez-vous toute votre réponse de cette manière ou simplement un type spécial de demande - réponse?
Par la manière ci-dessus, je l'ai mentionné!
vous pouvez ajouter une annotation dans votre requête. Grâce à cela, vous pouvez ajouter un champ personnalisé à votre requête
@Sarang avez-vous un échantillon! Veuillez le poster
docs.djangoproject.com/en/2.1/ref/models/querysets / # annoter vérifier ce lien. Il existe de nombreuses fonctionnalités que vous pouvez utiliser avec annotate.
J'ai mis à jour le problème et plus de code