connecting mongodb server via seperate class - python

I am using flask to create simple api. The api simply returns values from mongoDB. Everything works great if i do the connection within same function. I am not doing connection simply at start of file because i am using uwsgi and nginx server on ubuntu. If i do that then there will be a problem of fork.
However, I have to use this connection with other api so thought to make a seperate class for connection and each api will simply call it . I m using this functionality to make codes manageable. However when i try the these codes it always shows internal server error. I tried making this function static too , still the error exists.
Note - I have replaced mongodb address with xxx as i am using mongodbatlas account here
from flask import Flask
from flask import request, jsonify
from flask_pymongo import pymongo
from pymongo import MongoClient
from flask_restful import Resource, Api, reqparse
app = Flask(__name__)
api = Api(app)
#client = MongoClient("xxx")
#db = client.get_database('restdb')
#records = db.stars
class dbConnect():
def connect(self):
client = MongoClient("xxx")
db = client.get_database('restdb')
records = db.stars
return records
class Order(Resource):
def get(self):
#client = MongoClient("xxx")
#db = client.get_database('restdb')
#records = db.stars
#star = records
star = dbConnect.connect
output = []
for s in star.find():
output.append({'name' : s['name'], 'distance' : s['distance']})
return jsonify({'result' : output})
api.add_resource(Order, '/')
if __name__ == "__main__":
app.run(host='0.0.0.0')
ERROR {"message": "Internal Server Error"}

Preliminary investigation suggests that you haven't instantiated your dbConnect class. Also, you haven't called the method connect properly.
class Order(Resource):
def get(self):
db = dbConnect() # This was missing
star = db.connect() # This is how you make method call properly.
output = []
for s in star.find():
output.append({'name' : s['name'], 'distance' : s['distance']})
return jsonify({'result' : output})
Also class dbConnect() should be declared as class dbConnect:.

Related

How to fetch the requested graphQL query attributes in resolver function with Ariadne

I have created a Flask server in Python and used Ariadne to bind Python functions to Apollo GraphQL schema. I have followed this guide - Apollo GraphQL with Python
It's working as expected but I want to query only those fields in my underlying data source which the client requests from the GraphQL query. So, I need to access the requested query in the resolver function, which I am unable to figure out how.
This is my Flask /graphQL API:
import json
from api import app
from ariadne.constants import PLAYGROUND_HTML
from flask import request, jsonify
from ariadne import load_schema_from_path, make_executable_schema, snake_case_fallback_resolvers, graphql_sync, ObjectType
from queries import get_asset_resolver
query = ObjectType("Query")
query.set_field("getAsset", get_asset_resolver)
type_defs = load_schema_from_path("./schema.graphql")
schema = make_executable_schema(type_defs, query, snake_case_fallback_resolvers)
#app.route("/graphql", methods=["GET"])
def graphql_playground():
return PLAYGROUND_HTML, 200
#app.route("/graphql", methods=["POST"])
def graphql_server():
data = request.get_json()
print('GraphQL request: ', data)
success, result = graphql_sync(
schema,
data,
context_value=request,
debug=app.debug
)
print('Result from resolver: ', success, result)
status_code = 200 if success else 400
return jsonify(result), status_code
And this is my resolver function:
from ariadne import convert_kwargs_to_snake_case
import sqlquerybuilder
import runquery
#convert_kwargs_to_snake_case
def get_asset_resolver(obj, info, filters):
# I want to fetch the requested query attributes here
query = sqlquerybuilder.build_query_for_assets(filters)
assets = runquery.run_query_for_assets(query)
return assets

Testing a POST method unit test which inserts data to mongodb database

I want to know how am I supposed to test my code and see whether it works properly. I want to make sure that it stores the received data to the database. Can you please tell me how am I supposed to do that? While I was searching the forum I found this post but I did not really understand what is going on. here is the code I want to test.
client = MongoClient(os.environ.get("MONGODB_URI"))
app.db = client.securify
app.secret_key = str(os.environ.get("APP_SECRET"))
#app.route("/", methods=["GET", "POST"])
def home():
if request.method == "POST":
ip_address = request.remote_addr
entry_content = request.form.get("content")
formatted_date = datetime.datetime.today().strftime("%Y-%m-%d/%H:%M")
app.db.entries.insert({"content": entry_content, "date": formatted_date, "IP": ip_address})
return render_template("home.html")
and here is the mock test I wrote:
import os
from unittest import TestCase
from app import app
class AppTest(TestCase):
# executed prior to each test
def setUp(self):
# you can change your application configuration
app.config['TESTING'] = True
# you can recover a "test cient" of your defined application
self.app = app.test_client()
# then in your test method you can use self.app.[get, post, etc.] to make the request
def test_home(self):
url_path = '/'
response = self.app.get(url_path)
self.assertEqual(response.status_code, 200)
def test_post(self):
url_path = '/'
response = self.app.post(url_path,data={"content": "this is a test"})
self.assertEqual(response.status_code, 200)
The test_post gets stuck and after some seconds gives an error when reaches app.db.entries.insert({"content": entry_content, "date": formatted_date, "IP": ip_address}) part. Please tell me also how can I retrieve the saved data in order to make sure it is saved in the expected way
This is what I do using NodeJS, not tested at all in python but the idea is the same.
First of all, find a in-memory DB, there are options like pymongo-inmemory or mongomock
Then in your code you have to do the connection according to you environment (production/development/whatever)
Something like this:
env = os.environ.get("ENV")
if env == "TESTING":
# connect to mock db
elif env == "DEVELOMPENT":
# for example if you want to test against a real DB but not the production one
# then do the connection here
else:
# connect to production DB
I don't know if it is the proper way to do it but I found a solution. After creating a test client self.app = app.test_client() the db gets set to localhost:27017 so I changed it manually as follows and it worked:
self.app = app.test_client()
client = MongoClient(os.environ.get("MONGODB_URI"))

Using Pytest to monkeypatch an initialized redis connection in a flask application

I've been struggling with this for awhile now. I Have a flask app that is executed in my app.py file. In this file I have a bunch of endpoints that call different functions from other files. In another file, extensions.py, I've instantiated a class that contains a redis connection. See the file structure below.
#app.py
from flask import Flask
from extensions import redis_obj
app = Flask(__name__)
#app.route('/flush-cache', methods=['POST'])
def flush_redis():
result = redis_obj.flush_redis_cache()
return result
# extensions.py
from redis_class import CloudRedis
redis_obj = CloudRedis()
# redis_class
import redis
class CloudRedis:
def __init__(self):
self.conn = redis.Redis(connection_pool=redis.ConnectionPool.from_url('REDIS_URL',
ssl_cert_reqs=None))
def flush_redis_cache(self):
try:
self.conn.flushdb()
return 'OK'
except:
return 'redis flush failed'
I've been attempting to use monkeypatching in a test patch flush_redis_cache, so when I run flush_redis() the call to redis_obj.flush_redis_cache() will just return "Ok", since I've already tested the CloudRedis class in other pytests. However, no matter what I've tried I haven't been able to successfully patch this. This is what I have below.
from extensions import redis_obj
from app import app
#pytest.fixture()
def client():
yield app.test_client()
def test_flush_redis_when_redis_flushed(client, monkeypatch):
# setup
def get_mock_flush_redis_cache():
return 'OK'
monkeypatch.setattr(cloud_reids, 'flush_redis_cache', get_mock_flush_redis_cache)
cloud_redis.flush_redis = get_mock_flush_redis_cache
# act
res = client.post('/flush-cache')
result = flush_redis()
Does anyone have any ideas on how this can be done?

AttributeError: 'str' object has no attribute 'client'

I have a code where, I am establishing connection with MongoDB. This code is
ConnectMongoDB.py:
import pymongo
from pymongo import MongoClient
from flask import Flask, render_template, request,redirect,url_for
app = Flask(__name__)
# Connection to MongoDB
class ConnectMdb:
#staticmethod
def connect2mongodb():
global client
try:
client = pymongo.MongoClient("mongodb") # modified to avoid showing actual string. Kindly ignore this part.
print("Connected to Avengers MongoClient Successfully!!!")
print (type(client))
print(client)
except:
print("Connection to MongoClient Failed!!!")
#db = client.avengers_hack_db
return("Connection established")
if __name__ == '__main__':
ConnectMdb.connect2mongodb()
I import this script in another program which has some business logic. Here is some part of the code which is relevant to this issue:
ProcessData.py:
import pymongo
from pymongo import MongoClient
import datetime
import sys
from flask import Flask, render_template, request
#import ProcessTaskData
#import werkzeug
import ConnectMongoDB as cDB
app = Flask(__name__)
CMdb = cDB.ConnectMdb.connect2mongodb()
db = CMdb.client.avengers_hack_db
#app.route('/')
def index():
return render_template('index.html')
#app.route('/Avengers',methods = ['POST'])
def Avengers():
ip = request.remote_addr
Project_ID = request.form['pid']
Name = request.form['pname']
Resource_Names = request.form['rsrc']
db.ppm_master_db_collection.insert_many([
{"Org_Id":"",
"Name": Name,
"last_modified": datetime.datetime.utcnow()}
])
return render_template('ptasks.html', user_ip=ip)
#app.route('/ProjectTasks',methods = ['POST'])
def ProjectTask():
ip = request.remote_addr
Task_ID = request.form['tid']
TAlert = request.form['talrt']
TResource_Names = request.form['trsrc']
db.ppm_tasks_data_collection.insert_many([
{"Task_ID": Task_ID,
"Resource_Names": TResource_Names,
"last_modified": datetime.datetime.utcnow()}
])
return render_template('ptasks.html', user_ip=ip)
if __name__ == '__main__':
app.run(debug = True)
If I put the code from ConnectMongoDB.py directly in the ProcessData.py rather than importing, it works good. But from separate file it errors.
Also, client is of type:
<class 'pymongo.mongo_client.MongoClient'>
Ideally it is expected to behave normally (establish connection to db as well) like when the code is in the ProcessData.py. Not sure where am I missing.
Changing
db = CMdb.client.avengers_hack_db
to
db = cDB.client.avengers_hack_db
should make your error go away, you are referencing the wrong thing. The return value of your staticmethod is a string, and it has no client attribute.
A bit better approach would be if your connect2mongodb method would return client:
class ConnectMdb:
#staticmethod
def connect2mongodb():
try:
client = pymongo.MongoClient("mongodb") # modified to avoid showing actual string. Kindly ignore this part.
print("Connected to Avengers MongoClient Successfully!!!")
print (type(client))
print(client)
except:
raise Exception("Connection to MongoClient Failed!!!")
return client
This way db = CMdb.client.avengers_hack_db would work.

Flask RESTful API: Issue with db connection pool

I'm trying to create REST API endpoints using flask framework. This is my fully working script:
from flask import Flask, jsonify
from flask_restful import Resource, Api
from flask_restful import reqparse
from sqlalchemy import create_engine
from flask.ext.httpauth import HTTPBasicAuth
from flask.ext.cors import CORS
conn_string = "mssql+pyodbc://x:x#x:1433/x?driver=SQL Server"
auth = HTTPBasicAuth()
#auth.get_password
def get_password(username):
if username == 'x':
return 'x'
return None
app = Flask(__name__)
cors = CORS(app)
api = Api(app)
class Report(Resource):
decorators = [auth.login_required]
def get(self):
parser = reqparse.RequestParser()
parser.add_argument('start', type = str)
parser.add_argument('end', type = str)
args = parser.parse_args()
e = create_engine(conn_string)
conn = e.connect()
stat = """
select x from report
"""
query = conn.execute(stat)
json_dict = []
for i in query.cursor.fetchall():
res = {'x': i[0], 'xx': i[1]}
json_dict.append(res)
conn.close()
e.dispose()
return jsonify(results=json_dict)
api.add_resource(Report, '/report')
if __name__ == '__main__':
app.run(host='0.0.0.0')
The issue is that I get results when I call this API only for a day or so after which I stop getting results unless I restart my script (or sometimes even my VM) after which I get results again. I reckon there is some issue with the database connection pool or something but I'm closing the connection and disposing it as well. I have no idea why the API gives me results only for some time being because of which I have to restart my VM every single day. Any ideas?
Per my experience, the issue was caused by coding create_engine(conn_string) to create db pool inside the Class Report so that always do the create & destory operations of db pool for per restful request. It's not correct way for using SQLAlchemy ORM, and be cause IO resouce clash related to DB connection, see the engine.dispose() function description below at http://docs.sqlalchemy.org/en/rel_1_0/core/connections.html#sqlalchemy.engine.Engine:
To resolve the issue, you just need to move e = create_engine(conn_string) to the below of the code conn_string = "mssql+pyodbc://x:x#x:1433/x?driver=SQL Server" and remove the code e.dispose() both in the Class Report, see below.
conn_string = "mssql+pyodbc://x:x#x:1433/x?driver=SQL Server"
e = create_engine(conn_string) # To here
In the def get(delf) function:
args = parser.parse_args()
# Move: e = create_engine(conn_string)
conn = e.connect()
and
conn.close()
# Remove: e.dispose()
return jsonify(results=json_dict)

Categories

Resources