ParseError On Django Restframework - python-3.x

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

Related

Best practice to call function inside route methode

I'm new to flask and in order to refactor an existing route method on a Flask API, i'm looking for the best practice to reduce it and call method inside the route method.
Acutally the route is designed like that :
#qman.route('/add_report/', methods=['POST'])
def create_report():
"""
Check if data send throught http POST request, is correct based on the report
schema and not already recorded in the table report of the DB.
:param: data from POST request
:return: Ok, valide and imported -> 201, Correct but AlreadyKnown -> 208,
InvalideScheme -> 422
"""
jsonData = request.get_json()
reportSchema = ReportSchema()
try:
data = reportSchema.load(jsonData)
except ValidationError as validation_err:
return(validation_err.messages), 422
nameReportCheck = data["report_name"]
report = Report.query.filter_by(report_name=nameReportCheck).first()
if report is None:
# Create new report
report = Report(
report_name=nameReportCheck,
hostname=data["hostname"],
status=data["status"],
date=data["date"],
nb_analysis_erreur=data["nb_analysis_erreur"]
)
db.session.add(report)
db.session.commit()
NewResult = reportSchema.dump(Report.query.get(report.reportID))
return{"message" : "Created new report" , "report" : NewResult}, 201
else :
reportAlreadyKnown = reportSchema.dump(Report.query.get(report.reportID))
return{"message" : "This report is already in the DB", "report" : reportAlreadyKnown}, 208
In the facts i would like to call a function named valid_schema(_schema, _jsondata) to check if the data send throught POST request match with my schema of model Report().
This function return a Response() object with serialized data and a 200 code if it's serialization is possible or an error that i cath inside try/except with 400 error code.
def valid_schema(_schema, _jsondata):
schema = _schema()
try:
data = schema.load(_jsondata)
except ValidationError as validation_err:
response = Response(validation_err.messages, 422)
return response
response = Response(data, 200, mimetype="application/json")
return response
Then the route method call an other function named create_report(report_data) if valid_schema(_schema, _jsondata) return report_data and 200 code in response object.
With his args, this method check if the records is not already in the DB and if is not, he create a Report() object from report_data arg and insert this one as a new record into the DB.
In fact I guess I can easily call this method inside the route function but it seem weird and there is probably an other way that I can't find, maybe decorator ?
One possibility for refactoring is the use of webargs, Flask-Marshmallow and marshmallow-sqlalchemy.
With Flask-Marshmallow you can check the input by specifying fields and validators. Webargs offers you the option of validating the defined scheme in a decorator and passing it on to the route as an argument. Using marshmallow-sqlalchemy in combination, this is immediately converted into a database model.
The following example is based on your information and gives you a brief overview of the usage. By defining your own error handler, the error messages can also be sent as JSON. Use in blueprints, views or the like is possible.
from flask import Flask
from flask import jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from marshmallow.validate import Length, OneOf
from webargs.flaskparser import use_args
app = Flask(__name__)
db = SQLAlchemy(app)
ma = Marshmallow(app)
class Report(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True)
hostname = db.Column(db.String)
status = db.Column(db.String)
date = db.Column(db.DateTime)
nb_analysis_error = db.Column(db.String)
class ReportSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Report
load_instance = True
sqla_session = db.session
name = ma.Str(required=True, validate=Length(min=3))
hostname = ma.Str(required=True)
date = ma.DateTime(required=True)
status = ma.Str(required=True, validate=OneOf(['online', 'offline']))
nb_analysis_error = ma.Str(missing='Unknown Error')
#app.route('/add_report', methods=['POST'])
#use_args(ReportSchema(), location='json')
def add_report(report):
report_schema = ReportSchema()
_report = Report.query.filter_by(name=report.name).first()
if _report:
report_data = report_schema.dump(_report)
return jsonify(message='Already Reported', report=report_data), 208
else:
db.session.add(report)
db.session.commit()
report_data = report_schema.dump(report)
return jsonify(message='Created', report=report_data), 201
with app.app_context():
db.drop_all()
db.create_all()

How to filter Django serializer data?

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 cant access to post data in get from django

I need read 'dataset' in get function for showing amount and name in the template but I can't access to 'dataset' in get function
class Port(View):
def post(self, request, pid):
session = requests.Session()
response = session.get("http://localhost:8001/pay/" + str(pid))
if response.status_code is 200:
try:
dataset = json.loads(request.body.decode('utf-8'))
print("###",data['amount'],' - ',data['name'],' - ',pid)
return dataset
except ConnectionError as ce:
print(ce)
return HttpResponse("Json Parse Error")
return dataset
else:
return HttpResponse("* wrong request *")
def get(self, request, pid):
context = {
'amount' : dataset['amount'],
'firstname' : dataset['name'],
'order_id' : pid,
}
return render(request, 'index.html',context)
I believe dataset is initialized as an attribute in Port class. Use self.dataset

Django3.0 add request.user to form request

Let's say we have this model:
class Creation(models.Model):
title = models.CharField(max_length=DEFAULT_LENGTH)
url = models.CharField(max_length=DEFAULT_LENGTH)
date = models.DateTimeField('date published', default=datetime.date.today)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
def __str__(self):
return self.title
And let's use this view:
def submit(request):
FormSet = modelformset_factory(Creation, fields=('title', 'url'))
if request.method == 'POST':
formset = FormSet(request.POST, request.FILES)
if formset.is_valid():
obj = formset.save(commit=False)
obj.author = request.user
obj.save()
else:
formset = FormSet
return render(request, 'app/submit.html', {'formset': formset})
I saved formset to obj and added request.user and committed it to the database.
This doesn't work, because now django throws an error which says
'list' object has no attribute 'author'
which makes perfectly sense, because there is no author in the fields-list above.
But if I add 'author' to fields, another selectbox would be displayed while rendering the template via {{formset}}.
Maybe I could code my own template-code instead of using {{formset}} and omit the author, but I feel that there must be a more elegant way with Django3.0. Is there any?
All I want to do is to get the foreignkey author (see model above) filled with the logged in user.
Turns out that something is wrong with modelformset_factory(..).
If you use a proper CreationForm in models.py:
class CreationForm(ModelForm):
class Meta:
model = Creation
fields = ['title', 'url']
and use it in your views.py:
formset = CreationForm(request.POST)
if request.method == 'POST':
if formset.is_valid():
f = formset.save(commit=False)
f.author = request.user
f.save()
it works.

Django rest framework. If user exist return True, False if not

Need to preform simple check: If user exist return True, False if not.I am trying to solve thise problem using custom json answers, not sure it is the right way to deal with it. Please help me .
My serializer:
User = get_user_model()
Class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (User.USERNAME_FIELD, )
My views:
#api_view(['GET'])
def UserViewSet(self, request):
if request.method == 'GET':
try:
user = User.objects.get(username=User.USERNAME_FIELD)
if User.DoesNotExist:
json = {}
json['message']= 'False'
return Response(json)
else:
json = {}
json['message']= 'True'
return Response(json)
Instead of using UserViewSet and UserSerializer, you can just use APIView for that.
class UserExistsView(APIView):
def get(self, request, *args, **kwargs):
# use this if username is in url kwargs
username = self.kwargs.get('username')
# use this if username is being sent as a query parameter
username = self.request.query_params.get('username')
try:
user = User.objects.get(username=username) # retrieve the user using username
except User.DoesNotExist:
return Response(data={'message':False}) # return false as user does not exist
else:
return Response(data={'message':True}) # Otherwise, return True

Resources