web-dev-qa-db-fra.com

Comment définir l'utilisateur actuel en champ d'utilisateur dans Django Rest Framework?

J'ai le code suivant fonctionne parfaitement. Je peux créer un objet Post à partir du panneau DRF en sélectionnant une image et un utilisateur. Cependant, je souhaite que DRF remplisse le champ utilisateur par l'utilisateur actuellement connecté.

models.py

class Post(TimeStamped):
    user = models.ForeignKey(User)
    photo = models.ImageField(upload_to='upload/')
    hidden = models.BooleanField(default=False)
    upvotes = models.PositiveIntegerField(default=0)
    downvotes = models.PositiveIntegerField(default=0)
    comments = models.PositiveIntegerField(default=0)

serializers.py

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'user', 'photo']

views.py

class PhotoListAPIView(generics.ListCreateAPIView):
    queryset = Post.objects.filter(hidden=False)
    serializer_class = PostSerializer
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)

Comment puis-je faire ceci?

29
MiniGunnR

De mémoire, vous pouvez simplement remplacer la méthode perform_create():

class PhotoListAPIView(generics.ListCreateAPIView):
    ...
    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

Donnez un coup de feu et laissez-moi savoir si cela fonctionne

34
DaveBensonPhillips

Vous pouvez utiliser CurrentUserDefault:

user = serializers.PrimaryKeyRelatedField(
    read_only=True, 
    default=serializers.CurrentUserDefault()
)

Voir http://www.Django-rest-framework.org/api-guide/validators/#currentuserdefault

48
Marc Gibbons

Cela dépend de votre cas d'utilisation. Si vous voulez qu'il soit "en écriture seule", cela signifie que DRF remplit automatiquement le champ en écriture et ne renvoie pas la lecture, l'implémentation la plus simple selon la documentation serait avec un HiddenField:

class PhotoListAPIView(generics.ListCreateAPIView):
    user = serializers.HiddenField(
        default=serializers.CurrentUserDefault(),
    )

Si vous voulez qu'il soit lisible, vous pouvez utiliser un champ PrimaryKeyRelatedField tout en veillant à ce que votre sérialiseur préremplisse le champ en écriture. Dans le cas contraire, un utilisateur peut définir le champ user de sorte qu'il pointe vers un autre utilisateur aléatoire.

class PhotoListAPIView(generics.ListCreateAPIView):
    user = serializers.PrimaryKeyRelatedField(
        # set it to read_only as we're handling the writing part ourselves
        read_only=True,
    )

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

Enfin, notez que si vous utilisez la variable plus détaillée APIView au lieu de generics.ListCreateAPIView, vous devez écraser create au lieu de perform_create comme suit:

class PhotoListAPIView(generics.ListCreateAPIView):
    user = serializers.PrimaryKeyRelatedField(
        read_only=True,
    )

    def create(self, validated_data):
        # add the current User to the validated_data dict and call
        # the super method which basically only creates a model
        # instance with that data
        validated_data['user'] = self.request.user
        return super(PhotoListAPIView, self).create(validated_data)
10
olieidel

La réponse de @ DaveBensonPhillips peut fonctionner dans votre cas particulier pendant un certain temps, mais elle n’est pas très générique car elle rompt la chaîne d’héritage OOP.

ListCreateAPIView hérite de CreateModelMixin qui enregistre déjà le sérialiseur. Vous devez toujours vous efforcer d’exécuter la chaîne complète des méthodes remplacées, à moins que vous n’ayez une très bonne raison de ne pas le faire. Ainsi, votre code reste DRY et robuste contre les modifications:

class PhotoListAPIView(generics.ListCreateAPIView):
    ...
    def perform_create(self, serializer):
        serializer.validated_data['user'] = self.request.user
        return super(PhotoListAPIView, self).perform_create(serializer)
4
Jozef

Vous devrez remplacer le comportement par défaut de la façon dont generics.ListCreateAPIView crée un objet.

class PhotoListAPIView(generics.ListCreateAPIView):
    queryset = Post.objects.filter(hidden=False)
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)

    def get_serializer_class(self):
        if self.request.method == 'POST':
            return CreatePostSerializer
        else:
            return ListPostSerializer

    def create(self, request, *args, **kwargs):
        # Copy parsed content from HTTP request
        data = request.data.copy()

        # Add id of currently logged user
        data['user'] = request.user.id

        # Default behavior but pass our modified data instead
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

La .get_serializer_class() n’est pas nécessaire car vous pouvez spécifier les champs en lecture seule de votre sérialiseur, mais en fonction des projets sur lesquels j’ai travaillé, je me retrouve généralement avec des sérialiseurs «asymétriques», c’est-à-dire différents sérialiseurs en fonction de l’opération souhaitée.

2
Wannabe Coder

Essaye ça:

def post(self, request, format=None)

        serializer = ProjectSerializer(data=request.data)
        request.data['user'] = request.user.id


        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST
1

C'est ce qui fonctionne pour moi dans serializers.py, où j'utilise également des données imbriquées. Je souhaite afficher created_by_username sans avoir à rechercher d'autres utilisateurs.

class ListSerializer(serializers.ModelSerializer):
    """
    A list may be created with items
    """
    items = ItemSerializer(many=True)

    # automatically set created_by_id as the current user's id
    created_by_id = serializers.PrimaryKeyRelatedField(
        read_only=True,
    )

    created_by_username = serializers.PrimaryKeyRelatedField(
        read_only=True
    )


    class Meta:
        model = List
        fields = ('id', 'name', 'description', 'is_public',
            'slug', 'created_by_id', 'created_by_username', 'created_at',
            'modified_by', 'modified_at', 'items')

    def create(self, validated_data):
        items_data = validated_data.pop('items', None)
        validated_data['created_by_id'] = self.context['request'].user
        validated_data['created_by_username'] = self.context['request'].user.username
        newlist = List.objects.create(**validated_data)

        for item_data in items_data:
            Item.objects.create(list=newlist, **item_data)
        return newlist
0
Little Brain

Vous pouvez éviter de transmettre la variable user dans votre demande et vous ne la verrez pas dans la sortie, mais DRF la remplira automatiquement:

from rest_framework import serializers

class MyModelSerializer(serializers.ModelSerializer):
    user = serializers.HiddenField(default=serializers.CurrentUserDefault())

    class Meta:
        model = models.MyModel
        fields = (
            'user',
            'other',
            'fields',
        )
0
Babken Vardanyan