POST json array and get response as json array - django

my client is sending me a POST with a json-array and is awaiting a response with the complete details of the requested data. I have no problems with single requests and single responses, but to minimize the overhead, I'd like to process an array.
my models.py
class RoFile(models.Model):
user = models.ForeignKey('auth.User', null=True)
filename = models.CharField(max_length=120, null=True)
deleted = models.BooleanField(default=False)
info = models.CharField(max_length=120, null=True)
md5check = models.CharField(max_length=120, null=True)
one try of my serializer:
class RoFileSerializer(serializers.ModelSerializer):
deleted = serializers.ReadOnlyField(required=False)
user = serializers.ReadOnlyField(required=False)
info = serializers.ReadOnlyField(required=False)
class Meta:
model = RoFile
fields = (
'filename', 'md5check', 'deleted', 'user', 'info',
)
def create(self, validated_data):
return RoFile(**validated_data)
on try of my views:
#api_view(['POST'])
def rofile_detaillist(request, format=None):
data = JSONParser().parse(request)
serializer = RoFileSerializer(data=data, many=True)
if serializer.is_valid():
json_add = []
for x in serializer.validated_data:
try:
rofile = RoFile.objects.filter(md5check=x['md5check'])
except ObjectDoesNotExist:
continue
*invalid code here*
return Response(jsonarraywithallinfos)
else:
return Resonse(status=status.HTTP_400_BAD_REQUEST)
another view try:
class RoFileDetailList(viewsets.ModelViewSet):
model = RoFile
serializer_class = RoFileSerializer(many=True)
def get_queryset(self):
return Rofile.objects.filter(md5check=self.request.data['md5check'])
a POST example:
{"filename": "filename1.exe", "md5check": "f8541061779b1efc5c30c4783edfb8f8"},
{"filename": "filename2.exe", "md5check": "16cdac5eb0ec829c2e2c199488681f6e"}
what I need as a response back:
{"filename": "filename1.exe", "md5check": "f8541061779b1efc5c30c4783edfb8f8", user: "testuser1", deleted: "True", info: ""},
{"filename": "filename2.exe", "md5check": "16cdac5eb0ec829c2e2c199488681f6e", user: "testuser1", deleted: "False", info: ""}
Sorry for the invalid code part, but I have already tried so much, so I deleted that part (by accident).
Thank you!
EDIT:
I don't need to create with POST, I only need to retrieve additional data (the rest of the model). I had to change the create function in the serializer, because I don't want to create the entries, I only want to retrieve the data associated with the md5check from the db.
thanks to #zaphod100.10
my actual serializer:
class RoFileSerializer(serializers.ModelSerializer):
class Meta:
model = RoFile
fields = '__all__'
read_only_fields = ('deleted',)
def create(self, validated_data):
return RoFile(**validated_data)
my view:
class RoFileListDetailApi(generics.ListCreateAPIView):
serializer_class = RoFileSerializer
def get_queryset(self):
return RoFile.objects.filter(md5check=self.request.data['md5check'])
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_200_OK, headers=headers)
my post:
{"filename": "filename1.exe", "md5check": "f8541061779b1efc5c30c4783edfb8f8"},
{"filename": "filename2.exe", "md5check": "16cdac5eb0ec829c2e2c199488681f6e"}
my actual response is now a list but only with my POST-data and not the real data from the db:
{"filename": "filename1.exe", "md5check": "f8541061779b1efc5c30c4783edfb8f8", deleted: false, info: null, user: null},
{"filename": "filename2.exe", "md5check": "16cdac5eb0ec829c2e2c199488681f6e", deleted: false, info: null, user: null}
should be:
{"filename": "filename1.exe", "md5check": "f8541061779b1efc5c30c4783edfb8f8", deleted: true, info: "some info", user: "usertest1"},
{"filename": "filename2.exe", "md5check": "16cdac5eb0ec829c2e2c199488681f6e", deleted: false, info: "some info2", user: "usertest2"}

use this:
class RoFileSerializer(serializers.ModelSerializer):
class Meta:
model = RoFile
fields = '__all__'
read_only_fields = ('deleted', 'user', 'info')
class RoFileListCreateApi(generics.ListCreateAPIView):
serializer_class = RoFileSerializer
def get_queryset(self):
return Rofile.objects.filter(md5check=self.request.data['md5check'])
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
# override perform_create or the serializers create method for custom create logic
self.perform_create(serializer)
# assign other fields to the objs and save again
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
You just have to pass many=True to the serializer for handling lists.
Override perform_create method in the generic view or the serializers create method for applying custom creation logic.
EDIT:
based on new info provided I have changed the create method.
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
# don't create anything just insert required data
for rof_data in serializer.data:
md5check = rof_data['md5check']
# code to retrieve data from db based on md5check
....
# code to insert values in rof_data
rof_data['user'] = user.username
rof_data['deleted'] = deleted
rof_data['info'] = info
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

Related

Django .save() doesn't update on PUT request

I am trying to update a foreign-field with a PUT request on a resource. My serializer.data and the http response is correct after callig .is_valid, but the object doesn't get updated.
View
def put(self, request, user_pk):
try:
userById = getUserById(user_pk)
except ChatUser.DoesNotExist:
raise Http404
serializer = ChatUserInputSerializer(userById, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Serializer
class IdentificationInputSerializer(serializers.ModelSerializer):
class Meta:
model = Identification
fields = "__all__"
read_only_fields = ["id"]
class ChatUserInputSerializer(serializers.ModelSerializer):
identification = IdentificationInputSerializer()
class Meta:
model = ChatUser
fields = ["id", "userId", "identification"]
read_only_fields = ["id", "userId"]
def update(self, instance, validated_data):
identification = validated_data.pop('identification')
instance.identification.salutation = identification.get('salutation', instance.identification.salutation)
instance.identification.firstname = identification.get('firstname', instance.identification.firstname)
instance.identification.name = identification.get('name', instance.identification.name)
instance.identification.street = identification.get('street', instance.identification.street)
instance.identification.plz = identification.get('plz', instance.identification.plz)
instance.identification.city = identification.get('city', instance.identification.city)
instance.identification.country = identification.get('country', instance.identification.country)
instance.save()
return instance
I pretty sure that django won't save related models when you call .save().
Try instance.identification.save()

user: ["This field is required."] in DRF

I am trying to allow user to update the name of Lists they have created. However, when I attempt to POST the data I return the following error:
user: ["This field is required."]
I have racked my brain trying to solve this, hardcoded the username etc. but I keep turning up empty. I'd be grateful for some more expertise to assist me.
Here is my view:
class UpdateUserListViewSet(viewsets.ModelViewSet):
serializer_class = UserListSerializer
queryset = UserList.objects.all()
def update(self, instance, validated_data):
serializer_class = UserListSerializer
if self.request.method == "POST":
list_id = request.data.get('id')
user = UserList(user=self.request.user.id)
list_name = request.data.get('list_name')
data = {'id':int(list_id), 'list_name': list_name}
serializer = serializer_class(user, data=data)
if serializer.is_valid():
serializer.update()
return Response({'status' : 'ok'}, status=200)
else:
return Response({'error' : serializer.errors}, status=400)
And here is my serializer:
class UserListSerializer(serializers.ModelSerializer):
class Meta:
model = UserList
fields = ['id', 'user', 'list_name']
So actually you are trying to update with POST request, you have to check wheather your code had been reached inside this update function and if it is reaching there then you have to pass partial=True in update otherwise serailizer will try to validate all the required fields so for that you can change as :-
serializer = serializer_class(user, data=data, partial=True)

MultipartParser is not validating data in Django Rest Framework

I am using a multipart/form-data in a form which have a manytomany relation as well as multiple file upload. But the validated data doesn't contains the array data
Views.py
class ExpenseCreateAPIView(CreateAPIView):
permission_classes = (permissions.IsAuthenticated,)
parser_classes = ( MultiPartParser,)
def post(self, request, *args, **kwargs):
owner = request.user.pk
d = request.data.copy()
d['owner'] = owner
serializer = ExpenseSerializer(data=d)
print("exp")
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.py
class ExpenseSerializer(serializers.ModelSerializer):
transactions = ExpenseTransactionsSerializer(many=True, required=False)
bill = ExpenseFilesSerializer(many=True, required=False)
class Meta:
model = Expense
fields = "__all__"
def create(self, validated_data):
print("validated data", validated_data)
items_objects = validated_data.pop('transactions', None)
files_objects = validated_data.pop('bill', None)
prdcts = []
files = []
for item in items_objects:
i = ExpenseTransactions.objects.create(**item)
prdcts.append(i)
if files_objects == None:
pass
else:
for item in files_objects:
i = ExpenseFiles.objects.create(**item)
files.append(i)
instance = Expense.objects.create(**validated_data)
instance.transactions.set(prdcts)
instance.bill.set(files)
return instance
How else should I use the MultiPartParser class in the views ?
I keep getting the error:
TypeError: 'NoneType' object is not iterable
at
for item in items_objects:
Make sure you have transactions in validated_data. If transactions can empty or not required, just change:
validated_data.pop('transactions', None)
to
validated_data.pop('transactions', [])
It means that if transactions not in validated_data then pop [] (an empty list), instead of None, then empty list can keep iterate in next your code.
I think you need to try validated_data.get instead of validated_data.pop

Django REST Framework: How do you get the attributes from a serialized object?

I have a POST method which is going to be used to retrieve a JSON object, which is then going to be used to retrieve the first_name, last_name, and username -- although I can't figure out how to get the fields (i.e. username) after I serialize it. What's the best way to go about that?
views.py
#api_view(['POST'])
def createUser(request):
# Making a Connection w/ MongoClient
client = MongoClient('mongodb+srv://test_user:0FP33TLJVWrjl8Vy#cluster0.5sacp.mongodb.net/sample_clubs?retryWrites=true&w=majority')
# Getting the Database
db = client['sample_clubs']
# Getting the Collection/Table
collection = db['users']
serializer = MyUserSerializer(data=request.data)
# Gives bug if next 2 lines aren't here
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
serializers.py
class MyUserSerializer(serializers.ModelSerializer):
def get_first_name(self, obj):
# obj is model instance
return obj.first_name
def get_last_name(self, obj):
# obj is model instance
return obj.last_name
def get_user_name(self, obj):
# obj is model instance
return obj.user_name
class Meta:
model = MyUser
fields = ['first_name', 'last_name', 'username']
# fields = '__all__'
models.py
class MyUser(models.Model):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
username = models.CharField(max_length=200)
def __str__(self):
return self.username
A serializer's save method in DRF will return the instance that has been saved. So you can simply call any of its field like this:
if serializer.is_valid():
obj = serializer.save()
print(obj.user_name)
The data will also be available through the serializer's validated data:
if serializer.is_valid():
print(serializer.validated_data.get('user_name')
You can also use the raw JSON that's been generated by serializer:
# note that serializer.data won't be available if 'is_valid()` returns False
print(serializer.data["user_name"])
Also, you shouldn't return serializer.data outside of the is_valid scope. If is_valid() is False, then there won't be any data so you will run to an error. The proper way would be this:
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
If you only want to return the user_name as response:
if serializer.is_valid():
obj = serializer.save()
return Response({"username": obj.user_name})
return Response(serializer.errors)

Save multiple models in a single post - Django rest frameweok

I have 4 models
class User(AbstractEmailUser):
first_name = models.CharField(max_length=100, blank=True)
last_name = models.CharField(max_length=100, blank=True)
class Event(models.Model):
name = models.CharField(max_length=200)
address = models.CharField(max_length=200)
date = models.DateField()
class EventLocation(models.Model):
event = models.ForeignKey(Event)
ubigeo = ArrayField(models.CharField(max_length=200), blank=True)
class EventStaff(models.Model):
recycler = models.ForeignKey(User)
event = models.ForeignKey(Event)
When I want to register an event and be able to assign users to this same publication at the time of creation, assign users or do not assign them. I have already created a nested serialier that in the documentation is well explained so that the event is saved and at the same time it is saved in the ubigeo field of the EventLocation table (code of the district of the place):
Class EventLocationSerializer(serializers.ModelSerializer):
class Meta:
model = EventLocation
fields = ('id', 'ubigeo')
class EventSerializer(serializers.ModelSerializer):
event_location = EventLocationSerializer(required=True, write_only=True)
def to_representation(self, instance):
representation = super(EventSerializer, self).to_representation(instance)
event_location = EventLocation.objects.filter(event=instance.id)
if event_location:
representation['event_location'] = event_location.values('ubigeo')[0]
return representation
class Meta:
model = Event
fields = ('id', 'date', 'name', 'address', 'schedule', 'event_location')
def create(self, validated_data):
location_data = validated_data.pop('event_location')
event = Event.objects.create(**validated_data)
EventLocation.objects.create(event=event, **location_data)
return event
and it works correctly, but how would you add the users you want to assign to the event at the same time? I know I have to save them in the EventStaff table but how do I insert them in that same post?
This is my viewset:
#transaction.atomic
def create(self, request, *args, **kwargs):
with transaction.atomic():
try:
data = request.data
serializer = EventSerializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response({"status": True, "results": "Evento registrado correctamente"},
status=status.HTTP_201_CREATED)
except ValidationError as err:
return Response({"status": False, "error_description": err.detail}, status=status.HTTP_400_BAD_REQUEST)
This is the json format:
{
"date": "2018-03-01",
"name": "La prueba reciclaje",
"address": "Av espaƱa trujillo",
"users": [
{"id": 40, "first_name": "Raul"},
{"id": 23, "first_name": "ALejandro"}
],
"eventlocation": {
"ubigeo": ["130101"]
}
}
In my opinion, we can custom your def create a bit more.
So we create one Serializer for User, get params user and save it after Event saved.
Maybe like this:
#transaction.atomic
def create(self, request, *args, **kwargs):
with transaction.atomic():
try:
data = request.data
serializer = EventSerializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save()
// recheck , this loop have input is all users in json
for user in data.get('users'):
user_serializer = UserSerializer(data=user)
if user_serializer.is_valid(raise_exception=True):
user_serializer.save()
return Response({"status": True, "results": "Evento registrado correctamente"},
status=status.HTTP_201_CREATED)
except ValidationError as err:
return Response({"status": False, "error_description": err.detail}, status=status.HTTP_400_BAD_REQUEST)
Hoop this help
As I said in the commentary, it works wonderfully :D
#transaction.atomic
def create(self, request, *args, **kwargs):
with transaction.atomic():
try:
data = request.data
users = request.data.get('users', None)
serializer = EventSerializer(data=data)
if serializer.is_valid(raise_exception=True):
instance = serializer.save()
if users:
for user in users:
EventStaff.objects.create(recycler_id=user['id'], event_id=instance.id)
return Response({"status": True, "results": "Evento registrado correctamente"},
status=status.HTTP_201_CREATED)
except ValidationError as err:
return Response({"status": False, "error_description": err.detail}, status=status.HTTP_400_BAD_REQUEST)