How To Declare JSON Fields in a Django Model? - mysql

I'm seeking to create my first Django mySQL model that contains a JSON field.
I've read the docs here, but they don't seem to show an example of how to declare a model that contains multiple fields, including JSON fields. I've done a lot of searches on Google and SO, but haven't yet found examples.
What would be the correct way to declare a model that would store data like this?
candy_bar_name='Snickers',
info={
'target_markets': [
{
'market_type': 'type_a',
'included_markets': [array of strings]
},
{
'market_type': 'type_b',
'included_markets': [array of strings]
},
{
'market_type': 'type_c',
'included_markets': [array of strings]
},
],
}
UPDATE 2-13-2020
Responding to comment from #iklinac --
Thanks for this comment. I see this:
from django_mysql.models import JSONField, Model
class ShopItem(Model):
name = models.CharField(max_length=200)
attrs = JSONField()
def __str__(self):
return self.name
...but I'm confused by this:
def __str__(self):
return self.name
How can a model with multiple fields return a single string? I thought possibly this might be a class that defines a single JSON field in a model defined elsewhere. ;)
UPDATE 2-14-2020
This has now been solved via the help of the comments to this post. This works:
from django_mysql.models import JSONField, Model
class CandyBars(models.Model):
candy_bar_name = models.TextField()
info = JSONField()

Related

Django model with value as a special HTML character code

I am confused on how to implement a Django model with the value being a special html character code for making a chessboard.
As a reference I'm looking at a sudoku board model:
class Board(models.Model):
name = models.CharField(max_length=4, primary_key=True)
value = models.SmallIntegerField()
The value for sudoku is easy, since the table will only be filled with numbers.
For reference here's a snippet from the sudoku page_data dictionary in views.py giving each table cell its appropriate value:
{"r1c1": 6, "r1c2": 7, "r1c3": 0, ...}
I don't know what to put for my model's value variable:
class Board(models.Model):
name = models.CharField(max_length=2, primary_key=True)
value =
Here's a snippet of where I assign the name/value pairs in my views.py with the special HTML chess piece character codes in my own page_data dictionary:
{"a8": html.unescape('♖'), "b8": html.unescape('♘'), "c8": html.unescape('♗'), ...}
Any help is appreciated.
So you are looking for a way to store your json data in a django model field.
One option would be to convert your json data to a string and store it in a Charfield.
class Board(models.Model):
name = models.CharField(max_length=4, primary_key=True)
value = models.CharField(blank=True, null=True)
Then convert your json data to a string with the following code:
import json
data = {"r1c1": 6, "r1c2": 7, "r1c3": 0}
Board.objects.create(name="bo", value=json.dumps(data))
You can use the json data in the template by using the safe tag:
from django.utils.safestring import SafeString
def view(request):
return render(request, 'template.html', {'board': SafeString(my_board.value)})
If you do not want to convert your json to string, you can take a look at the JSONField.

DRF: can you deserialize one JSON key:value pair into multiple fields

In my API I have a module, which collects JSON objects obtained via POST request. JSON objects I'm receiving look more or less like this:
{
"id": "bec7426d-c5c3-4341-9df2-a0b2c54ab8da",
"data": {
"temperature": -2.4,
// some other stuff is here as well ...
}
}
The problem is requirement that I have to save both: records from data dictionary and whole data dictionary as a JSONField. My ORM model looks like this:
class Report(BaseModel):
id = models.BigAutoField(primary_key=True)
data = JSONField(verbose_name=_("Data"), encoder=DjangoJSONEncoder, blank=True, null=True)
temperature = models.DecimalField(
max_digits=3,
decimal_places=1,
)
# other properties
Is there any neat way to get both properties in one attempt to deserialize JSON object? Currently I use nested serializers formed like this:
class DataSerializer(serializers.ModelSerializer):
temperature = serializers.DecimalField(
source="temperature", write_only=True, max_digits=3, decimal_places=1
)
class Meta:
model = Report
fields = ("temperature")
class ReportSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(source="uuid", read_only=True)
data = DataSerializer(source="*")
class Meta:
model = Report
fields = ("id", "data")
which obviously does not pass whole data dictionary to validated_data, which means I have to update the JSON field elsewhere (not good since I would like to have both data and temperature at null=False in ORM model). Any good ideas on how to fix this using DRF serializers would be appreciated.
I believe you should be able to override validate method for your serializer where you can "store initial data JSON field" and do the default validation by calling super()... method.
More info https://www.django-rest-framework.org/api-guide/serializers/#validation
Also, there are object-level validation functions available, you can take a look there as well for the initial posted data
https://www.django-rest-framework.org/api-guide/serializers/#object-level-validation
Also, you can override the method run_validation to access the initially passed data object.

Django View: Return Queryset in JSON Format

i am trying to make the following view with return JsonResponse() at the end work correctly:
def get_data(request):
full_data = Fund.objects.all()
data = {
"test2": full_data.values('investment_strategy').annotate(sum=Sum('commitment')),
}
return JsonResponse(data)
However, I get an error message saying "Object of type QuerySet is not JSON serializable".
When I put the above Queryset in a view with return render() at the end:
def get_more_data(request):
full_data = Fund.objects.all()
data = {"test2": full_data.values('investment_strategy').annotate(sum=Sum('commitment'))}
return render (request, 'test.html', data)
I get the the following result: <QuerySet [{'investment_strategy': 'Buyout', 'sum': 29}, {'investment_strategy': 'Growth', 'sum': 13}, {'investment_strategy': 'Miscellaneous', 'sum': 14}, {'investment_strategy': 'Venture Capital', 'sum': 23}, {'investment_strategy': 'n/a', 'sum': 36}]>
So the queryset works fine, I just have no clue how to return the data in proper Json format (which I would need to use the data charts.js)
I looked through answers for similar questions such as:
TypeError: object is not JSON serializable in DJango 1.8 Python 3.4
Output Django queryset as JSON
etc.
but could not find a meaningful solution for my problem.
Any help would be much appreciated!
So I managed to find a solution, that worked for me - in case anyone else has the same problem. I changed my view to the following:
def get_data(request):
full_data = Fund.objects.all()
full_data_filtered = full_data.values('investment_strategy').annotate(sum=Sum('commitment'))
labels = []
values = []
for d in full_data_filtered:
labels.append(d['investment_strategy'])
values.append(d['sum'])
data = {
"labels": labels,
"values": values,
}
return JsonResponse(data)
So basically I iterate over the Queryset and assign the values I need to lists, which can be passed to JsonResponse. I don't know if this is the most elegant way to do this (sure not), but it works and I can render my data in charts.js
JsonResponse(list(data)) will evaluate the Queryset (actually perform the query to the database) and turn it into a list that can be passed to JsonResponse.
This works because you use values and annotate, so the list is a list of dictionaries containing serializable fields.
In the example you mentioned, it didn't work because the Queryset was just returning a list of model instances, so wrapping in list() isn't enough. If you hadn't added values, you'd have had a list of Fund instances which are not serializable.
The best way I found was to create a custom QuerySet and Manager, it's not a lot of code and it is reusable!
I began by creating the custom QuerySet:
# managers.py but you can do that in the models.py too
from django.db import models
class DictQuerySet(models.QuerySet):
def dict(self):
values = self.values()
result = {}
for value in values:
id = value['id']
result[id] = value # you can customize the content of your dictionary here
return result
Then I created a custom Manager, it is optional but i prefer this way.
# managers.py, also optional
class DictManager(models.Manager):
def get_queryset(self):
return DictQuerySet(self.model, using=self._db)
Change the default manager in your model:
# models.py
from .managers import DictManager
class Fund(models.Model):
# ...
objects = DictManager()
# ...
And now you can call the dict() method from the query
# views.py
def get_data(request):
full_data = Fund.objects.all().dict()
return JsonResponse(full_data)
The response will be the full_data as a dictionary of dictionaries and each key is the primary key of the corresponding object.
If you intend to keep the same format for your JSONs, then you can use the same custom manager for all your models.

Return proper JSON from Django JsonResponse

My Model:
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
phone = models.CharField(max_length=20)
email = models.EmailField()
My View:
def users(request):
people = Person.objects.all()
data = serializers.serialize('json', people)
return JsonResponse(data, safe=False)
All I want back is the data in JSON format. What I'm getting back is this:
"[{\"model\": \"myapp.person\", \"pk\": 1, \"fields\": {\"first_name\": \"ahmet\", \"last_name\": \"arsan\", \"phone\": \"xxx-xxx-xxxx\", \"email\": \"aarsan#xxxxxxxx.com\"}}]"
While technically that is valid JSON, there are 2 problems (for me) with this response:
I don't want those double quotes escaped.
I don't need the model name (myapp.person).
I don't know if/what I'm doing wrong, but it seems like something is off here. Perhaps my query should be returning a dict but I don't know how to get it to do that. I am using Django 1.10.1, Python 3.4.
You already have an answer, but what you're doing wrong is double encoding. JsonResponse serialises to json, but you already have json as that's what's returned from the serialiser.
Either serialise to "python" or use a standard HttpResponse.
I am assuming you are asking this question for an API response. I would suggest using Rest Framework for that as it makes things very easy. You can select your own fields by writing your own serializers for the model.
from rest_framework import serializers
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = ('first_name', 'last_name', 'phone', 'email')

How to JSON-serialize a dictionary that contains a Django PointField? [duplicate]

This question already has an answer here:
Rendering spatial data of GeoQuerySet in a custom view on GeoDjango
(1 answer)
Closed 2 years ago.
I am in the process of building a website on geodjango. On the front end I have a map on openlayers and I want to be able to fire ajax queries at some django url and get back geojson.
The problem is that the standard django json serializer doesn't do geojson. So I have a model like:
class Asset (models.Model):
objects = models.GeoManager()
url_name = models.CharField(max_length=200)
name = models.CharField(max_length=200)
point = models.PointField(srid=4326)
def __unicode__(self):
return self.name
And I want to create a view:
def geojson_query(request):
#geographic query
...
assets = Asset.objects.all().filter(point__contained=bb_4326)
json = serializers.serialize("json", assets)
return HttpResponse(json)
But then I get back a response that looks more like this:
"url_name": "Upper_Svaneti",
"name": "Upper Svaneti",
"point": "POINT (43.0113899999999987 42.9163899999999998)"
Whereas I want something that looks like this:
"url_name": "Upper_Svaneti",
"name": "Upper Svaneti",
"geometry": {
"type": "Point",
"coordinates": [43.0113899999999987 42.9163899999999998]
}
So I guess my question is, what is the best way to implement a geojson serializer in django without totally rolling my own? And if it comes to rolling my own, where do I start?
You need to write your own serializer. Just inherit from the DjangoJSONEncoder, here's one I created that supports the Point type:
from django.core.serializers.json import DjangoJSONEncoder
from django.contrib.gis.geos import Point
class GeoJSONEncoder(DjangoJSONEncoder):
def default(self, obj):
if isinstance(obj, Point):
return obj.coords
return super(GeoJSONEncoder, self).default(obj)
You can then use it like so:
from my_app.serializers import GeoJSONEncoder
from django.utils import simplejson
json = simplejson.dumps(data, cls=GeoJSONEncoder)
So... I have done something slightly unpretty. I hardcoded the non-geojson parts of the serializer and used the json function from GEOS to get the geojson part.
So the method in the model looks like:
def get_footprint_json(self):
geojson=self.footprint.json
json='{"type": "Feature","geometry": %s,"properties": {"name":"%s","url_name":"%s"}}'%(geojson,self.name,self.url_name)
return json
And... I have a view that looks like this:
json='{ "srid":4326, "type": "FeatureCollection","features": ['+','.join([asset.get_footprint_json() for asset in assets])+'] }'
return HttpResponse(json)
I'd be curious to see if anyone else has a better way or if django has updated their serializer to include geojson.