how to serialize python classes? - python

I have a model class defined as;
class SiteConfig(object):
def __init__(self,command,category,frequency,delay):
self.command=command
self.category=category
self.frequency=frequency
self.delay=delay
im calling this model from another class like;
from flask import Flask,request,jsonify
import httplib
from models.SiteConfig import *
app = Flask(__name__)
#app.route('/')
def index():
return "Endpoint is not configured", 500
#app.route('/SitePollingConfiguration',methods=['GET'])
def SitePollingConfiguration():
response = jsonify(statusCode=httplib.OK, config=BuildSiteConfig())
response.status_code = httplib.OK
return response
def BuildSiteConfig():
a= SiteConfig('20c','1',60,60)
print (a)
return a
if __name__ == '__main__':
app.run()
But im getting SiteConfig is not JSON serializable error.
How can i serialize the class object in python?
EDIT
i try to use existing flask json encoder;
modified my siteconfig class like;
from flask import Flask, jsonify
from flask.json import JSONEncoder
class JsonSerializable(object):
def toJson(self):
return JSONEncoder.default(self.__dict__)
class SiteConfig(JsonSerializable):
def __init__(self,command,category,frequency,delay):
self.command=command
self.category=category
self.frequency=frequency
self.delay=delay
And then calling in my main class like;
def ATGSitePollingConfiguration():
response = jsonify(statusCode=httplib.OK, config=BuildATGSiteConfig())
response.status_code = httplib.OK
return response
def BuildATGSiteConfig():
a= SiteConfig('20c','1',60,60)
print (a)
return a
But still serializable issue. Sorry im new to python;
can anyone provide simple code sample on how to achieve this?

By default, flask uses flask.json.JSONEncoder to encode objects as JSON. In order to jsonify your user-defined classes you will need to create a subclass of JSONEncoder that implements the serialisation of your class. How you implement the serialisation is up to you, but the easiest method is probably to convert your object into something that JSONEncoder already knows about (like a dict).
Try adding this code to your app. (You should use the first version of the SiteConfig class that you posted, not your modified one).
from flask.json import JSONEncoder
# A customized JSON encoder that knows about your SiteConfig class
class CustomJSONEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, SiteConfig):
return obj.__dict__
return JSONEncoder.default(self, obj)
# Tell your flask app to use your customised JSON encoder
app.json_encoder = CustomJSONEncoder
With this you should get the response:
{
"config": {
"category": "1",
"command": "20c",
"delay": 60,
"frequency": 60
},
"statusCode": 200
}
See here for a similar example.

You may also consider jsonpickle:
import jsonpickle
frozen = jsonpickle.encode(a)

Related

How do I access a context_processor from within a method route in Flask?

I'd like to setup variables available to both my views and my methods without polluting the request object in a before_request decorator.
Context processors seems like a nice way to do this however, I can't figure out how to actually access them from within my methods.
The best solution I have come up with is to memoize the context function so that it doesn't get called twice, once by me in my method and then again when Flask injects it into the template.
However, this will cache the method for all future requests and I only want it cached per request.
Here is my working example
from functools import cache
#app.context_processor
#cache
def setup_context():
return {
'planet': db.query('select planet from planets order by random()').first()
}
#app.route("/")
def index():
ctx = setup_context()
if ctx['planet'] == 'pluto':
return redirect('/pluto-is-not-a-planet')
return render_template('planet_greeting.html')
Any ideas on how to accomplish this without using functools.cache?
There may be a more elegant way to do this but here is what I have come up with so far.
The basic idea is to use the extensions pattern and create an object "Current" that the app gets passed to.
I can then use properties on this object to access the _app_ctx_stack as well as populate templates with context variables using the context_processor hook.
This approach will allow me to have templates that don't use "g" and a nice object to work with in my routes.
from flask import (
Flask, current_app, _app_ctx_stack,
render_template as template
)
from random import shuffle
planets = ['earth', 'pluto', 'mars']
class Current(object):
def __init__(self, app=None):
self.app = app
self.app.context_processor(self.context_processor)
def context_processor(self):
return _app_ctx_stack.top.__dict__
#property
def planet(self):
ctx = _app_ctx_stack.top
if not hasattr(ctx, 'planet'):
shuffle(planets)
ctx.planet = {
'name': planets[0]
}
return ctx.planet
app = Flask(__name__)
current = Current(app)
#app.route("/")
def index():
if current.planet['name'] == 'pluto':
return "Pluto is not a planet!"
return template("planet.html")
if __name__ == '__main__':
app.run(debug=True)
And in my template
{%# in my template %}
The planet is {{ planet.name }}!

Serialize UUID objects in flask-restful

I have a flask-restful project that interfaces with some custom classes, containing uuid (uuid.UUID) types used as ids. There are a couple of api endpoints which return the object associated with the given id, parsed by flask as an UUID. The issue is that, when I return them as a json payload, I get the following exception:
UUID('…') is not JSON serializable
I want to have those uuids represented as strings to the final user, making the process seamless (the user can take the returned uuid and use it for his next api request).
In order to fix this problem, I had to put together suggestions from two different places:
first, I need to create a custom json encoder, which when dealing with uuids, returns their string representation. StackOverflow answer here
import json
from uuid import UUID
class UUIDEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, UUID):
# if the obj is uuid, we simply return the value of uuid
return str(obj) # <- notice I'm not returning obj.hex as the original answer
return json.JSONEncoder.default(self, obj)
second, I need to take this new encoder and set it as the flask-restful encoder used for the responses. GitHub answer here
class MyConfig(object):
RESTFUL_JSON = {'cls': MyCustomEncoder}
app = Flask(__name__)
app.config.from_object(MyConfig)
api = Api(app)
putting it together:
# ?: custom json encoder to be able to fix the UUID('…') is not JSON serializable
class UUIDEncoder(json.JSONEncoder):
def default(self, obj: Any) -> Any: # pylint:disable=arguments-differ
if isinstance(obj, UUID):
return str(obj) # <- notice I'm not returning obj.hex as the original answer
return json.JSONEncoder.default(self, obj)
# ?: api configuration to switch the json encoder
class MyConfig(object):
RESTFUL_JSON = {"cls": UUIDEncoder}
app = Flask(__name__)
app.config.from_object(MyConfig)
api = Api(app)
on a side note, if you are using vanilla flask, the process is simpler, just set your app json encoder directly (app.json_encoder = UUIDEncoder)
I hope it's useful to someone!

Flask Create global response function to be called from every where in the web app

new to flask , i have web app that containes few parts
for example:
utils/
-- utils.py
db/
-- oracle.py
main.py
My Question is how can i make global_response function/handler that can be called from : utils.py,oracle.py,main.py without threading problem
for example:
in utils.py
clas Utils():
def A(self):
return global_response("error","a")
oracle.py
clas Utils():
def ORA(self):
return global_response("error ora","b")
main.py
def some_fun(self):
return global_response("error main","c")
and here is the response function which was in main.py but now needs to be called from any where :
def global_response(error_msg, title,*options):
res = {"status": error_msg, "title": title}
for option in options:
res.update(option)
return json.dumps(res)
Instead of creating a response function, you can create a custom response class which will replace flask's builtin Response class.
The class will inherit flask's Response class, meaning:
class GlobalResponse(Response):
pass
In order to generate the generic response, you should call the app.make_response method.
You can view This link for a walk through of how to create a response class.
Side notes
I would recommended you to check out flask's jsonify function which wraps the json.dumps and is considered "stronger" (works for more cases).
You may want to look at flask's make_response method.

TypeError: Collection(Database(MongoClient("localhost", 27017), u'demo_database'), u'entries) is not JSON serializable

Good afternoon.
I'm trying to combine Python, MongoDB (via pymongo) and Flask to create client-server application. I want to use one of methods to return all the entire collection, like here:
#app.route('/entries', methods = ['GET'])
def get_entries():
client = MongoClient(db_host, db_port)
db_demo = client['demo_database']
entries = db_demo.entries
return JSONEncoder().encode(entries)
I also have an Encoder class, as advised here:
class JSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, ObjectId):
return str(o)
return json.JSONEncoder.default(self, o)
Data collection is very simple - actually, only one item with few fields. What am I doing wrong? Perhaps I should develop more sophisticated encoder class?
Use bson.json_util.dumps, which already supports all the MongoDB extended JSON types:
>>> from bson.json_util import dumps
>>> c.test.test.insert_many([{} for _ in range(3)])
<pymongo.results.InsertManyResult object at 0x7f6ed3189550>
>>> dumps(c.test.test.find())
'[{"_id": {"$oid": "554faa99fa5bd8782e1698cf"}}, {"_id": {"$oid": "554faa99fa5bd8782e1698d0"}}, {"_id": {"$oid": "554faa99fa5bd8782e1698d1"}}]'
Using a combination of both approaches, I prefer the solution that I provided here
from flask import Flask
from flask.json import JSONEncoder
from bson import json_util
from . import resources
# define a custom encoder point to the json_util provided by pymongo (or its dependency bson)
class CustomJSONEncoder(JSONEncoder):
def default(self, obj): return json_util.default(obj)
application = Flask(__name__)
application.json_encoder = CustomJSONEncoder
if __name__ == "__main__":
application.run()

Celery: is there a way to write custom JSON Encoder/Decoder?

I have some objects I want to send to celery tasks on my application. Those objects are obviously not json serializable using the default json library. Is there a way to make celery serialize/de-serialize those objects with custom JSON Encoder/Decoder?
A bit late here, but you should be able to define a custom encoder and decoder by registering them in the kombu serializer registry, as in the docs: http://docs.celeryproject.org/en/latest/userguide/calling.html#serializers.
For example, the following is a custom datetime serializer/deserializer (subclassing python's builtin json module) for Django:
myjson.py (put it in the same folder of your settings.py file)
import json
from datetime import datetime
from time import mktime
class MyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return {
'__type__': '__datetime__',
'epoch': int(mktime(obj.timetuple()))
}
else:
return json.JSONEncoder.default(self, obj)
def my_decoder(obj):
if '__type__' in obj:
if obj['__type__'] == '__datetime__':
return datetime.fromtimestamp(obj['epoch'])
return obj
# Encoder function
def my_dumps(obj):
return json.dumps(obj, cls=MyEncoder)
# Decoder function
def my_loads(obj):
return json.loads(obj, object_hook=my_decoder)
settings.py
# Register your new serializer methods into kombu
from kombu.serialization import register
from .myjson import my_dumps, my_loads
register('myjson', my_dumps, my_loads,
content_type='application/x-myjson',
content_encoding='utf-8')
# Tell celery to use your new serializer:
CELERY_ACCEPT_CONTENT = ['myjson']
CELERY_TASK_SERIALIZER = 'myjson'
CELERY_RESULT_SERIALIZER = 'myjson'

Categories