can't send post data to api in django - python

i can't send post data to api using django rest framework. i used postman to send data only user part is adding to the database , the activity of user is rejecting . i can't figure out problem can anyone help me to solve this problem
sending post data to api
{
"name": "karen",
"username": "karen",
"timezone": "US/Samoa",
"activity_periods": [
{
"log_in": "2020-06-09T21:53:25.120897Z",
"log_out": null
},
{
"log_in": "2020-06-09T22:02:35.289891Z",
"log_out": null
},
{
"log_in": "2020-06-09T22:03:36.425212Z",
"log_out": null
}
]
}
but only the user data is stored the activity is ignored
like this
{
"name": "karen",
"username": "karen",
"timezone": "US/Samoa",
"activity_periods": []
}
how can i add activity data to user...?
models.py
class User(models.Model):
name = models.CharField(max_length=20)
username = models.CharField(max_length=20)
password = models.CharField(max_length=20)
timezone = models.CharField(max_length=32, choices=TIMEZONES, default='UTC')
def __str__(self):
return self.name
class Activity(models.Model):
user = models.ForeignKey(User, related_name="activity_periods",on_delete=models.CASCADE,null=True, blank=True)
log_in = models.DateTimeField(null=True, blank=True)
log_out = models.DateTimeField(null=True, blank=True)
def __str__(self):
return self.user.name
serializers.py
class ActivitySerializer(serializers.ModelSerializer):
class Meta:
model = Activity
fields = ['log_in', 'log_out']
class UserSerializer(serializers.ModelSerializer):
# Passing login Logout to User
activity_periods = ActivitySerializer(many=True, read_only=True)
class Meta:
model = User
fields = ['name', 'username','timezone', 'activity_periods']
views.py
class ActivityListView(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
name = "activity-list"
urls.py
path('rest/',views.ActivityListView.as_view())
how can i add activity data to user...?

In your UserSerializer, you have the following line:
activity_periods = ActivitySerializer(many=True, read_only=True)
Since read_only is set to True, when you POST data it will not be written to the database. Try setting it to False instead.

Related

Get user's last message django-postman & Rest API

I've been install Django-Postman user to user messaging package. I'm trying to get user's last message with Rest API.
You can check django-postman package on here: https://pypi.org/project/django-postman/
A part of models.py
class Message(models.Model):
"""
A message between a User and another User or an AnonymousUser.
"""
SUBJECT_MAX_LENGTH = 120
subject = models.CharField(_("subject"), max_length=SUBJECT_MAX_LENGTH)
body = models.TextField(_("body"), blank=True)
sender = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='sent_messages',
null=True, blank=True, verbose_name=_("sender"))
recipient = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='received_messages',
null=True, blank=True, verbose_name=_("recipient"))
sent_at = models.DateTimeField(_("sent at"), default=now)
objects = MessageManager()
Views.py
class InboxLastMessagesViewSet(viewsets.ModelViewSet):
serializer_class = InboxLastMessagesSerializer
authentication_classes = (JSONWebTokenAuthentication,)
permission_classes = (IsAuthenticated,)
def get_queryset(self):
user = self.request.user
return Message.objects.filter(Q(sender=user) | Q(recipient=user)).order_by('sender')
Serializers.py
class InboxLastMessagesSerializer(serializers.ModelSerializer):
senderusername = serializers.CharField(source='sender.username', read_only=True)
reciusername = serializers.CharField(source='recipient.username', read_only=True)
sonmesaj = serializers.SerializerMethodField()
def get_lastmessage(self, obj):
/// I'M TRYING TO CREATE A FUNCTION ON HERE FOR GET SENDER'S LAST MESSAGE ////
lastmsg = obj.latest('sent_at')
return dict(body=lastmsg)
class Meta:
model = Message
fields = ('senderusername', 'reciusername', 'body', 'sent_at', 'lastmessage')
I want to an output like this:
{
"senderusername": "user",
"reciusername": "user2",
"body": "Actually that is not last message",
"sent_at": "2019-01-19T23:08:54Z",
"lastmessage": {
"body": "That's conversation's last message!"
}
},
{
"senderusername": "user",
"reciusername": "user2",
"body": "I said that is not last message",
"sent_at": "2021-05-10T23:09:42Z",
"lastmessage": {
"body": "That's conversation's last message!"
}
},
You can get the last message like this:
def get_lastmessage(self, obj):
lastmsg = Message.objects.latest('sent_at')
data = {'senderusername': obj.senderusername,
'reciusername': obj.reciusername,
'body': lastmsg.body
'sent_at': lastmsg.sent_at
'last_message': {
'body': lastmsg.body
}
}
return data

Python Django Rest Framework: The `.create()` method does not support writable nested fields by default

I am using django rest framework to create an api endpoint. I am using the default user model django offers. I need to create a post which uses the user as a foreign key. A user called "author" in the post can have multiple posts.
This is an example of a post json.
[
{
"author": {
"id": 1,
"username": "sorin"
},
"title": "First Post",
"description": "Hello World!",
"created_at": "2020-08-05T14:20:51.981163Z",
"updated_at": "2020-08-05T14:20:51.981163Z"
}
]
This is the model.
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
This is the serializer.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username')
class PostSerializer(serializers.HyperlinkedModelSerializer):
author = UserSerializer()
class Meta:
model = Post
fields = ('author', 'title', 'description', 'created_at', 'updated_at')
I am getting the error "The .create() method does not support writable nested fields by default." when trying to make a post request using a "username", "title" and "description".
Any help to how to solve this?
I like hooking in the create function of the serializer for these kind of use cases.
Make sure your UserSerializer is set to read_only=True.
class PostSerializer(serializers.HyperlinkedModelSerializer):
author = UserSerializer(read_only=True)
class Meta:
model = Post
fields = ('author', 'title', 'description', 'created_at', 'updated_at')
def create(self, validated_data):
request = self.context['request']
author_data = request.data.get('author')
if author is None or not isinstance(author.get('id'), int):
raise ValidationError({'author': ['This field is invalid.']})
author_instance = get_object_or_404(User, id=author.get('id'))
return Post.objects.create(author=author_instance, **validated_data)

Queryset in one to many relation

I am trying to get a Json of elements with their related elements
I had two tables, Service and Room. One service have many rooms. I would like to get the service where have room_id = x.
Models
class Service(models.Model):
name = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = True
db_table = 'Service'
class Room(models.Model):
name = models.CharField(max_length=255, blank=True, null=True)
service = models.ForeignKey(Service, models.DO_NOTHING, blank=True,
null=True)
class Meta:
managed = True
db_table = 'Room'
Serializer
class ServiceSerializer(serializers.ModelSerializer):
room_set = RoomSerializer(many=True, read_only=True)
class Meta:
model = Service
fields = ('name','room_set')
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
fields = '__all__'
View
queryset = Service.objects.filter(room__id=1)
serializer = ServiceSerializer(queryset, many=True)
return JsonResponse(serializer.data, safe=False)
I expect a Json like this:
{
"name": "Hotel1",
"room_set": [
{
"id": 1,
"name": "Room1"
},
But I get this:
{
"name": "Hotel1",
"room_set": [
{
"id": 1,
"name": "Room1",
},
{
"id": 2,
"name": "Room2",
},
{
"id": 3,
"name": "Room3",
}
}
Is it possible to get a json like the one I'm expecting?
You can patch the set by adding a custom Prefetch object [Django-doc] with a filtered queryset, like:
from django.db.models import Prefetch
queryset = Service.objects.filter(
room__id=1
).prefetch_related(
Prefetch('room_set', queryset=Room.objects.filter(id=1), to_attr='room_set1')
)
serializer = ServiceSerializer(queryset, many=True)
return JsonResponse(serializer.data, safe=False)
and let the Serializer parse the new related manager:
class ServiceSerializer(serializers.ModelSerializer):
room_set = RoomSerializer(many=True, read_only=True, source='room_set1')
class Meta:
model = Service
fields = ('name','room_set1')
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
fields = '__all__'
You can pass the room id via the serializer context and filter accordingly inside a SerializerMethodField()
class ServiceSerializer(serializers.ModelSerializer):
rooms = serializers.SerializerMethodField()
class Meta:
model = Service
fields = ('name','rooms')
get_rooms(self,service):
room_id = self.get_context('room')
if room_id:
queryset = service.rooms_set.filter(id=room_id)
return RoomSerializer(queryset,many=True).data
return RoomSerializer(service.rooms_set.all(),many=True).data
serializer = ServiceSerializer(queryset, many=True,context={'room':1})
return JsonResponse(serializer.data, safe=False)
This's how to do it via the serializer and it's highly customizable , Willem Van Onsem's answer is brief enough , but it also requires two queries the same as of mine.

Django Rest Framework: Patching a OneToOneField

I need help with a PATCH request using Django rest framework.
I have a User model that inherits from AbstractBaseUser which has 2 fields: name and email. The email field is unique.
Then I have a DojoMaster model that has a OneToOne relationship with the User model:
models.py
class DojoMaster(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
phone = models.BigIntegerField()
username = models.CharField(max_length=100, unique=True)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
I am able to POST a "dojomaster" to the application. Let's say the "dojomaster" POST looks like this:
POST payload
{
"user": {
"name": "XYZ",
"email": "xyz#mail.com",
"password": "2He$8Mv*"
},
"phone": 2685211,
"country": 575,
"username": "iAmXyZ"
}
Now the "dojomaster" wants to change some of these details, so a PATCH request is sent:
PATCH payload
{
"user": {
"name": "XYZ",
"email": "xyz24#mail.com", #change email
"password": "p#55w0rd" #change password
},
"phone": 3972925, #change phone number
"country": 575,
"username": "iAmXyZ"
}
To achieve this, I created the following in my serializers.py:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('name', 'email', 'password')
class DojoMasterUpdateSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
def update(self, instance, validated_data):
user_data = validated_data.pop('user')
user = User.objects.update(**user_data)
instance.user = validated_data.get('user', user)
instance.username = validated_data.get('username', instance.username)
instance.phone = validated_data.get('phone', instance.phone)
instance.country = Country.objects.get(
country=validated_data.get('country', instance.country)
)
instance.save()
return instance
class Meta:
model = DojoMaster
fields = ('user', 'country', 'phone', 'username')
write_only_fields = ('password',)
To use the serializers, I created the following view:
views.py
class DojoMasterUpdateView(generics.UpdateAPIView):
queryset = DojoMaster.objects.all()
serializer_class = DojoMasterUpdateSerializer
However, when I do this, I get a Status: 400 Bad Request error with the following payload:
{
"user": {
"email": [
"user with this email already exists."
]
}
}
I also tried doing with using a PUT request to no success.
How do I PATCH an entity with this type of OneToOneField relationship using DRF? Your help will be much appreciated.
This question is old and you might have already found your answer.
However, if anybody wonders:
the issue is this:
user = User.objects.update(**user_data)
With this, you are not updating one instance of user - you are trying to update all users in the User table with user_data.
Since you are patching the instance of DojoMaster, you should already have the instance of user which belongs to it:
So, your update method can look like this instead:
def update(self, instance, validated_data):
user_data = validated_data.pop('user')
user_ser = UserSerializer(instance=instance.user, data=user_data)
if user_ser.is_valid():
user_ser.save()
instance.username = validated_data.get('username', instance.username)
instance.phone = validated_data.get('phone', instance.phone)
instance.save()
return instance

Django REST Framework nested serializer FK creation

I'm in trouble creating a bunch of related models using DRF nested serializers.
They are failing validation on the foreign key.
Models
class Employee(models.Model):
user = models.OneToOneField(User) # Django user
...
class Task(models.Model):
author = models.ForeignKey(Employee, related_name='tasks')
title = models.CharField(max_length=64)
...
class EmployeeTarget(models.Model):
employee = models.ForeignKey(Employee, null=False)
task = models.ForeignKey(Task, null=False, related_name='employee_targets')
...
Objective
Basically I have the Employees already created, and I want to create a Task and related EmployeeTarget in a single request, getting the request user as the author. JSON request example:
{
"title": "Lorem Ipsum",
"employee_targets": [
{ "employee": 10 },
{ "employee": 11 }]
}
/* or */
{
"title": "Lorem Ipsum",
"employee_targets": [10,11]
}
Serializers
class EmployeeSerializer(serializers.ModelSerializer):
name = serializers.CharField(source="user.get_full_name", read_only=True)
email = serializers.CharField(source="user.email", read_only=True)
class Meta:
model = Employee
class EmployeeTargetSerializer(serializers.ModelSerializer):
employee = EmployeeSerializer()
class Meta:
model = EmployeeTarget
class TaskSerializer(base.ModelSerializer):
employee_targets = EmployeeTargetSerializer(many=True, required=False)
class Meta:
model = Task
def create(self, validated_data):
employee_target_data = validated_data.pop('employee_targets')
task = Task.objects.create(**validated_data)
EmployeeTarget.objects.create(task=task, **employee_target_data)
return task
ViewSet
class TaskViewSet(ModelViewSet):
serializer_class = TaskSerializer
def get_queryset(self):
request_employee = self.request.user.employee
return Task.objects.filter(Q(author=request_employee) |
Q(employee_targets__employee=request_employee))
def perform_create(self, serializer):
serializer.save(author=self.request.user.employee)
Result
I'm getting 400 BAD REQUEST with the following error:
{
"employee_targets": [
{
"employee": {
"non_field_errors": ["Invalid data. Expected a dictionary, but got int."]
},
"task": ["This field is required."]
}
],
"author": ["This field is required."]
}
The employee error was expected, but I haven't figured out how to create them using only the ID.
The bigger problem here is the employee_targets failing validation at the task FK, before the enclosing TaskSerializer specify them at create method.
Can you try with this:
class EmployeeSerializer(serializers.ModelSerializer):
name = serializers.CharField()
email = serializers.CharField()
class Meta:
depth = 2
model = Employee

Categories

Resources