I am trying to post a list of objects with Axios but I can't get it to work. I am working with a React front-end and a Django (python) backend.
I want to POST data like this:
[
{
invitee: "..."
party: "..."
}, {
invitee: "..."
party: "..."
},
...
]
My first thought was to just take an Array as the data attribute of axios:
const res = await authAxios.post(`/conversations/invitations/`, toAPIArr);
I also tried to JSON.stringify the Array, but in the backend I always get an error saying that I am not sending a list. I am doing a simple check (this is python):
isinstance(request.data, list) --> always false
Is there something obvious that I am doing wrong here?
Edit: I am using django-rest-framewok on the backend:
View
class PartyList(APIView):
def post(self, request, format=None):
print(isinstance(request.data, list))
serializer = PartySerializer(data=request.data, many=True)
user = request.user
if serializer.is_valid():
instances = serializer.save(creator=user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Serializer
class PartySerializer(ModelSerializer):
creator = ReadOnlyField(source='creator.uuid')
class Meta:
model = Invitation
fields = (
'invitee',
'party',
)
read_only_fields = (
'uuid',
)
There was one answer to this question that actually got downvoted and deleted that yielded one working solution for me.
It is possible to send an object with axios:
const res = await authAxios.post(`/conversations/invitations/`, {list: toAPIArr});
From the backend it is then possible to access this list (this is python):
arr = request.data.get('list')
This is a bit of a hack but so far it works for me. If there is a right way (implying that this might be the wrong way) to do this maybe there will be someone who shares it.
Related
I am trying to make a music controller room with django backend and react frontend. These are running on two different localhost servers, 3000 and 8000. I am using django session's session_key attribute to be able to identify who the host(the person who created the room) is. If the user using the app in the frontend creates a room and comes back to create another room before the session expires, the user should be taken to the room they have created instead of having the backend create another room. My problem is that each time I hit the create room button on the frontend seconds after creating another room(obviously the session hasn't expired so I expect to be taken to the previous room), the fetch method returns a new room.
Here is a view in my views.py that handles this POST request:
class CreateRoomview(APIView):
# declaring the class we are going to serialize our data with.
serializer_class = CreateRoomSerializer
# manually defining the method we will use to handle post data from our frontend
def post(self, request, format=None):
'''
a check to see if in any of our rooms we have a room that that has a host with the
current session key. We are filtering out all those rooms that have a host with
the current session key.
'''
if not self.request.session.exists(self.request.session.session_key):
self.request.session.create()
serializer = self.serializer_class(data=request.data)
# if not Room.objects.filter(host=self.request.session.session_key).exists():
# # if the room does not exist, we create a session like that.
# self.request.session.create()
# we serialize our request data
serializer = self.serializer_class(data=request.data)
# check to see if the data we have serialized is valid
if serializer.is_valid():
# define those three attributes of our model to be those values.
guest_can_pause = serializer.data.get('guest_can_pause')
votes_to_skip = serializer.data.get('votes_to_skip')
host = self.request.session.session_key
# obtain a queryset with the newly defined host.
queryset = Room.objects.filter(host=host)
# if the queryset is not empty,
if queryset.exists():
# assign a room to the first entry in the queryset.
room = queryset[0]
# assign those room attributes to the values we defined above.
room.guest_can_pause = guest_can_pause
room.votes_to_skip = votes_to_skip
# do not create a new room, but rather,update the values we only
# only want to change
room.save(update_fields=['guest_can_pause', 'votes_to_skip'])
# if the queryset is empty,
else:
# create a room with the values specified
room = Room(host=host, guest_can_pause=guest_can_pause,
votes_to_skip=votes_to_skip)
room.save()
return Response(RoomSerializer(room).data, status=status.HTTP_201_CREATED)
Here is method in one of my react frontend components that sends the POST request:
handleRoomButtonClicked() {
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
votes_to_skip: this.state.votes_to_skip,
guest_can_pause: this.state.guest_can_pause,
}),
};
fetch("http://127.0.0.1:8000/api/createroom/", requestOptions)
.then((response) => response.json())
.then((data) => console.log(data));
}
Background information
Im using django-cors-headers to allow for cross origin requests since my backend and frontend are runnig on two different ports.
i have CORS_ALLOW_ALL_ORIGINS = True in my settings.py.
I have tried to search for similar questions on stack but none were helpful, I forgot to save their links so I can attach to this question
I have frontend on React and backend on Django. They are running on two different ports.
The Goal is to save data from frontend to Django session and having access to it on every request.
But thing is it creates new session everytime I make a request
This is how request looks like on React side
const data = await axios.post(
"http://127.0.0.1:8000/api/urls/",
qs.stringify({
long: long_url,
subpart: subpart,
})
);
And this is how it processed by view in Django where i am trying to create list of urls and append it every time.
#api_view(['POST'])
def users_urls(request):
if request.method == 'POST':
long_url = request.POST.get('long')
subpart = request.POST.get('subpart')
if 'users_urls' in request.session:
request.session['users_urls'].append(subpart)
else:
request.session['users_urls'] = [subpart]
return Response(short_url)
It works as it should work when i make requests from Postman. But there is some trouble with react.
Help me please to figure this out
Building my first app with a real backend.
In my app, when a user registers or logs in (with username and password), a token is saved to the cookies of their browser. After registration (or when logging in), I can easily return information pertaining to this particular user (name, id, etc.).
# Django REST backend for loggin in and getting user token
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(
data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'email': user.email,
'user_type': user.user_type,
})
Next time the user accesses the app in the same device, the token will be there and I can make the http requests for which a token is necessary. However, since I won't be logging the user in again (not asking for username and password every single session), I won't get that user's additional information.
In my React app I would like to have the user set in state at all times, e.g. user = {first_name: 'john', user_type: 'p'} but I don't know how to get the user info when the only thing I have is their token.
I am more than welcome to criticism to this approach and to learning what's the best way of doing this. I don't even know if keeping the user in state is the right way to do things...
I tried this:
class UserAPI(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer
def get_object(self):
print(self.request.user)
return self.request.user
curl -H "Authorization: Token b2e33463esdf8as7d9f8j34lf98sd8a" http://localhost:8000/current-user/
but the return value from self.request.user is AnonymousUser
If it's not sensitive information, such as username, id, user type, first name, etc. you can store this in localStorage.
problem was in my settings.py file:
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
There are two parts of answer to this question. first part is criticism to your approach and little bit of guidance towards better approach, second part of the answer is about your actual question.
Lets start with second part first.
From the look of your code, you are already storing the key in Token Table along with User. You can easily get the user by first running this query token = Token.objects.get(key=key).select_related('user') and then simple user = token.user will give you token.
Coming to first part now.
Since you are using DRF, you do not need to override the class unless extremely necessary. If you want user with each approved request, what you can do is simply add your Token verification class to Settings of DRF
I had the same problem but I found how to do that this code will help you
if request.user.is_authenticated:
user = Account.objects.filter(username=request.user)
serializer = UserSerializer(user, many=True)
return Response(serializer.data)
else:
return Response('You have to login first')
I'm intern and work on a project where I develop DRF API that need to interact with mobile app written by my colleague with Ionic framework.
We are creating new user. My view method is following:
class NewUser(generics.CreateAPIView):
model = User
permission_classes = [permissions.AllowAny]
serializer_class = NewUserSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
token, created = Token.objects.get_or_create(user=serializer.instance)
return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=headers)
When someone wants to create new user via POST request if user name has't been taken yet, then API return 201 status code and token in JSON, if user name already taken it returns 400 status and error message in JSON. MY colleague request me to change status message to 200 when he tries to create username with name that already exist. He says that he can't consume the ERROR response.
His code looks like:
$http.post(url,{
username:$scope.tel,
password:$scope.passwd
}).success(function(data){
alert(data);
$ionicLoading.hide();
console.log(data);
})
Question:
1) Should I tweak my API to send 200 status instead of more logical 400 for 'user already register' error?
I tried to change my code, But i couldn't find the method to override in CreateAPIView/ModelSerializer of DRF. I ended up rewriting my view class to method:
#api_view(['POST'])
def newUser(request):
"""
Saves a new user on the database
"""
if request.method == 'POST':
serializer = NewUserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
token, created = Token.objects.get_or_create(user=serializer.instance)
return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_200_OK)
Question:
2) If I want to change behaviorof API and responce, which method should I override
3) I'm new to Django and still don't qiute know where we should use generic views VS. #.... methods
200 vs 400 in this case is mostly preference. 400 means "Bad Request". This is generally more correct for a incorrectly formatted request, rather than one that doesn't meet some condition.
200 is just as appropriate it conveys the correct information:
Your request was valid, but I didn't create a new record.
As to how to do the override. The shortest path is to override the CreateAPIView.create and change the response code used. You should also avoid repeating the default behavior of CreateAPIView by calling super.
class CreateUserView(generics.CreateAPIView):
model = User
permission_classes = [permissions.AllowAny]
serializer_class = NewUserSerializer
def create(self, request, *args, **kwargs):
response = super(CreateUserView, self).create(request, *args, **kwargs)
token, created = Token.objects.get_or_create(user=serializer.instance)
response.status = status.HTTP_200_OK
response.data = {'token': token.key}
return response
Personally, I would have also crafted my NewUserSerializer to have a token field and handle the token so I didn't have to do that work in the View. It doesn't belong in a View.
Save and deletion hooks:
The following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior.
perform_create(self, serializer) - Called by CreateModelMixin when
saving a new object instance. perform_update(self, serializer) -
Called by UpdateModelMixin when saving an existing object instance.
perform_destroy(self, instance) - Called by DestroyModelMixin when
deleting an object instance.
These hooks are particularly useful for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument.
https://www.django-rest-framework.org/api-guide/generic-views/#methods
class CourseOrder(generics.CreateAPIView):
serializer_class = serializers.OrderCoursesSerializer
permission_classes = [permissions.AllowAny]
# hook before creating
def perform_create(self, serializer):
# print(serializer['name'].value)
# save post data
serializer.save()
try:
subject, from_email, to = 'New order', 'zelenchyks#gmail.com', 'zelenchyks#gmail.com'
text_content = 'New order'
html_content = '''
<p>client name: %s </p>
<p>client phone: %s </p>
'''
% (serializer['name'].value, serializer['mobile'].value)
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()
except Warning:
print('Huston we have a problems with smtp')
The Python source code for Djangular Demos gives examples of how to process the post request from a form that creates a Django object instance. But they don't show how to process the post request from a form which updates an existing object instance.
The code for updating an object seems rather complicated: my code is missing something crucial. Using my code I always get a form validation error: Object with this Name already exists.
I am using the Django generic UpdateView class and my model has a unique field called name.
My code:
from django.views.generic.edit import UpdateView
class MyForm(NgModelFormMixin, Bootstrap3FormMixin, NgModelForm):
scope_prefix='form_data'
form_name = 'my_form'
class Meta:
model = models.MyModel
fields = ['name','person']
class MyModelUpdate(UpdateView):
model = models.MyModel
form_class = MyForm
def post(self, request, **kwargs):
if request.is_ajax():
return self.ajax(request, **kwargs)
return super(MyModelUpdate, self).post(request, **kwargs)
# from the djangular combined_validation example
def ajax(self, request, **kwargs):
# tbd: need update-specific logic here: pass in instance
# parameter (object) or set it from pk. Base class post
# methods use self.get_object()
form = self.form_class(data=json.loads(request.body))
return JsonResponse({'errors': form.errors,
'success_url': force_text(self.success_url)})
What code do I need to get Django to load the instance identified by the pk argument and attach it to the form. That would be the default behavior when the request data comes from POST rather than ajax?
After trial and error experimentation I came up with the following new implementation for the view's ajax method. It passes my tests but feels clunky.
def ajax(self, request, **kwargs):
form = self.form_class(data=json.loads(request.body),
instance=self.get_object())
try:
form.save()
except:
# error is in form.errors
pass
return JsonResponse({'errors': form.errors,
'success_url': force_text(self.success_url)})