Как установить текущее пользовательское поле в Django Rest Framework?
У меня следующий код работает отлично. Я могу создать объект Post
из панели DRF, выбрав изображение и пользователя. Однако я хочу, чтобы DRF заполнял пользовательское поле текущим пользователем.
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,)
Как я могу это сделать?
Ответы
Ответ 1
Сверху моей головы вы можете просто переопределить метод perform_create()
:
class PhotoListAPIView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Дайте мне выстрел и дайте мне знать, если он работает
Ответ 2
Вы можете использовать CurrentUserDefault:
user = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
См. Http://www.django-rest-framework.org/api-guide/validators/#currentuserdefault
Ответ 3
Это зависит от вашего варианта использования. Если вы хотите, чтобы это было "только для записи", то есть DRF автоматически заполняет поле при записи и не возвращает при чтении, самая простая реализация в соответствии с документами будет с HiddenField:
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault(),
)
Если вы хотите, чтобы он был читабельным, вы можете использовать PrimaryKeyRelatedField, следя за тем, чтобы ваш сериализатор предварительно заполнял поле при записи - в противном случае пользователь может установить поле user
, указывая на какого-то другого случайного пользователя.
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)
Наконец, обратите внимание, что если вы используете более подробный APIView
вместо generics.ListCreateAPIView
, вы должны перезаписать create
вместо perform_create
следующим образом:
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)
Ответ 4
Ответ @DaveBensonPhillips может работать в вашем конкретном случае в течение некоторого времени, но он не очень общий, так как он нарушает цепочку наследования ООП.
ListCreateAPIView
наследует от CreateModelMixin
который уже сохраняет сериализатор. Вы всегда должны стремиться к тому, чтобы полная цепочка переопределенных методов выполнялась, если у вас нет очень веской причины. Таким образом, ваш код остается сухим и надежным в отношении изменений:
class PhotoListAPIView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.validated_data['user'] = self.request.user
return super(PhotoListAPIView, self).perform_create(serializer)
Ответ 5
Вам придется переопределить поведение по умолчанию того, как generics.ListCreateAPIView
создает объект.
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)
Не .get_serializer_class()
, так как вы можете указать, какие поля доступны только для чтения из вашего сериализатора, но на основе проектов, над которыми я работал, обычно я получаю "асимметричные" сериализаторы, то есть разные сериализаторы в зависимости от предполагаемой операции,
Ответ 6
Вы можете избежать передачи user
в своем запросе и не увидите его в выводе, но DRF заполнит его автоматически:
from rest_framework import serializers
class MyModelSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = models.MyModel
fields = (
'user',
'other',
'fields',
)
Ответ 7
Попробуй это:
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
Ответ 8
Это то, что работает для меня в serializers.py, где я также использую вложенные данные. Я хочу отобразить созданное_пользовательское имя без необходимости поиска других пользователей.
class ListSerializer(serializers.ModelSerializer):
"""
A list may be created with items
"""
items = ItemSerializer(many=True)
# automatically set created_by_id as the current user 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
Ответ 9
Начиная с версии 3.8.0 DRF (обсуждение запроса на извлечение) вы можете переопределить функцию save() в сериализаторе.
from rest_framework import serializers
...
class PostSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Post
fields = ['id', 'user', 'photo']
def save(self, **kwargs):
"""Include default for read_only 'user' field"""
kwargs["user"] = self.fields["user"].get_default()
return super().save(**kwargs)