Как сделать POST-запрос и использовать ID автора?
У меня есть эта модель Game
и у меня есть рабочий GET
метод и я хотел бы создавать новые Game
объекты в моей базе данных с помощью POST запроса.
У меня Game
вот так:
class Game(models.Model):
owner = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)
name = models.CharField(max_length=255)
...
У меня есть такой вид:
class GameDetail(APIView):
def get_object(self, game_slug):
try:
return Game.objects.filter(slug=game_slug)
except Game.DoesNotExist:
raise Http404
def get(self, request, game_slug, format=None):
game = self.get_object(game_slug).first()
serializer = GameSerializer(game)
return Response(serializer.data)
def post(self, request, format=None):
game_data = request.data
game_data['owner'] = { 'username': 'admin', 'email': 'admin@example.com'} // just to test
serializer = GameSerializer(data=game_data)
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)
Если я посылаю серверу POST-запрос со следующим телом:
{
"name": "Posted game",
... // ommiting owner field
}
Похоже, что сервер думает, что я хочу создать нового пользователя, но я просто хочу, чтобы игра была связана с userId
{
"owner": {
"username": [
"user with this username already exists."
]
}
}
Если вместо этого я устанавливаю game_data['owner']
непосредственно в идентификатор пользователя, он жалуется, что ожидает словарь.
Как я могу сделать так, чтобы когда я GET
играю, я видел владельца (имя пользователя и email) и когда я пишу сообщение, оно может просто добавить вошедшего пользователя?
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("username", "email")
class GameSerializer(serializers.ModelSerializer):
owner = UserSerializer()
class Meta:
model = Game
fields = (
"name",
"owner",
...
)
Иногда лучшим решением является создание двух сериализаторов и использование нужного для нужной конечной точки:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("username", "email")
class ReadGameSerializer(serializers.ModelSerializer):
owner = UserSerializer()
class Meta:
model = Game
fields = (
"name",
"owner",
...
)
class WriteGameSerializer(serializers.ModelSerializer):
class Meta:
model = Game
fields = (
"name",
"owner",
...
)
Затем обновите представление:
class GameDetail(APIView):
def get_object(self, game_slug):
try:
return Game.objects.filter(slug=game_slug)
except Game.DoesNotExist:
raise Http404
def get(self, request, game_slug, format=None):
game = self.get_object(game_slug).first()
serializer = ReadGameSerializer(game)
return Response(serializer.data)
def post(self, request, format=None):
game_data = request.data
game_data['owner'] = request.user # if the user is authenticated
serializer = WriteGameSerializer(data=game_data)
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)
Другим решением было бы сделать вложенный сериализатор записываемым, но по опыту это решение сложнее поддерживать.
Существует множество способов ассоциирования, Если вы хотите ассоциировать вошедшего пользователя с игрой, то передайте запрос в сериализаторе
...
def post(self, request, format=None):
serializer = GameSerializer(data=request.data, context={"request": requeset})
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)
Модифицируйте ваш сериализатор,
serializers.CharField(default=serializers.CurrentUserDefault())
это автоматически получит вашего пользователя из запроса и свяжет его с полем owner в Game
Model
Переопределите метод to_representation
для манипулирования данными ответа
class GameSerializer(serializers.ModelSerializer):
owner = serializers.CharField(default=serializers.CurrentUserDefault())
class Meta:
model = Game
fields = (
"name",
"owner",
...
)
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['owner'] = {"username":instance.owner.username, "email": instance.owner.email}
return representation