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?
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
Vous pouvez utiliser CurrentUserDefault:
user = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
Voir http://www.Django-rest-framework.org/api-guide/validators/#currentuserdefault
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)
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)
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.
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
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
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',
)