How to send request from one django server to another server - django

I have a django problem. I want to send datas which are from browser or business logic
on my django server to another django server or just the same server but different port, to handle the request. How can I do? I have tried to achieve using socket,but it seems no working.
Following is my code:
accept the client's request:
def im(request):
userp = None
try:
userp = UserProfile.objects.get(user = request.user)
except:
pass
if not userp:
return HttpResponse("error")
print '111'
if request.method == "GET":
import json
msg = json.loads(request.GET.get('msg'))
try:
msg['from_id'] = userp.id
if msg.get('type', '') == 'sync': #页面同步消息
msg['to_id'] = userp.id
push_msg(msg)
return HttpResponse("success")
except:
return HttpResponse("error")
#return HttpResponseRedirect("http://127.0.0.1:9000/on_message")
return HttpResponse("error")
helper.py:push_msg:
def push_msg(msg):
print '111'
params = str(msg)
headers = {"Content-type":"application/x-www-form-urlencoded", "Accept":"text/plain"}
conn = httplib.HTTPConnection("http://127.0.0.1:9000/push_msg/")
conn.request("POST", "/cgi-bin/query", params, headers)
url(r'^push_msg/$', 'chat.events.on_message')
events.py:on_message
def on_message(request):
msg = request.POST.get('msg')
msg = eval(msg)
try:
print 'handle messages'
from_id = int(msg['from_id'])
to_id = int(msg['to_id'])
user_to = UserProfile.objects.get(id = msg['to_id'])
django_socketio.broadcast_channel(msg, user_to.channel)
if msg.get('type', '') == 'chat':
ct = Chat.objects.send_msg(from_id=from_id,to_id=to_id,content=data['content'],type=1)
ct.read = 1
ct.save()
except:
pass

use python requests module to do this requests has more features then httplib2 and it is very easy to use http://docs.python-requests.org/

I have used httplib2 to accomplish something similar. From the httplib2 documentation try:
import httplib2
import urllib
data = {'name': 'fred', 'address': '123 shady lane'}
body = urllib.urlencode(data)
h = httplib2.Http()
resp, content = h.request("http://example.com", method="POST", body=body)
You should then be able to handle the POST in your second django server, and return the appropriate results to the first django server.

Related

How to get data from external API to Django Rest Framework

I am creating a DRF project (only API's), where logged in user can specify how many people's randomly generated credentials he wants to receive.
Here's my problem:
I want to get these credentials from an external API (https://randomuser.me/api).
This website generates random users data in quantity specified in the url's "results" parameter.
Ex. https://randomuser.me/api/?results=40
My question is:
How can I even get this data? I know JavaScript fetch() method might be useful but I don't actually know how to connect it with Django Rest Framework, and then manipulate it.
I want to show the data to the user after he sends the POST request (only specifying the number of users to be generated) and also save the results in the database, so he can access them later on (through GET request).
If you have any ideas or tips I would be very grateful.
Thank you!
Here is how you can make an API call in a Django Rest Framework API View:
Since you want to store external API request in a database. This is a example of model to store the user result.
models.py
from django.conf import settings
class Credential(models.Models):
""" A user can have many generated credentials """
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
value = models.CharField()
# Here we override the save method to avoid that each user request create new credentials on top of the existing one
def __str__(self):
return f"{self.user.username} - {self.value}"
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
# Assume that you have installed requests: pip install requests
import requests
import json
class GenerateCredential(APIVIew):
""" This view make and external api call, save the result and return
the data generated as json object """
# Only authenticated user can make request on this view
permission_classes = (IsAuthenticated, )
def get(self, request, format=None):
# The url is like https://localhost:8000/api/?results=40
results = self.request.query_params.get('type')
response = {}
# Make an external api request ( use auth if authentication is required for the external API)
r = requests.get('https://randomuser.me/api/?results=40', auth=('user', 'pass'))
r_status = r.status_code
# If it is a success
if r_status = 200:
# convert the json result to python object
data = json.loads(r.json)
# Loop through the credentials and save them
# But it is good to avoid that each user request create new
# credentials on top of the existing one
# ( you can retrieve and delete the old one and save the news credentials )
for c in data:
credential = Credential(user = self.request.user, value=c)
credential.save()
response['status'] = 200
response['message'] = 'success'
response['credentials'] = data
else:
response['status'] = r.status_code
response['message'] = 'error'
response['credentials'] = {}
return Response(response)
class UserCredentials(APIView):
"""This view return the current authenticated user credentials """
permission_classes = (IsAuthenticated, )
def get(self, request, format=None):
current_user = self.request.user
credentials = Credential.objects.filter(user__id=current_user)
return Response(credentials)
NB : These views assume that the user who make the request is authenticated, more infos here. Because we need user to save the retrieved credentials
in the database.
urls.py
path('api/get_user_credentials/', views.UserCredentials.as_view()),
path('api/generate_credentials/', views.GenerateCredentials.as_view()),
.js
const url = "http://localhost:8000/api/generate_credentials/";
# const url = "http://localhost:8000/api/get_user_credentials/";
fetch(url)
.then((resp) => resp.json())
.then(function(data) {
console.log(data);
})
.catch(function(error) {
console.log(error);
});
**how to call external api in Django view**
#convert img in base64string
def encode(img):
with open(img, "rb") as image_file:
encoded_string = base64.b64encode(image_file.read())
return encoded_string
#Django View Function
from django.conf import settings
def store_new_camera(request):
Data = {}
try:
lChannel_no = request.POST.get('name')
lCamera_name = request.POST.get('name')
lCamera_id = request.POST.get('camera_id')
lCompany_id = request.POST.get('company_id')
lWearhouse = request.POST.get('wearhouse')
media_dir = settings.MEDIA_ROOT
image_dir = media_dir + "\\" + str('media') + "\\" + str('entrance_img') + "\\" + str(temp_file_name)
data = encode(image_dir)
picture = data
try:
url = "#BaseURL or endpoint"
payload = '[{\"cameraId\":"' + str(lCamera_id) + '","companyId\":"' + str(lCompany_id) + '",\"name\": "' + str(
lCamera_name) + '",\"warehouse\":"' + str(lWearhouse) + '",\"picture\": "' + str(
picture) + '", \"channelNo\":"' + str(lChannel_no) + '"\r\n\t}]'
headers = {
'HodHodApiKey': 'xyz',
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
except Exception as e:
import os, sys
exception_type, exception_object, exception_traceback = sys.exc_info()
filename = exception_traceback.tb_frame.f_code.co_filename
line_number = exception_traceback.tb_lineno
print("Exception type: ", exception_type)
print("File name: ", filename)
print("Line number: ", line_number)

Add existing API into django rest framework

Besides the API I created by using the django rest framework, now I want to add the existing commerial API into the backend, how to do this?
The commerial API include several part(API), for example, /prepare, /upload, /merge,/get_result, etc. They all use the "POST" indeed.
Do I need to call the commerial API directly on the frontend? Or I need to integrate the commerial API in to one backend API? Any suggestions are appreciated.Thanks.
For example:
```
class TestView(APIView):
"""
Add all the commerial API here in order
/prepare(POST)
/upload(POST)
/merge(POST)
/get_result(POST)
return Result
"""
```
Depending on your needs, I suggest making the external API calls on backend.
As a good practice, you should seperate your external API calls from your views. As it can be messy as the project gets bigger.
Check the sample code of how I manage external API calls, by seperating them to a different file, for example api_client.py
My default api_client.py looks something like this.
(You need to install "requests" pip package by pip install requests)
import requests
from django.conf import settings
class MyApiClient(object):
def __init__(self):
self.base_url = settings.EXTERNAL_API_1.get('url')
self.auth_url = "{0}login/".format(self.base_url)
self.username = settings.EXTERNAL_API_1.get('username')
self.password = settings.EXTERNAL_API_1.get('password')
self.session = None
self.access_token = None
self.token_type = None
self.token_expires_in = None
def _request(self, url, data=None, method="POST", as_json=True):
if self.session is None:
self.session = requests.Session()
if not self.access_token:
self.authenticate()
r = requests.Request(method, url=url, data=data, headers={
"Accept": "application/json",
"Content-Type": "application/json",
'Authorization': 'Token {0}'.format(
self.access_token)
})
prepared_req = r.prepare()
res = self.session.send(prepared_req, timeout=60)
if as_json:
json_result = res.json()
return json_result
return res
def _get(self, url):
return self._request(url=url, method="GET")
def _post(self, url, data):
return self._request(url=url, data=data, method="POST")
def authenticate(self):
res = requests.post(self.auth_url, json={'username': self.username,
'password': self.password},
headers={"Content-Type": "application/json"})
if res.status_code != 200:
res.raise_for_status()
json_result = res.json()
self.access_token = json_result.get('response', None).get('token',None)
def prepare(self):
something = 'bla bla'
request_url = "{0}prepare".format(self.base_url)
result = self._post(url=request_url, data=something)
return result
And in your views.py
from api_client import MyApiClient
class TestView(APIView):
api_client = MyApiClient()
def post(request, *args, **kwargs):
res1 = api_client.prepare()
res2 = api_client.your_other_method()
res3 = api_client.your_last_method()
return Response(res3)
Edited! Hope this helps now :)

Cloud Composer non interactive authentication

I've tried a lot before posting this question, I'm not against down votes, at least let me know WHY you are down voting.
I have built an Airflow plugin to fetch data from Cloud composer Airflow environment and accessing the cloud composer is working great from browsers as it needs the user to sign in before accessing any of the Airflow endpoints.
In my use case, I need to trigger the endpoints via code. Is there a way in which I can do this.
Below is the Airflow-Flask plugin being used
from datetime import datetime
import json
import os
import six
import time
from flask import Blueprint, request, Response
from sqlalchemy import or_
from airflow import settings
from airflow.exceptions import AirflowException, AirflowConfigException
from airflow.models import DagBag, DagRun
from airflow.utils.state import State
from airflow.utils.dates import date_range as utils_date_range
from airflow.www.app import csrf
airflow_api_blueprint = Blueprint('airflow_api', __name__, url_prefix='/api/v1')
class ApiInputException(Exception):
pass
class ApiResponse:
def __init__(self):
pass
STATUS_OK = 200
STATUS_BAD_REQUEST = 400
STATUS_UNAUTHORIZED = 401
STATUS_NOT_FOUND = 404
STATUS_SERVER_ERROR = 500
#staticmethod
def standard_response(status, payload):
json_data = json.dumps({
'response': payload
})
resp = Response(json_data, status=status, mimetype='application/json')
return resp
#staticmethod
def success(payload):
return ApiResponse.standard_response(ApiResponse.STATUS_OK, payload)
#staticmethod
def error(status, error):
return ApiResponse.standard_response(status, {
'error': error
})
#staticmethod
def bad_request(error):
return ApiResponse.error(ApiResponse.STATUS_BAD_REQUEST, error)
#staticmethod
def not_found(error='Resource not found'):
return ApiResponse.error(ApiResponse.STATUS_NOT_FOUND, error)
#staticmethod
def unauthorized(error='Not authorized to access this resource'):
return ApiResponse.error(ApiResponse.STATUS_UNAUTHORIZED, error)
#staticmethod
def server_error(error='An unexpected problem occurred'):
return ApiResponse.error(ApiResponse.STATUS_SERVER_ERROR, error)
#airflow_api_blueprint.before_request
def verify_authentication():
authorization = request.headers.get('authorization')
try:
api_auth_key = settings.conf.get('AIRFLOW_API_PLUGIN', 'AIRFLOW_API_AUTH')
except AirflowConfigException:
return
if authorization != api_auth_key:
return ApiResponse.unauthorized("You are not authorized to use this resource")
def format_dag_run(dag_run):
return {
'run_id': dag_run.run_id,
'dag_id': dag_run.dag_id,
'state': dag_run.get_state(),
'start_date': (None if not dag_run.start_date else str(dag_run.start_date)),
'end_date': (None if not dag_run.end_date else str(dag_run.end_date)),
'external_trigger': dag_run.external_trigger,
'execution_date': str(dag_run.execution_date)
}
def find_dag_runs(session, dag_id, dag_run_id, execution_date):
qry = session.query(DagRun)
qry = qry.filter(DagRun.dag_id == dag_id)
qry = qry.filter(or_(DagRun.run_id == dag_run_id, DagRun.execution_date == execution_date))
return qry.order_by(DagRun.execution_date).all()
#airflow_api_blueprint.route('/dags', methods=['GET'])
def dags_index():
dagbag = DagBag()
dags = []
for dag_id in dagbag.dags:
payload = {
'dag_id': dag_id,
'full_path': None,
'is_active': False,
'last_execution': None,
}
dag = dagbag.get_dag(dag_id)
if dag:
payload['full_path'] = dag.full_filepath
payload['is_active'] = (not dag.is_paused)
payload['last_execution'] = str(dag.latest_execution_date)
if request.args.get('dag_id') is not None:
if request.args.get('dag_id') not in payload['dag_id']:
continue
dags.append(payload)
return ApiResponse.success({'dags': dags})
#airflow_api_blueprint.route('/dag_runs', methods=['GET'])
def get_dag_runs():
dag_runs = []
session = settings.Session()
query = session.query(DagRun)
if request.args.get('state') is not None:
query = query.filter(DagRun.state == request.args.get('state'))
if request.args.get('external_trigger') is not None:
# query = query.filter(DagRun.external_trigger == (request.args.get('external_trigger') is True))
query = query.filter(DagRun.external_trigger == (request.args.get('external_trigger') in ['true', 'True']))
if request.args.get('prefix') is not None:
query = query.filter(DagRun.run_id.ilike('{}%'.format(request.args.get('prefix'))))
if request.args.get('dag_id') is not None:
query = query.filter(DagRun.dag_id.ilike('{}%'.format(request.args.get('dag_id'))))
runs = query.order_by(DagRun.execution_date).all()
for run in runs:
dag_runs.append(format_dag_run(run))
session.close()
return ApiResponse.success({'dag_runs': dag_runs})
#csrf.exempt
#airflow_api_blueprint.route('/dag_runs', methods=['POST'])
def create_dag_run():
# decode input
data = request.get_json(force=True)
# ensure there is a dag id
if 'dag_id' not in data or data['dag_id'] is None:
return ApiResponse.bad_request('Must specify the dag id to create dag runs for')
dag_id = data['dag_id']
limit = 500
partial = False
if 'limit' in data and data['limit'] is not None:
try:
limit = int(data['limit'])
if limit <= 0:
return ApiResponse.bad_request('Limit must be a number greater than 0')
if limit > 500:
return ApiResponse.bad_request('Limit cannot exceed 500')
except ValueError:
return ApiResponse.bad_request('Limit must be an integer')
if 'partial' in data and data['partial'] in ['true', 'True', True]:
partial = True
# ensure there is run data
start_date = datetime.now()
end_date = datetime.now()
if 'start_date' in data and data['start_date'] is not None:
try:
start_date = datetime.strptime(data['start_date'], '%Y-%m-%dT%H:%M:%S')
except ValueError:
error = '\'start_date\' has invalid format \'{}\', Ex format: YYYY-MM-DDThh:mm:ss'
return ApiResponse.bad_request(error.format(data['start_date']))
if 'end_date' in data and data['end_date'] is not None:
try:
end_date = datetime.strptime(data['end_date'], '%Y-%m-%dT%H:%M:%S')
except ValueError:
error = '\'end_date\' has invalid format \'{}\', Ex format: YYYY-MM-DDThh:mm:ss'
return ApiResponse.bad_request(error.format(data['end_date']))
# determine run_id prefix
prefix = 'manual_{}'.format(int(time.time()))
if 'prefix' in data and data['prefix'] is not None:
prefix = data['prefix']
if 'backfill' in prefix:
return ApiResponse.bad_request('Prefix cannot contain \'backfill\', Airflow will ignore dag runs using it')
# ensure prefix doesn't have an underscore appended
if prefix[:-1:] == "_":
prefix = prefix[:-1]
conf = None
if 'conf' in data and data['conf'] is not None:
if isinstance(data['conf'], six.string_types):
conf = data['conf']
else:
try:
conf = json.dumps(data['conf'])
except Exception:
return ApiResponse.bad_request('Could not encode specified conf JSON')
try:
session = settings.Session()
dagbag = DagBag('dags')
if dag_id not in dagbag.dags:
return ApiResponse.bad_request("Dag id {} not found".format(dag_id))
dag = dagbag.get_dag(dag_id)
# ensure run data has all required attributes and that everything is valid, returns transformed data
runs = utils_date_range(start_date=start_date, end_date=end_date, delta=dag._schedule_interval)
if len(runs) > limit and partial is False:
error = '{} dag runs would be created, which exceeds the limit of {}.' \
' Reduce start/end date to reduce the dag run count'
return ApiResponse.bad_request(error.format(len(runs), limit))
payloads = []
for exec_date in runs:
run_id = '{}_{}'.format(prefix, exec_date.isoformat())
if find_dag_runs(session, dag_id, run_id, exec_date):
continue
payloads.append({
'run_id': run_id,
'execution_date': exec_date,
'conf': conf
})
results = []
for index, run in enumerate(payloads):
if len(results) >= limit:
break
dag.create_dagrun(
run_id=run['run_id'],
execution_date=run['execution_date'],
state=State.RUNNING,
conf=conf,
external_trigger=True
)
results.append(run['run_id'])
session.close()
except ApiInputException as e:
return ApiResponse.bad_request(str(e))
except ValueError as e:
return ApiResponse.server_error(str(e))
except AirflowException as e:
return ApiResponse.server_error(str(e))
except Exception as e:
return ApiResponse.server_error(str(e))
return ApiResponse.success({'dag_run_ids': results})
#airflow_api_blueprint.route('/dag_runs/<dag_run_id>', methods=['GET'])
def get_dag_run(dag_run_id):
session = settings.Session()
runs = DagRun.find(run_id=dag_run_id, session=session)
if len(runs) == 0:
return ApiResponse.not_found('Dag run not found')
dag_run = runs[0]
session.close()
return ApiResponse.success({'dag_run': format_dag_run(dag_run)})
For programmatic auth to Composer's Airflow webserver, you will have to authenticate through IAP.
https://cloud.google.com/composer/docs/how-to/using/triggering-with-gcf contains a js example of this.
https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/iap/make_iap_request.py is a Python example, but note that it is missing a timeout on the final IAP request (https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1812).

How to get clean urls?

from flask import Flask, redirect, url_for, session, request, jsonify
from flask_oauthlib.client import OAuth
app = Flask(__name__)
app.config['GOOGLE_ID'] = "12"
app.config['GOOGLE_SECRET'] = "A"BC
app.debug = True
app.secret_key = 'development'
oauth = OAuth(app)
google = oauth.remote_app(
'google',
consumer_key=app.config.get('GOOGLE_ID'),
consumer_secret=app.config.get('GOOGLE_SECRET'),
request_token_params={
'scope': 'email'
},
base_url='https://www.googleapis.com/oauth2/v1/',
request_token_url=None,
access_token_method='POST',
access_token_url='https://accounts.google.com/o/oauth2/token',
authorize_url='https://accounts.google.com/o/oauth2/auth',
)
#app.route('/')
def index():
if 'google_token' in session:
me = google.get('userinfo')
return jsonify({"data": me.data})
return redirect(url_for('login'))
#app.route('/login')
def login():
return google.authorize(callback=url_for('authorized', _external=True))
#app.route('/logout')
def logout():
session.pop('google_token', None)
return redirect(url_for('index'))
#app.route('/login/authorized')
def authorized():
resp = google.authorized_response()
if resp is None:
return 'Access denied: reason=%s error=%s' % (
request.args['error_reason'],
request.args['error_description']
)
session['google_token'] = (resp['access_token'], '')
me = google.get('userinfo')
return jsonify({"data": me.data})
#google.tokengetter
def get_google_oauth_token():
return session.get('google_token')
Here when i am logging via google, my URL changes to something like this:
http://localhost:5000/login/authorized?code=4/U89v8kn76_zspiZUuZwdv01KuifACegxtt7NWBQLF3w#
What I want is what I gave in the URL
http://localhost:5000/login/authorized
What should I do?
This sounds like expected behavior for the callback portion of the auth process.
What you want to do is redirect the user to the main route at the end of the authorized() function. that function more or less "belongs" to the OAuth process (is a good way to think about it). you just determine if the process was successful and then redirect the user where they need to go.
i like to use Message Flashing to communicate with the user during this process.
example:
#app.route('/')
def index():
if 'google_token' not in session:
flash("Please log in to see this page")
return redirect(url_for('login'))
me = google.get('userinfo')
return render_template("index.html", user=me)
#app.route('/login/authorized')
def authorized():
resp = google.authorized_response()
if resp is None:
flash("Access denied: reason={0} error={1}".format(
request.args['error_reason'],
request.args['error_description']
))
return redirect(url_for("login"))
session['google_token'] = (resp['access_token'], '')
flash("Successful login!") # superfluous, just for example
return redirect(url_for("index"))
and you should see here that the session key is present (e.g. the cyrptocookie)... also obviously you should set secret key with os.urandom(24) per the docs

How to get json data from a url using flask in python

I am developing an openerp module.
Someone makes a POST to a url.the POST contains json data:
{
"phone":"987654321",
"message":"this is a test",
"date": "2015/10/09 12:00:00",
}
I want to get the data from the URL,and handle the parameters inside the openerp. This is my code so far :
from flask import Flask, request
app = Flask(__name__)
class myClass():
def __init__(self):
self.HOST = "http://0.0.0.0:8069"
self.DB = 'database'
self.LOGIN = 'admin'
self.PASSWORD = 'admin'
self.phone = None
self.message = None
self.date = None
def authenticate(self):
p = {'jsonrpc': "2.0",
'method': "call",
'params': {
'db': self.DB,
'login': self.LOGIN,
'password': self.PASSWORD}
}
#app.route("/post", methods=['GET', 'POST'])
def post():
sent_obj = myClass()
sent_obj.phone = request.args.get('phone')
sent_obj.message = request.args.get('message')
sent_obj.date = request.args.get('date')
if __name__ == "__main__":
app.debug = True
app.run()
How can i use flask, so i can get the data from the url, authenticate openerp, and handle the data inside the openerp?What is the procedure i have to follow?I am really confused...
Use json.loads() to read and decode the data.
from flask import Flask,jsonify
import requests
import simplejson
import json
app = Flask(__name__)
#app.route("/")
def home():
uri = "https://api.stackexchange.com/2.0/users? order=desc&sort=reputation&inname=fuchida&site=stackoverflow"
try:
uResponse = requests.get(uri)
except requests.ConnectionError:
return "Connection Error"
Jresponse = uResponse.text
data = json.loads(Jresponse)
displayName = data['items'][0]['display_name']# <-- The display name
reputation = data['items'][0]['reputation']# <-- The reputation
return Jresponse
if __name__ == "__main__":
app.run(debug = True)
Use the following code:
json.loads(request.data)