Serialize Django Request - django

I'd like to serialize the Django request in order to log it in a DB. I tried different approaches but none of them successfully.
class RunTest(View):
def get(self, request, url):
srd = serializers.serialize('json', request)
return HttpResponse(json.dumps(request.META))
But this raise the error
module 'rest_framework.serializers' has no attribute 'serialize'
Probably because I'm using the rest-framework as a Middleware.
I also used
srd = json.dumps(request)
In this case the error is
Object of type 'WSGIRequest' is not JSON serializable
Any ideas how to fix this?

I've faced similar problem when trying to store received requests META data in JSONField. Problem is that request.META is a dict but it's not appropriate JSON.
Example request.META I have is:
{
"wsgi.version": (1, 0),
"wsgi.url_scheme": "http",
"wsgi.input": <_io.BufferedReader name=10>,
"wsgi.errors": <_io.TextIOWrapper name="<stderr>" mode="w" encoding="utf-8">,
"wsgi.multithread": True,
"wsgi.multiprocess": False,
"wsgi.run_once": False,
"SERVER_SOFTWARE": "Werkzeug/1.0.1",
"REQUEST_METHOD": "POST",
"SCRIPT_NAME": "",
"PATH_INFO": "/api/v1/vouchers/voucher-distribute/",
"QUERY_STRING": "",
"REQUEST_URI": "/api/v1/vouchers/voucher-distribute"
...
}
So as you can see first few keys with wsgi prefix is inappropriate JSON format what you can also check online at: http://json.parser.online.fr/
So to store request.META as JSON dict it's necessary to get rid of this keys. The trick is that you cannot use request.META.pop("wsgi.version") because request.META is not appropriate JSON format :)
What I did is I've created helper function:
def create_request_meta_json_object(meta_data):
return {
"REQUEST_METHOD": meta_data["REQUEST_METHOD"],
"SERVER_SOFTWARE": meta_data["SERVER_SOFTWARE"],
"REQUEST_METHOD": meta_data["REQUEST_METHOD"],
"SCRIPT_NAME": meta_data["SCRIPT_NAME"],
"PATH_INFO": meta_data["PATH_INFO"],
"QUERY_STRING": meta_data["QUERY_STRING"],
"REQUEST_URI": meta_data["REQUEST_URI"],
"RAW_URI": meta_data["RAW_URI"],
"REMOTE_ADDR": meta_data["REMOTE_ADDR"],
"REMOTE_PORT": meta_data["REMOTE_PORT"],
"SERVER_NAME": meta_data["SERVER_NAME"],
"SERVER_PORT": meta_data["SERVER_PORT"],
"SERVER_PROTOCOL": meta_data["SERVER_PROTOCOL"],
"HTTP_X_FORWARDED_HOST": meta_data["HTTP_X_FORWARDED_HOST"],
"HTTP_X_FORWARDED_PORT": meta_data["HTTP_X_FORWARDED_PORT"],
"HTTP_ACCEPT_ENCODING": meta_data["HTTP_ACCEPT_ENCODING"],
"HTTP_USER_AGENT": meta_data["HTTP_USER_AGENT"],
"HTTP_FROM": meta_data["HTTP_FROM"],
"HTTP_ACCEPT": meta_data["HTTP_ACCEPT"],
"CONTENT_TYPE": meta_data["CONTENT_TYPE"],
"CONTENT_LENGTH": meta_data["CONTENT_LENGTH"],
"HTTP_CONNECTION": meta_data["HTTP_CONNECTION"],
"HTTP_X_NGINX_PROXY": meta_data["HTTP_X_NGINX_PROXY"],
"HTTP_X_FORWARDED_PROTO": meta_data["HTTP_X_FORWARDED_PROTO"],
"HTTP_X_FORWARDED_FOR": meta_data["HTTP_X_FORWARDED_FOR"],
"HTTP_X_REAL_IP": meta_data["HTTP_X_REAL_IP"],
}
and use it like:
meta_data_as_json = create_request_meta_json_object(request.META)

You cannot serialize request - you could serialize request.GET also know as request.query_paramsin DRF.
srd = json.dumps(request.query_params)
or
srd = json.dumps(request.GET)
To use a serializer, you first have to create one. Declaring Serializers is a good starting point.
Another potential solution is to use dictionary comprehension:
meta_keys = {
"wsgi.version": (1, 0),
"wsgi.version": "http",
"wsgi.input": object(),
"wsgi.errors": object(),
"wsgi.multithread": True,
"wsgi.multiprocess": False,
"wsgi.run_once": False,
"SERVER_SOFTWARE": "Werkzeug/1.0.1",
"REQUEST_METHOD": "POST",
"SCRIPT_NAME": "",
"PATH_INFO": "/api/v1/vouchers/voucher-distribute/",
"QUERY_STRING": "",
"REQUEST_URI": "/api/v1/vouchers/voucher-distribute"
}
excluded_meta_keys = ['wsgi.version', 'wsgi.version', 'wsgi.input', 'wsgi.multithread', 'wsgi.multiprocess', 'wsgi.run_once', 'wsgi.errors']
print({key: value for key, value in meta_keys.items() if key not in excluded_meta_keys})
print({key: value for key, value in meta_keys.items() if isinstance(value, (str, bool, int, float))})
results in:
{'SERVER_SOFTWARE': 'Werkzeug/1.0.1', 'REQUEST_METHOD': 'POST', 'SCRIPT_NAME': '', 'PATH_INFO': '/api/v1/vouchers/voucher-distribute/', 'QUERY_STRING': '', 'REQUEST_URI': '/api/v1/vouchers/voucher-distribute'}
{'wsgi.version': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.run_once': False, 'SERVER_SOFTWARE': 'Werkzeug/1.0.1', 'REQUEST_METHOD': 'POST', 'SCRIPT_NAME': '', 'PATH_INFO': '/api/v1/vouchers/voucher-distribute/', 'QUERY_STRING': '', 'REQUEST_URI': '/api/v1/vouchers/voucher-distribute'}
respectively

request.META has keys of types not allowed by a json object, so you can generate another dictionary only with keys with values of str type:
dict = create_request_meta_json_object(request.META)
def create_request_meta_json_object(meta_data):
diccionario = []
for key, value in meta_data.items():
if type(value) is str:
diccionario.append({"key": key, "value": value})
return diccionario

Related

django : Save Buffer in FileField

I need to save file received from an express(nodejs) server in django file field format
Django==2.0
class DoctorFileUpload(views.APIView):
permission_classes = (AllowAny, )
def post(self, request, *args, **kwargs):
fileFromRequest = request.data['doctorfile']
newFile = dict()
newFile['format'] = fileFromRequest['mimetype']
newFile['doctor'] = request.userprofile.doctoraccount.id
newFile['file'] = ContentFile(
fileFromRequest['buffer'], name=fileFromRequest['originalname'])
doctorfile_srz = DoctorFileSrz(data=newFile)
if doctorfile_srz.is_valid():
doctorfile_srz.save()
return toolsViews.ResponseHandler(
status.HTTP_202_ACCEPTED,
{
'doctorfile': doctorfile_srz.data
}
)
else:
return toolsViews.ResponseHandler(
status.HTTP_400_BAD_REQUEST,
{},
errors=doctorfile_srz.errors
)
I need to save a file in a FileField Django Model, there is the request :
{
doctorfile: {
fieldname: 'file',
originalname: 'Capture d’écran 2022-10-06 à 10.33.56.png',
encoding: '7bit',
mimetype: 'image/png',
buffer: { type: 'Buffer', data: [Array] },
size: 134774
},
category: 'OT'
}
I don't know how to do, i checked documentation but i didn't find the solution...
Do i need to convert to b64 format or other format before saving?
here is the error when i tried the code :
TypeError: a bytes-like object is required, not 'dict'

Django rest framework - serializer for dictionary structured payload

I'm trying to create a Serializer for a payload that looks something like this -
{
"2fd08845-9b21-4972-87ed-2e7fd03448c5": {
"operation": "Create",
"operationId": "356f6501-a117-4c8d-98ce-dcb4344d481b",
"user": "superuser",
"immediate": "true"
},
"fe6d0c85-0021-431e-9955-e8e1b1ebc414": {
"operation": "Create",
"operationId": "adcedb2f-c751-441f-8108-2c29667ea9cf",
"user": "employee",
"immediate": "false"
}
}
I thought of using DictField, but my problem is that there isn't a field name. it's only a dictionary of keys and values.
I tried something like:
class UserOperationSerializer(serializers.Serializer):
operation = serializers.ChoiceField(choices=["Create", "Delete"])
operationId = serializers.UUIDField()
user = serializers.CharField()
immediate = serializers.BooleanField()
class UserOperationsSerializer(serializers.Serializer):
test = serializers.DictField(child=RelationshipAuthorizeObjectSerializer())
But again, there isn't a 'test' field.
I think your easiest path forward would be to flatten the payload to the following format:
[
{
"request_id": "2fd08845-9b21-4972-87ed-2e7fd03448c5",
"operation": "Create",
"operationId": "356f6501-a117-4c8d-98ce-dcb4344d481b",
"user": "superuser",
"immediate": "true"
},
{
"request_id": "fe6d0c85-0021-431e-9955-e8e1b1ebc414",
"operation": "Create",
"operationId": "adcedb2f-c751-441f-8108-2c29667ea9cf",
"user": "employee",
"immediate": "false"
}
]
And then serialize it. Otherwise, you'd be creating custom fields/serializers which is not pretty.
The way I finally solved it was to add a dynamic 'body' field that contains the real payload of the request.
class UserOperationSerializer(serializers.Serializer):
operation = serializers.ChoiceField(choices=["Create", "Delete"])
operationId = serializers.UUIDField()
user = serializers.CharField()
immediate = serializers.BooleanField()
class UserOperationsSerializer(serializers.Serializer):
body = serializers.DictField(child=UserOperationSerializer())
def __init__(self, *args, **kwargs):
kwargs['data'] = {'body': kwargs['data']}
super().__init__(*args, **kwargs)
Then, in the View, I will use that data as serializer.validated_data['body']
That did the work for me.

Test datetime object using flask and mockupdb (mongodb)

I'm testing MongoDB as DB with a Flask's REST Server (and flask-pymongo), using the mockupdb module. I want to receive a DateTime in the json request, and store it as Date object, to perform some range query using this field in the future, so, I send the data as EJSON (BSON) to keep the data exactly as I.
This is the testcase:
#pytest.fixture()
def client_and_mongoserver():
random.seed()
mongo_server = MockupDB(auto_ismaster=True, verbose=True)
mongo_server.run()
config = Config()
config.MONGO_URI = mongo_server.uri + '/test'
flask_app = create_app(config)
flask_app.testing = True
client = flask_app.test_client()
yield client, mongo_server
mongo_server.stop()
def test_insert(client_and_mongoserver):
client, server = client_and_mongoserver
headers = [('Content-Type', 'application/json')]
id = str(uuid.uuid4()).encode('utf-8')[:12]
now = datetime.now()
obj_id = ObjectId(id)
toInsert = {
"_id": obj_id,
"datetime": now
}
toVerify = {
"_id": obj_id,
"datetime": now
}
future = go(client.post, '/api/insert', data=dumps(toInsert), headers=headers)
request = server.receives(
OpMsg({
'insert': 'test',
'ordered': True,
'$db': "test",
'$readPreference': {"mode": "primary"},
'documents': [
toVerify
]
}, namespace='test')
)
request.ok(cursor={'inserted_id': id})
# act
http_response = future()
# assert
data = http_response.get_data(as_text=True)
This is the endpoint. Before the insert call I convert the datetime string to datetime object:
from flask_restful import Resource
from bson import json_util
class Insert(Resource):
def post(self):
if not request.json:
abort(400)
json_data = json_util.loads(request.data)
result = mongo.db.test.insert_one(json_data)
return {'message': 'OK'}, 200
But the test generate this assertion:
self = MockupDB(localhost, 37213)
args = (OpMsg({"insert": "test", "ordered": true, "$db": "test", "$readPreference": {"mode": "primary"}, "documents": [{"_id": {"$oid": "63343264363661622d393764"}, "datetime": {"$date": 1543493218306}}]}, namespace="test"),)
kwargs = {}, timeout = 10, end = 1543504028.309115
matcher = Matcher(OpMsg({"insert": "test", "ordered": true, "$db": "test", "$readPreference": {"mode": "primary"}, "documents": [{"_id": {"$oid": "63343264363661622d393764"}, "datetime": {"$date": 1543493218306}}]}, namespace="test"))
request = OpMsg({"insert": "test", "ordered": true, "$db": "test", "$readPreference": {"mode": "primary"}, "documents": [{"_id": {"$oid": "63343264363661622d393764"}, "datetime": {"$date": 1543493218306}}]}, namespace="test")
def receives(self, *args, **kwargs):
"""Pop the next `Request` and assert it matches.
Returns None if the server is stopped.
Pass a `Request` or request pattern to specify what client request to
expect. See the tutorial for examples. Pass ``timeout`` as a keyword
argument to override this server's ``request_timeout``.
"""
timeout = kwargs.pop('timeout', self._request_timeout)
end = time.time() + timeout
matcher = Matcher(*args, **kwargs)
while not self._stopped:
try:
# Short timeout so we notice if the server is stopped.
request = self._request_q.get(timeout=0.05)
except Empty:
if time.time() > end:
raise AssertionError('expected to receive %r, got nothing' % matcher.prototype)
else:
if matcher.matches(request):
return request
else:
raise AssertionError('expected to receive %r, got %r'
> % (matcher.prototype, request))
E AssertionError: expected to receive OpMsg({"insert": "test", "ordered": true, "$db": "test", "$readPreference": {"mode": "primary"}, "documents": [{"_id": {"$oid": "63343264363661622d393764"}, "datetime": {"$date": 1543493218306}}]}, namespace="test"), got OpMsg({"insert": "test", "ordered": true, "$db": "test", "$readPreference": {"mode": "primary"}, "documents": [{"_id": {"$oid": "63343264363661622d393764"}, "datetime": {"$date": 1543493218306}}]}, namespace="test")
.venv/lib/python3.6/site-packages/mockupdb/__init__.py:1291: AssertionError
The value match but the assertion is raised either way.
How can I test the Date object using flask?
EDIT:
As pointed out by #bauman.space. The lack of:
'$db': 'test', # this key appears somewhere at the driver
'$readPreference': {"mode": "primary"}, # so does this one
Don't affect the validation made by mockupdb. I'd tested that in other test cases.
EDIT 2: Change question to prevent confusion
your assertion is quite descriptive
AssertionError:
expected to receive
OpMsg(
{"insert": "test",
"ordered": true,
"documents": [{"_id": "a3dbe8a7e1cc43469b706a8877b0a14a",
"datetime": {"$date": 1542901445120}}]
}, namespace="test"
),
got
OpMsg(
{"insert": "test",
"ordered": true,
"$db": "test",
"$readPreference": {"mode": "primary"},
"documents": [{"_id": "a3dbe8a7e1cc43469b706a8877b0a14a",
"datetime": {"$date": 1542901445120}}]
}, namespace="test")
looks like you simply need to include some of the standard MongoDB keys in your verification code.
Swap yours out with this and give it a try?
request = server.receives(
OpMsg({
'insert': 'test',
'ordered': True,
'$db': 'test', # this key appears somewhere at the driver
'$readPreference': {"mode": "primary"}, # so does this one
'documents': [
toVerify
]
}, namespace='test')
)

in tastypie, how can i set name for json result

In tastypie, I want set json result name.
I have a class that I use for it but I can set name in.
enter cclass ContentResource(ModelResource):
class Meta:
results = ListField(attribute='results')
queryset = Content.objects.all()
resource_name = 'content'
max_limit = None
#filtering = {"title": "contains"}
def alter_list_data_to_serialize(self, request, data_dict):
if isinstance(data_dict, dict):
if 'meta' in data_dict:
# Get rid of the "meta".
del(data_dict['meta'])
# Rename the objects.
data_dict['Mobile'] = data_dict['objects']
del(data_dict['objects'])
return data_dict
ode here it returns this
{"Mobile":
[
{
"added": "2015-07-23T11:30:20.911835",
"content_cast": "",
"content_company": "HamrahCinema",
"content_description": "so nice",
"content_director": "",
"content_duration": "2:20",
"content_filelanguage": null,
}
]
}
when I use /content/api/content every thing is ok, but when I use /content/api/content/1,"mobile" is removed.
as educated guess, I would suggest using alter_detail_data_to_serialize

Troposphere Add S3 LifecycleRule to the class LifecycleConfiguration

Method Signature:
I have tried to change the implementation some different ways, but its not helping.
import troposphere.s3 as S3
class LifecycleConfiguration(AWSProperty):
props = {
'Rules': ([LifecycleRule], True),
}
class LifecycleRule(AWSProperty):
props = {
'ExpirationDate': (basestring, False),
'ExpirationInDays': (positive_integer, False),
'Id': (basestring, False),
'Prefix': (basestring, False),
'Status': (basestring, True),
'Transition': (LifecycleRuleTransition, False),
}
Test Implimentation
myLifecycleConfiguration = S3.LifecycleConfiguration(title='myLifecycleConfiguration',
Rules=S3.LifecycleRule(title="check"))
Error Signature
TypeError: Rules is <class 'troposphere.s3.LifecycleRule'>, expected [<class 'troposphere.s3.LifecycleRule'>]
That's normal, and the error message is telling you what the problem is : the 'Rules' property of a LifecycleConfiguration is expected to be a list of LifecycleRule objects (see in the code : 'Rules': ([LifecycleRule], True),)
But when you create your S3.LifecycleConfiguration, you feed the 'Rules' property with a single LifecycleRule object instead.
You shoud write:
myLifecycleConfiguration = S3.LifecycleConfiguration(
title='myLifecycleConfiguration',Rules=[S3.LifecycleRule(title="check")])