how to convert json in django models instances? - python

I am trying to save json data as django models instances, I am new to djano-rest-framework
here is my model:
class objective(models.Model):
description = models.CharField(max_length=200)
profile_name = models.CharField(max_length=100)
pid = models.ForeignKey('personal_info')
serializer.py
class objective_Serilaizer(serializers.Serializer):
description = serializers.CharField(max_length=200)
profile_name = serializers.CharField(max_length=100)
pid = serializers.IntegerField()
def restore_object(self, attrs, instance=None):
if instance:
instance.description = attrs.get('description', instance.description)
instance.profile_name = attrs.get('profile_name', instance.profile_name)
instance.pid = attrs.get('pid', instance.pid)
return instance
return objective(**attrs)
json
{
"objective": {
"description": "To obtain job focusing in information technology.",
"profile_name": "Default",
"id": 1
}
}
I tried
>>> stream = StringIO(json)
>>> data = JSONParser().parse(stream)
I am getting following error
raise ParseError('JSON parse error - %s' % six.text_type(exc))
ParseError: JSON parse error - No JSON object could be decoded

Use:
objective_Serilaizer(data=json)
or probably because your json is data on the request object:
objective_Serilaizer(data=request.DATA)
Here's a good walk through from the Django Rest-framework docs.

If you are sure your JSON as a string is correct then it should be easy to parse without going to the lengths you currently are:
>>> import json
>>> stream = """\
... {
... "objective": {
... "description": "To obtain job focusing in information technology.",
... "profile_name": "Default",
... "id": 1
... }
... }"""
>>> json.loads(stream)
{u'objective': {u'profile_name': u'Default', u'description': u'To obtain job focusing in information technology.', u'id': 1}}
So surely the question is how come you aren't able to parse it. Where is that JSON string you quote actually coming from? Once your JSON object is parsed, you need to address the top-level "objective" key to access the individual data elements in the record you want to create.

Related

Django serializer test post of file with user information

I try to test a file upload like this:
#deconstructible
class FileGenerator:
#staticmethod
def generate_text_file(file_ending='txt'):
file_content = b'some test string'
file = io.BytesIO(file_content)
file.name = f'test.{file_ending}'
file.seek(0)
return file
def test_this(self, api_client, login_as):
user = login_as('quality-controller')
url = reverse('test-list')
organization = Organization(name="test")
organization.save()
data = {
"organization": organization.id,
"import_file": FileGenerator.generate_text_file('txt'),
"user": {
"id": user.id,
"username": user.username,
}
}
response = api_client.post(url, data, format='json')
But I receive the following error message:
b'{"import_file": ["The submitted data was not a file. Check the
encoding type on the form."]}'
I also tried to use: format='multipart' but then I receive the following error:
AssertionError: Test data contained a dictionary value for key 'user',
but multipart uploads do not support nested data. You may want to
consider using format='json' in this test case.
How can I solve this?
This is how I deal with this issue:
Simplest: flatten the form
Suck it up and just remove the issue by making your serializer to use user_id and user_username and fix it up on the server side in the serializer's validate(self, attrs) method. A bit ugly/hacky but it works just fine and can be documented.
def validate(self, attrs):
attrs["user"] = {
"id": attrs.pop("user_id"),
"name": attrs.pop("user_username")
}
return attrs
Nicest if you dont mind the size: B64 Fields
You can base64 encode the file field and pass it in the json. Then to decode it on the server side you would write (or search for) a simple Base64FileField() for DRF.
class UploadedBase64ImageSerializer(serializers.Serializer):
file = Base64ImageField(required=False)
created = serializers.DateTimeField()
Alternative - Flatten the form data
You can't pass nested data, but you can flatten the nested dicts and pass that to a DRF service. Serializers actually can understand nested data if the field names are correct.
I don't know if this field name format is standardized, but this is what worked for me after experimentation. I only use it for service->service communication TO drf, so you would have to clone it into JS, but you can use the python in unit tests. Let me know if it works for you.
def flatten_dict_for_formdata(input_dict, array_separator="[{i}]"):
"""
Recursively flattens nested dict()s into a single level suitable
for passing to a library that makes multipart/form-data posts.
"""
def __flatten(value, prefix, result_dict, previous=None):
if isinstance(value, dict):
# If we just processed a dict, then separate with a "."
# Don't do this if it is an object inside an array.
# In that case the [:id] _is_ the separator, adding
# a "." like list[1].name will break but list[x]name
# is correct (at least for DRF/django decoding)
if previous == "dict":
prefix += "."
for key, v in value.items():
__flatten(
value=v,
prefix=prefix + key,
result_dict=result_dict,
previous="dict"
)
elif isinstance(value, list) or isinstance(value, tuple):
for i, v in enumerate(value):
__flatten(
value=v,
prefix=prefix + array_separator.format(i=i), # e.g. name[1]
result_dict=result_dict,
previous="array"
)
else:
result_dict[prefix] = value
# return her to simplify the caller's life. ignored during recursion
return result_dict
return __flatten(input_dict, '', OrderedDict(), None)
# flatten_dict_for_formdata({...}):
{ # output field name
"file": SimpleUploadFile(...), # file
"user": {
"id": 1, # user.id
"name": "foghorn", # user.name
"jobs": [
"driver", # user.jobs[0]
"captain", # user.jobs[1]
"pilot" # user.jobs[1]
]
},
"objects": [
{
"type": "shoe", # objects[0]type
"size": "44" # objects[0]size
},
]
}

Django save request.POST to JSONField picks last item from list instead of saving the list

I have a view that receives a post request from client.post()
data = {
"token": create_hash(customer_name),
"images": [image_1, image_2],
"name": customer_name,
"email": "test#email.com",
"phone": "0612345678",
"product": "product-sku0",
"font_family": "Helvetica",
"font_size": 12,
"colors_used": (
"#AAAAAA|White D",
"#FFFFFF|Black C"
)
}
I am trying to save the post request as a whole to a model.JSONfield().
The post request key-value pair looks like this:
'colors_used': ['#AAAAAA|White D', '#FFFFFF|Black C']
When I save and later retrieve the value it looks like this:
'colors_used': '#FFFFFF|Black C'
Instead of saving the nested list in the JSONfield it only saved the last value.
The view:
#csrf_exempt
def order(request):
"""
Receives and saves request
"""
post = request.POST
files = request.FILES
print(f"{post=}")
assert post["token"] == create_hash(post["name"])
design_obj = RequestDetails.objects.create(
customer_name = post["name"],
customer_email = post["email"],
customer_phone = post["phone"],
request_json = post
)
I am using SQLite.
Turns out this is just default behaviour when you convert the queryset to a json string. On a key-level you can you getlist() to get all values of a multivalue key.
I ended up placing the whole nested data structure in a single json string using json.dumps(data) and just send that along with the request.

Json to django models. Python data-type error

I have got when I want convert json data in django models.
How can I solve it.
class Persons(models.Model):
rank = models.IntegerField()
employer = models.CharField(max_length=100)
employeesCount = models.IntegerField()
medianSalary = models.IntegerField()
object creater:
for json in json_string:
Persons.objects.create(id=json['rank'], employer=json['employer'], employeesCount=json['employeesCount'], medianSalary=json['medianSalary'])
json reader
f = open('data.json')
json_string = f.read()
f.close()
json file:
[
{
"rank": 1,
"employer": "Walmart",
"employeesCount": 2300000,
"medianSalary": 19177
},
{
"rank": 2,
"employer": "Amazon",
"employeesCount": 566000,
"medianSalary": 38466
}
]
Your code is expecting a dictionary. Convert the json string using the builtin library
import json
your_json = json.loads(f.read())
Th json_strings is just a string, not a list of dictionaries. You can make use of the json module [python-doc] to JSON deserialize it:
from json import load as json_load
with open('data.json') as f:
json_data = json_load(f)
Persons.objects.bulk_create([
Person(
id=record['rank'],
employer=record['employer'],
employeesCount=record['employeesCount'],
medianSalary=record['medianSalary']
)
for record in json_data
])
By using bulk_create you create the objects in bulk in the database, which reduces the number of roundtrips to the database.

Deserializing JSON Date field in Python/Django

I am trying to port a web crawler app from .Net to Python. It receives json responses similar to the following:
[
{
"Code": "AAA",
"Date": "/Date(1481875200000)/",
"Value": 12345.00
}
]
This could easily be deserialized by Newtonsoft Json. However I can't seem to deserialize this with Python's built in Json Decoder
from django.db import models
class ItemModel(models.Model):
code = models.CharField(max_length=5)
date = models.DateTimeField()
value = models.IntegerField(default=0)
import json
parsed_data = json.loads(json_data, encoding='utf-8')
new_model=ItemModel()
new_model.code = parsed_data["Code"]
new_model.date = parsed_data["Date"]
new_model.value = parsed_data["Value"]
new_model.save()
which gives
ValidationError: [u"'/Date(1481875200000)/' value has an invalid
format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."]
Edit: Now I know this is caused because of assigning a string to a Date Time field
Is there a way to try parse this data to the django model? - as I have no way to modify the json response. Also is this the right way to do this? as the code seems intuitively iffy to me.
You need to implement a custom decoder for 'Date' field.
import json
from datetime import datetime
def parseMyData(dct):
if 'Date' in dct:
timestamp = int(dct['Date'][6:-2])
dct['Date'] = datetime.fromtimestamp(timestamp)
return dct
jdata = '''{
"Code": "AAA",
"Date": "/Date(14818752000)/",
"Value": 12345.00
}
'''
json.loads(jdata, object_hook=parseMyData)
and returns
{u'Code': u'AAA',
'Date': datetime.datetime(2439, 8, 3, 10, 0),
u'Value': 12345.0}

JSON to model a class using Django

I'm trying to get a JSON object like:
{
"username": "clelio",
"name": "Clelio de Paula",
}
and transform it in:
class User(models.Model):
name = models.CharField(max_length=30)
username = models.CharField(max_length=20)
def jsonToClass(s):
aux = json.dumps(s, self)
self.name = aux['name']
self.id = aux['id']
So I tried to use the simplejson and one method called jsonToClass():
>>> import simplejson as json
>>> u1 = User()
>>> u1.jsonToClass(face)
>>> u1.save()
This doesn't work. What is the easiest method to do what I want?
You probably want to look at Django's (de)serialization framework. Given JSON like:
[
{
"model": "myapp.user",
"pk": "89900",
"fields": {
"name": "Clelio de Paula"
}
}
]
you can save it like this:
from django.core import serializers
for deserialized_object in serializers.deserialize("json", data):
deserialized_object.save()
Note that I believe you have to use the Django serialization format to use this method, so you might have to adjust your JSON accordingly.
I just realized that
{
"username": "clelio",
"name": "Clelio de Paula",
}
is a dict() object.
So, this is easiest than I thought.
What I need to solve is just
def jsonToClass(self, aux):
self.name = aux['name']
self.username = aux['username']
that's it.
This is simply achieved as follows:
data = {
"username": "stackExchange",
"name": "stack overflow"
}
for obj in serializers.deserialize("json", data):
do_something_with(obj)
Check out django docs for details

Categories