I have implemented API with django piston in which its take data from sms/mms . For MMS case i have to post XML data with image and others . Here is my code snippet on handlers.py
def create(self, request,*args,**kwagrs):
try:
file_type = None
raw_data = request.raw_post_data
data = serializers.deserialize("xml", raw_data)
try:
parser = Parse(data.stream.getvalue())
message = parser.get_message()
action_id = parser.get_action_id()
except Exception,e:
return HttpResponse(Response({'sender':parser.get_sender(),'error_description':str(e)}).get_error_response(), mimetype='text/xml')
if action_id in ['o','m','vt','vh','yritys']:
return self.post_message(request,parser)
elif action_id == 'poista' or action_id == 'lopeta':
return self.expired_message(request,parser)
elif action_id == 'tiedot':
return self.get_contact_info(request,parser)
except Exception,e:
ad_id = None
return HttpResponse(Response({'sender':parser.get_sender(),'error_description':str(e)}).get_error_response(), mimetype='text/xml')
when I am posting xml data with CURL its working , but when i use Firefox, httprequester its throwing me "BAD REQUEST"
Check this:
I get a 400 Bad Request error while using django-piston
Create middleware as:
class ContentTypeMiddleware(object):
def process_request(self, request):
if 'charset=UTF-8' in request.META['CONTENT_TYPE']:
request.META['CONTENT_TYPE'] = request.META['CONTENT_TYPE'].replace('; charset=UTF-8','')
return None
Add it in settings:
MIDDLEWARE_CLASSES = (
'app.middleware.ContentTypeMiddleware',
)
Try hurl.it for API testing. Check your post data. Set your header info if required.
Related
I am sending a get request in my view and then using the response to fill my database and I need some confirmation on the following:
should i make an api call inside of a view?
what should that view response be?
if i have done it wrong then what would be the right way to send get requests in Django?
my_app/views.py
class api(APIView):
template_name = 'payment/api.html'
def get(self, request):
#I SEND THE GET REQUEST HERE
config = configparser.ConfigParser()
config.read('config.ini')
r = requests.get(config['DEFAULT']['api'])
response = r.json()
#HERE I FILTER THE RESPONSE AND PUT IN A DB
for item in response:
if 'Covered Recipient Physician' in item.values():
person, _ = models.Person.objects.get_or_create(
profile_id = int(item['physician_profile_id']),
first_name = item['physician_first_name'].lower(),
last_name = item['physician_last_name'].lower()
)
address, _ = models.Address.objects.get_or_create(
business_street =
item['recipient_primary_business_street_address_line1'].lower(),
city = item['recipient_city'].lower(),
state = item['recipient_state'].lower(),
country = item['recipient_country'].lower()
)
business, _ = models.Business.objects.get_or_create(
business = item['submitting_applicable_manufacturer_or_applicable_gpo_name'].lower(),
)
business_address_link = models.Business_address_link.objects.create(
business = business,
address = address
)
business_address_link.save()
payment = models.Payment.objects.create(
record_id = int(item['record_id']),
amount = float(item['total_amount_of_payment_usdollars']),
date = item['date_of_payment'],
number_of_payments = int(item['number_of_payments_included_in_total_amount']),
payment_form = item['form_of_payment_or_transfer_of_value'],
nature_of_payment = item['nature_of_payment_or_transfer_of_value']
)
payment.save()
person_payment_information = models.Person_payment_information.objects.create(
person = person,
business_address_link = business_address_link,
payment = payment
)
person_payment_information.save()
Try to use this function for GET request -
#require_http_methods(['GET'])
def get_persons(request):
try:
data = list(Message.objects.all.values())
return render(request=request, template_name='template.html', context={"data": data})
except Exception as e:
return render(request=request, template_name='template.html', context={"error": e})
Once you get the response and send it to template.html, you can display it in any way you want.
If you want to add information to the DB you better use POST request, for example -
#require_http_methods(['POST'])
def add_person(request):
try:
data = json.loads(request.body)
new_person = Person(**data)
new_person.save()
return render(request=request, template_name='template.html', context={"data": data})
except Exception as e:
return render(request=request, template_name='template.html', context={"error": e})
I have my python class below. My question is what this error actually means EncodeError at /api/v1/jobs/apply/
> is not JSON serializable. What is it trying to point based on my code below ? can anyone give an idea? . The new_email is at below also.
def new_application(self, data):
try:
def new_application(self, data):
try:
html = render_to_string("email_templates/new_application.tpl",data)
name = "testt"
data["subject"] = "test"
if env == "localhost":
msg = EmailMessage(data["subject"], html, name, dev_emails)
msg.content_subtype = "html" # Main content is now text/html
msg.attach_file(data["resume"])
msg.send()
else:
msg = EmailMessage(data["subject"], html, name, [data["company_email"]])
msg.content_subtype = "html" # Main content is now text/html
msg.attach_file(data["resume"])
msg.send()
except Exception as e:
print(str(e))
error
EncodeError at /api/v1/jobs/apply/
<bound method Email.new_application of <v1.lib.emails.Email object at 0x7f6cbe44e908>> is not JSON serializable
Code
class ApplyJob(APIView):
def email(self, data):
email_ins = Email()
c_task.delay(email_ins.new_application, data)
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,IsApplicant)
def post(self, request, format=None):
data = request.data
job_applicant_ser = JobApplicantSerializer(data=data)
applicant = get_applicant_ins(request)
if applicant.profile_completeness <= 60:
return Response("You have not complete filling up your profile yet. Please complete it atleast 80% and above percentage..", status=status.HTTP_400_BAD_REQUEST)
if not applicant.resume:
return Response("Sorry, Please upload your resume.", status=status.HTTP_400_BAD_REQUEST)
job = data.get("job" , None)
cover_letter = data.get("cover_letter", None)
if not cover_letter or cover_letter == "":
return Response("Sorry, please fill your cover letter.", status=status.HTTP_400_BAD_REQUEST)
apply_job_checker_ins = JobApplicant.objects.filter(job=job,applicant=applicant).count()
if apply_job_checker_ins > 0:
return Response("Sorry but you cant apply to this company, it appears that you have already applied.", status=status.HTTP_400_BAD_REQUEST)
if job:
job = JobModel.objects.get(pk=job)
else:
return Response("Sorry but there is a problem with the application, please refresh page.", status=status.HTTP_400_BAD_REQUEST)
if job_applicant_ser.is_valid(raise_exception=True):
job_applicants = job_applicant_ser.save(applicant=applicant,job=job)
data = {}
data["job"] = job_applicants.job.title
data["account_url"] = APP_URL+"/account/job_applicants"
data["email"] = job_applicants.job.company.user.email
data["resume"] = STATIC_ROOT+"/uploads/resume/"+str(job_applicants.applicant.resume)
data["company_email"] = job_applicants.job.company.user.email
self.email(data)
return Response("You have applied to the jo
b. Please make sure your line is always open.", status=status.HTTP_200_OK)
Good afternoon.
I'm testing api based on django-rest-framework using pytest. I have the following method that creates a new object (method taken from here):
class JSONResponse(HttpResponse):
"""
An HttpResponse that renders its content into JSON.
"""
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
#csrf_exempt
#api_view(('POST',))
#permission_classes((IsAuthenticated, ))
def create_transaction(request):
"""
The method takes the data in JSON-format.
If the data is correct Transaction object will created, otherwise it returns an error also in JSON-format.
"""
stream = StringIO('[' + request.raw_post_data + ']')
data = JSONParser().parse(stream)
serializer = NewTransactionSerializer(data=data, many=True)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201)
else:
return JSONResponse(serializer.errors, status=400)
I wrote to it next test:
#pytest.mark.django_db
def test_create_method(client):
correct_data = '''{ "var1": "111",
"var2": "222",
"var3": 2 }'''
client.login(username='test2#github.com', password='test')
data = json.loads(correct_data)
response = client.post('/rest2/create_transaction/', data, format='json')
content = json.loads(response.content)
assert content[0]['var1'] == '111'
assert content[0]['var2'] == '222'
assert content[0]['var3'] == 2
assert response['Content-Type'] == 'application/json'
assert response.status_code == 201
When starting pytest displays the following: Exception: You cannot access body after reading from request's data stream. Its broke when i post data to url.
When I run the same code in the shell, the code runs without problems. I am new to testing, may miss something, help please.
If you're using django-rest-framework, then you can just use request.data instead of trying to parse json from the request yourself
http://www.django-rest-framework.org/api-guide/requests/
stream = StringIO('[' + request.raw_post_data + ']')
data = JSONParser().parse(stream)
this can be replaced with
data = request.data
I'm trying to unit test my RESTful API. Here's my API:
class BaseHandler(tornado.web.RequestHandler):
def __init__(self, *args, **kwargs):
tornado.web.RequestHandler.__init__(self, *args, **kwargs)
self.log = self.application.log
self.db = self.application.db
class ProductHandler(BaseHandler):
#tornado.web.removeslash
def put(self, id = None, *args, **kwargs):
try:
self.log.info("Handling PUT request")
if not id:
raise Exception('Object Id Required')
id = { '_id' : id }
new_values = dict()
name = self.get_argument('name', None)
description = self.get_argument('description', None)
if name:
new_values['name'] = name
if description:
new_values['description'] = description
self.db.products.update(id, new_values, safe = True)
except:
self.log.error("".join(tb.format_exception(*sys.exc_info())))
raise
class Application(tornado.web.Application):
def __init__(self, config_path, test = False, *args, **kwargs):
handlers = [
(r"/product/?(.*)", ProductHandler),
]
settings = dict(debug=True)
tornado.web.Application.__init__(self, handlers, **settings)
self.log = logging.getLogger(__name__)
self.config = ConfigParser()
self.config.read(config_path)
self.mongo_connection = Connection(
host = self.config.get('mongo','host'),
port = self.config.getint('mongo','port'),
)
if test:
db_name = self.config.get('test', 'mongo.db')
else:
db_name = self.config.get('mongo', 'db')
self.log.debug("Using db: %s" % db_name)
self.db = self.mongo_connection[db_name]
But, here's my problem: the handler isn't seeing the name or description arguments. :(
Any suggestions?
As a work-around, I found them in the request.body and parsed the encoded parameters manually. It was kindof annoying, but it works.
new_values = urlparse.parse_qs(self.request.body)
# values show as lists with only one item
for k in new_values:
new_values[k] = new_values[k][0]
Say if you are using jQuery to send this PUT request:
$.ajax({
type: "PUT",
url: "/yourURL",
data: JSON.stringify({'json':'your json here'),
dataType: 'json'
})
The data should not be like:
data: {'json': 'your json here'}, because it will automatically be encoded into query string, which needs to be parsed by parse_qs
Then in Tornado
def put(self, pid):
d = json.loads(self.request.body)
print d
put handler will parse request.body, if request had proper content-type header (application/x-www-form-urlencoded), for example if you are using tornado http client:
headers = HTTPHeaders({'content-type': 'application/x-www-form-urlencoded'})
http_client.fetch(
HTTPRequest(url, 'PUT', body=urllib.urlencode(body), headers=headers))
Have you tried using a get method instead? Because depending on how you test your program, if you test it via your browser like Firefox or Chrome, they might be able to do it. Doing a HTTP PUT from a browser
If I were you I would write get instead of put. Cause then you can definitely test it in your browser.
For example, instead of:
def put ...
Try:
def get ...
Or Actually in your:
name = self.get_argument('name', None)
description = self.get_argument('description', None)
Why is the None there? According to the documentation:
RequestHandler.get_argument(name, default=[], strip=True)
...
If default is not provided, the argument is considered to be required,
and we throw an HTTP 400 exception if it is missing.
So in your case because you are not providing a proper default, therefore your app is returning HTTP 400. Miss out the default! (i.e.)
name = self.get_argument('name')
description = self.get_argument('description')
Is there a better pattern for input validation than I'm using in this function?
https://github.com/nathancahill/clearbit-intercom/blob/133e4df0cfd1a146cedb3c749fc1b4fac85a6e1b/server.py#L71
Here's the same function without any validation. It's much more readable, it's short and to the point (9 LoC vs 53 LoC).
def webhook(clearbitkey, appid, intercomkey):
event = request.get_json()
id = event['data']['item']['id']
email = event['data']['item']['email']
person = requests.get(CLEARBIT_USER_ENDPOINT.format(email=email), auth=(clearbitkey, '')).json()
domain = person['employment']['domain']
company = requests.get(CLEARBIT_COMPANY_ENDPOINT.format(domain=domain), auth=(clearbitkey, '')).json()
note = create_note(person, company)
res = requests.post(INTERCOM_ENDPOINT,
json=dict(user=dict(id=id), body=note),
headers=dict(accept='application/json'),
auth=(appid, intercomkey))
return jsonify(note=res.json())
However, it doesn't handle any of these errors:
dict KeyError's (especially nested dicts)
HTTP errors
Invalid JSON
Unexpected responses
Is there a better pattern to follow? I looked into using a data validation library like voluptous but it seems like I'd still have the same problem of verbosity.
Your original code on github seems fine to me. It's a little over complicated, but also handle all cases of error. You can try to improve readability by abstract things.
Just for demonstration, I may write code like this:
class ValidationError(Exception):
"Raises when data validation fails"
pass
class CallExternalApiError(Exception):
"Raises when calling external api fails"
pass
def get_user_from_event(event):
"""Get user profile from event
:param dict event: request.get_json() result
:returns: A dict of user profile
"""
try:
event_type = event['data']['item']['type']
except KeyError:
raise ValidationError('Unexpected JSON format.')
if event_type != 'user':
return ValidationError('Event type is not supported.')
try:
id = event['data']['item']['id']
email = event['data']['item']['email']
except KeyError:
return ValidationError('User object missing fields.')
return {'id': id, 'email': email}
def call_json_api(request_function, api_name, *args, **kwargs):
"""An simple wrapper for sending request
:param request_function: function used for sending request
:param str api_name: name for this api call
"""
try:
res = request_function(*args, **kwargs)
except:
raise CallExternalApiError('API call failed to %s.' % api_name)
try:
return res.json()
except:
raise CallExternalApiError('Invalid response from %s.' % api_name)
#app.route('/<clearbitkey>+<appid>:<intercomkey>', methods=['POST'])
def webhook(clearbitkey, appid, intercomkey):
"""
Webhook endpoint for Intercom.io events. Uses this format for Clearbit and
Intercom.io keys:
/<clearbitkey>+<appid>:<intercomkey>
:clearbitkey: Clearbit API key.
:appid: Intercom.io app id.
:intercomkey: Intercom.io API key.
Supports User events, specifically designed for the User Created event.
Adds a note to the user with their employment and company metrics.
"""
event = request.get_json()
try:
return handle_event(event, clearbitkey, appid, intercomkey)
except (ValidationError, CallExternalApiError) as e:
# TODO: include **res_objs in response
return jsonify(error=str(e))
def handle_event(event):
"""Handle the incoming event
"""
user = get_user_from_event(event)
res_objs = dict(event=event)
person = call_json_api(
requests.get,
'Clearbit',
CLEARBIT_USER_ENDPOINT.format(email=user['email']),
auth=(clearbitkey, '')
)
res_objs['person'] = person
if 'error' in person:
raise CallExternalApiError('Error response from Clearbit.')
domain = person['employment']['domain']
company = None
if domain:
try:
company = call_json_api(
requests.get,
'Clearbit',
CLEARBIT_COMPANY_ENDPOINT.format(domain=domain),
auth=(clearbitkey, ''))
)
if 'error' in company:
company = None
except:
company = None
res_objs['company'] = company
try:
note = create_note(person, company)
except:
return jsonify(error='Failed to generate note for user.', **res_objs)
result = call_json_api(
requests.post,
'Intercom',
(INTERCOM_ENDPOINT, json=dict(user=dict(id=id), body=note),
headers=dict(accept='application/json'),
auth=(appid, intercomkey)
)
return jsonify(note=result, **res_objs)
I hope it helps.