I just started learning Django Rest Framework together with React and i am stuck on this problem for a couple of weeks now. Hope you guys can help me out.. Here's what i am trying to achieve: When a user clicks on 'Buy Now' there will be an order created with the status of the order set to open. This all works fine. My Orders view & model looks like this:
class OrdersView(ListCreateAPIView):
serializer_class = OrdersSerializer
queryset = Orders.objects.all()
def post(self, request):
serializer = OrdersSerializer(data=request.data)
if serializer.is_valid():
data = serializer.validated_data
price = data['price']
pakket = data['pakket']
payment = mollie_client.payments.create({
'amount': {
'currency': 'EUR',
'value': price,
},
'description': pakket,
'redirectUrl': 'http://127.0.0.1:3000/Thanks',
'webhookUrl': 'http://6efd85bb183c.ngrok.io/api/order-status/',
'method': 'ideal',
})
serializer.save(user=request.user, userID=request.user.id,
status=payment.status, orderID=payment.id)
return Response(status=status.HTTP_201_CREATED, data=payment)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class Orders(models.Model):
userID = models.CharField(max_length=30)
pakket = models.CharField(max_length=30)
price = models.CharField(max_length=30)
created_at = models.DateTimeField(default=one_hour_later)
status = models.CharField(max_length=30)
orderID = models.CharField(max_length=30)
user = models.EmailField(max_length=120)
def __str__(self):
return self.user
My serializer called OrdersSerializer looks like this:
class OrdersSerializer(serializers.ModelSerializer):
class Meta:
model = Orders
fields = '__all__'
read_only_fields = ('user','status','orderID','userID')
After the status of the order has changed (the user made the purchase or not) my webhook is called (a POST) and i am retreiving the order by the id which is POSTED to the webhook. This is all taken care of by using an API client (mollie_client). This all works fine too. My View looks like this:
class OrderStatusView(ListCreateAPIView):
serializer_class = OrdersSerializer
queryset = Orders.objects.all()
permission_classes = [AllowAny,]
def post(self, request):
data = request.data
payment_id = data['id']
payment = mollie_client.payments.get(payment_id)
if payment.is_paid():
return Response(status=status.HTTP_200_OK, data=payment.status)
elif payment.is_pending():
return Response(status=status.HTTP_200_OK, data=payment.status)
elif payment.is_open():
return Response(status=status.HTTP_200_OK, data=payment.status)
else:
return Response(status=status.HTTP_200_OK, data=payment.status)
return Response(status=status.HTTP_400_BAD_REQUEST)
Now my problem is: How do i retreive the original Order (by the id) and update the status of the order.
Grabbing the payment.status works fine, but how do i grab the Order and update its status from the payment.status?
I am able to grab the original order by: Orders.objects.get(orderID=payment_id) inside the view, but from there i am stuck. I am not sure how i can update that specific order and change the status to payment.status.
I am thinking i have to do something with the serializer.save(status=payment.status).
Maybe it is super simple, but i can't get my head around it.
Hope its clear and you guys can help.
Please keep in mind i am still a beginner on this topic.
Thanks in advance!
Greetings
As #Ngoc Pham pointed out It is better to keep payment and order different. What is generally followed is You keep pinging for the payment status of an order and then update the order status with choice field.
In Orders Model you can define a choice field for status and whenever you are buying create an order with its status to constants.PENDING.
Now filter all the orders with status as pending and hit the check_payment_status of api with the order id. And now if the payment status is success then update the order object with constants.SUCCESS. Here's a gist of what I meant.
class Orders(models.Model):
PENDING = 'pending'
SUCCESS = 'success'
FAILURE = 'failure'
ORDER_STATUS_CHOICES = [
(PENDING, "pending"),
(SUCCESS, "success"),
(FAILURE, "failure"),
]
status = models.CharField(
choices=ORDER_STATUS_CHOICES,
max_length=10,
null=True
)
Now whenever you buy make the status as
status=Orders.PENDING
if the payment status is pending. And then fetch the orders object to get the pending objects
Orders.objects.filter(status=Orders.PENDING)
Once you iterate through this queryset you can hit payment_status_check request for a particular id and update the status again through
status=Orders.SUCCESS
Hope this gives you a way to approach this problem :).
First, your OrderStatusView post never throw HTTP_400_BAD_REQUEST. Its have else condition in last. It always return HTTP_200_OK.
you can try edit like this: its can find orders have orderID=payment_id, and if not have anything match, return HTTP_400_BAD_REQUEST. Make sure all status of payment is same with status of Order
class OrderStatusView(ListCreateAPIView):
serializer_class = OrdersSerializer
queryset = Orders.objects.all()
permission_classes = [AllowAny,]
def post(self, request):
data = request.data
payment_id = data['id']
payment = mollie_client.payments.get(payment_id)
orders = self.get_queryset().filter(orderID=payment_id)
if not orders:
return Response(status=status.HTTP_400_BAD_REQUEST)
orders.update(status=payment.status)
return Response(status=status.HTTP_200_OK, data=payment.status)
Related
I'm trying to make an attendance system in the frontend I retrieve a list of users which the request.user can take attendance of, I'm using CreateAPIView but this won't get me the desired effect as I want the request.user to be able to toggle between Absent, Present, and on_leave even after the entry has been created on the first request
i have seen questions and answers about create or update here but i couldn't use them so any help would be appriciated
this is my view.py
class AttendanceListCreateAPIView(CreateAPIView):
permission_classes = [IsTeacher_Student]
queryset = Attendance.objects.all()
serializer_class = AttendanceSerializer
def post(self, request, *args, **kwargs):
user = request.user
data = request.data
serializer = AttendanceSerializer(data=data)
if serializer.is_valid():
data_user = serializer.validated_data['user']
## validation
return Response({"message": "Something is wrong, maybe you have already taken attendance for this user"},
status=status.HTTP_400_BAD_REQUEST)
my serializer.py
class AttendanceSerializer(serializers.ModelSerializer):
date = serializers.HiddenField(default=timezone.now)
leave_reason = serializers.CharField(required=False, default="")
class Meta:
model = Attendance
fields = ['user', 'presence', 'leave_reason', 'date']
extra_kwargs = {
'user': {'required': True},
'presence': {'required': True},
'leave_reason': {'required': False},
}
validators = [
UniqueForYearValidator(
queryset=Attendance.objects.all(),
field='user',
date_field='date',
message=("You have already taken the attendance")
)
]
def create(self, validated_data):
instance = Attendance.objects.create(
user=validated_data['user'],
presence=validated_data['presence'],
leave_reason=validated_data['leave_reason'],
date=validated_data['date'],
)
instance.save()
return instance
desired effect
the user fk and date are unique together meaning that if the date isn't unique for the user update it if it is create a new entry
I'm trying to filter data based on userName in JWT. This is how I've been trying to do it:
views.py:
class TestView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request):
token = request.META.get('HTTP_AUTHORIZATION', " ").split(' ')[1]
data = {'token': token}
try:
valid_data = VerifyJSONWebTokenSerializer().validate(data)
user = valid_data['user']
request.user = user
person = Person.objects.filter(userName=request.user)
except ValidationError as v:
print("validation error", v)
return Response(person[0])
This works as I can get the Person data with print("Person: ", person[0]). The return Response(person[0]) however returns an error: TypeError: Object of type Person is not JSON serializable. I guess I could use a serializer class to return a valid JSON, am I right? I have this in my serializers.py:
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = '__all__'
I just don't know how to use this with my view. If I use serializer instead of person = Person.objects.filter(userName=request.user), how is the filtering supposed to be done?
Please correct me if I'm not on right track at all.
You can add serializer to view, provide query result to serializer and get serialized data:
class TestView(APIView):
permission_classes = (IsAuthenticated,)
serializer_class = TestSerializer # add serializer
def get(self, request):
token = request.META.get('HTTP_AUTHORIZATION', " ").split(' ')[1]
data = {'token': token}
try:
valid_data = VerifyJSONWebTokenSerializer().validate(data)
user = valid_data['user']
request.user = user
person = Person.objects.filter(userName=request.user).last()
data = self.serializer_class(instance=person).data # serialize query result
except ValidationError as v:
print("validation error", v)
raise v
return Response(data) # return serialized response
Here you can find some examples of using serializer with class based views.
Also note that your Person model might have security-sensitive fields like password, etc, so it's better to specify exact fields you need in serializer rather then use fields = '__all__'.
Are you using Django-Rest-Framework (based on your use of ModelSerializer)?
In pure Django, from the documentation on serializers, you can do something like:
from django.core import serializers
json_response = serializers.serialize("json", person[0])
return Response(json_response)
If you are using Django-Rest-Framework:
return Response(TestSerializer(person[0]).data)
I have created a Custom user Model for my project, using Django 3.2.
To POST/PUT the details in the user I am using restframework.
But while giving the data for "PUT" operation to change some change I am getting the error as JSON parse error - Expecting value: line 1 column 1 (char 0)
def update_masteruser(request,pk):
try:
user = MasterUser.objects.get(pk=pk)
except MasterUser.DoesNotExist:
return JsonResponse({'message': 'The User does not exist'}, status=status.HTTP_404_NOT_FOUND)
if request.method == "PUT":
user_data = JSONParser().parse(request) #problem seems to be on this line
serializer = RegisterUserSerializer(user, data=user_data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
alternate method, this throws 'WSGIRequest' object has no attribute 'data':
def update_masteruser(request,pk):
try:
user = MasterUser.objects.get(pk=pk)
except MasterUser.DoesNotExist:
return JsonResponse({'message': 'The User does not exist'}, status=status.HTTP_404_NOT_FOUND)
if request.method == "PUT":
serializer = MasterUserSerializer(user, data=request.data)
data = {}
if serializer.is_valid():
serializer.save()
data["success"] = "updated"
return Response(data=data)
return Response(serializer.data)
serializers.py
class MasterUserSerializer(serializers.ModelSerializer):
class Meta:
model = MasterUser
fields = "__all__"
I searched the error cause, but couldnt find the reason on it. Why cant it parse the data from request?
Refer to This Documentation given below!!
https://www.django-rest-framework.org/api-guide/parsers/#how-the-parser-is-determined
I tried a alternate method. Instead of using .save , I use .update() and it worked. Something like below
serializer.update(user,serializer.validated_data)
Thanks #bdbd for you response and time! Appreciated
I'm trying to delete an entry in my data base that is returned by a modelviewset get_queryset. When sending a DELETE request through the DRF web interface and via postman, I receive this response "DELETE /api/remove_self/3 HTTP/1.1" 200 along with the data I am trying to delete. The code that gives this result looks like this:
Models.py
class EventAtendee(models.Model):
"""Lists users atending an event"""
#below connects user profile to event
id = models.AutoField(primary_key=True)
event_id = models.IntegerField(null = True)
user_profile = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
def __str__(self):
return self.event_id
views.py
class RemoveSelfFromEvent(viewsets.ModelViewSet):
"""Remove Yourself From an Event you were attending"""
authentication_classes = (TokenAuthentication,)
serializer_class = serializers.EventAtendeeSerializer
permission_classes = (permissions.UpdateOwnStatus, IsAuthenticated)
def perform_create(self, serializer):
"""Sets the user profile to the logged in user"""
#
serializer.save(user_profile=self.request.user)
def get_queryset(self):
"""
This view should return a list of all the purchases for
the user as determined by the username portion of the URL.
"""
#user_profile = self.kwargs['user_profile']
event_id = self.kwargs['event_id']
return models.EventAtendee.objects.filter(event_id=event_id, user_profile=self.request.user)
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
urls.py
router = DefaultRouter(trailing_slash=False)
router.register('events', views.EventAtendeeViewSet, basename='EventAtendee')
urlpatterns = [
path('remove_self/<event_id>', views.RemoveSelfFromEvent.as_view({'get': 'list', 'delete': 'list'})),
]
Any help is much appreciated!
You are mapping the method DELETE to list in your urls.
path('remove_self/<event_id>', views.RemoveSelfFromEvent.as_view({'get': 'list', 'delete': 'list'})),
Correct way to do:
path('remove_self/<pk>', views.RemoveSelfFromEvent.as_view({'get': 'list', 'delete': 'destroy'})),
mapping of various methods:
POST : create
GET : retrieve
PUT : update
PATCH : partial_update
DELETE : destroy
I have a table like this:
class Mapping(db.Model):
map_id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer)
bike_id = db.Column(db.String(255))
is_active_data = db.Column(db.Boolean, default=True)
created_by = db.Column(db.String(150))
updated_by = db.Column(db.String(150))
My POST method:
def save_MapperM(adddata):
create_data = Mapping(**adddata)
db.session.add(create_data)
db.session.commit()
return dict(Successful="Successfully Created")
Route:
#test.route("/addmapper"))
class MapperV(Resource):
#staticmethod
def post():
if request.method == 'POST':
save_data = request.get_json()
try:
return jsonify(save_MapperM(save_data))
except Exception:
return jsonify({'Unsuccessful': 'Looks like you missed something !!'})
Current Code :
The current code will take only one bike_id for every request.
Requirements:
I want to take multiple bike_id's as for one user id and store it as multiple records in the table level.
Example data format coming from UI:
{ user_id: 1, bike_id: 1,2,3,4 }
The easiest solution is to modify your save_MapperM function with a cycle:
def save_MapperM(adddata):
for bike_id in adddata["bike_id"]:
item_data = adddata.copy()
item_data["bike_id"] = bike_id
create_data = Mapping(**item_data)
db.session.add(create_data)
db.session.commit()
return dict(Successful="Successfully Created")
But be careful with this function as it allows to create Mapping instances with any parameters received from the POST request. It looks like it is intended but it can lead to security issues if your Mapping class has some private attributes which should be filled only on the server side. For example the user "Mike" can send a request:
{ "user_id": 1, "bike_id": [1, 2], "created_by": "Alex", "updated_by": "Alex" }
It will cause the save_MapperM function to create instances with created_by and updated_by values set to "Alex" which may not be true. It is better to get such attributes from the session data.
So your post method may look like this (post and save_MapperM functionality combined):
def post():
request_data = request.get_json()
for bike_id in request_data.get("bike_id", []):
item = Mapping(
user_id=request_data.get("user_id"),
bike_id=bike_id,
created_by=session.get("username"),
updated_by=session.get("username"),
)
db.session.add(item)
try:
db.session.commit()
except Exception:
return jsonify({"success": False})
return jsonify({"success": True})
The next step may be implementing request JSON data validation. It is OK when you have a couple of JSON keys with a simple structure but when you need to pass lots of data you need to be sure it is correct. You can use some of the serialization/ODM libraries for this Marshmallow for example.