Django Model load choices from a json file - python

I am writing a Django User model which contains a mobile_country_code field. This field needs to be populated from a list of ISD codes, pre-populated in a json file. What is the best pythonic way to do the same?
My current implementation, which is working:
json_data/countries.json
[
...
{
"name": "Malaysia",
"dial_code": "+60",
"code": "MY"
},
...
]
project/app/models.py
import json, os
class User(models.Model):
with open(os.path.dirname(__file__)+'/json_data/countries.json') as f:
countries_json = json.load(f)
COUNTRIES_ISD_CODES = [(str(country["dial_code"]), str(country["name"])) for country in countries_json]
mobile_country_code = models.CharField(choices=COUNTRIES_ISD_CODES, help_text="Country ISD code loaded from JSON file")
Other Possible options listed below. Which one is better to use?
Using a model's __init__ method to create COUNTRIES_ISD_CODES
Importing a library method, like:
from library import import_countries_isd_codes
class User(models.Model):
mobile_country_code = models.CharField(choices=import_countries_isd_codes())

Try this,
Since Django only takes tuples
def jsonDjangoTupple(jsonData):
"""
Django only takes tuples (actual value, human readable name) so we need to repack the json in a dictionay of tuples
"""
dicOfTupple = dict()
for key, valueList in jsonData.items():
dicOfTupple[str(key)]=[(value,value) for value in valueList]
return dicOfTupple
json_data = jsonDjangoTupple(jsonData)
in the model pass something like
class User(models.Model):
code = models.CharField(max_length=120,choices=json_data['code'])
Works for when there's more than one value per key.

Related

Python flask Graphene: Mapping fields with API response

I'm building a graphql api using python flask and python graphene.
basically my json file data looks like following.
{
"address":{
"streetAddress":"301",
"#city":"Los Angeles",
"state":"CA"
}
}
And my graphene schema looks like follow.
class Address(ObjectType):
streetAddress = String()
city = String()
state = String()
class Meta:
exclude_fields = ('#city',)
class Common(ObjectType):
data = Field(Address)
def resolve_data(self, info):
data = open("address.json", "r")
data_mod = json.loads(data.read())["address"]
return data_mod
So I am trying to map this #city json key value to my schema field called city.
I saw one of the articles and in that, it mentioned that using the meta class we can exclude original field name like this.
class Meta:
exclude_fields = ('#city',)
Still it didn't work. And I am using a common schema to fetch the json data to Address schema fields by using one resolver. Can someone tell me a solution to map these kind of fields to graphene schema fields.

Store Subtitles in a Database

I'm working on a project that uses AI to recognise the speech of an audio file. The output of this AI is a huge JSON object with tons of values. I'll remove some keys, and the final structure will look as follows.
{
text: "<recognised text>",
language: "<detected language>"
segments: [
{startTimestamp: "00:00:00", endTimestamp: "00:00:10", text: "<some text>"},
{startTimestamp: "00:00:10", endTimestamp: "00:00:17", text: "<some text>"},
{startTimestamp: "00:00:17", endTimestamp: "00:00:26", text: "<some text>"},
{ ... },
{ ... }
]
}
Now, I wish to store this new trimmed object in a SQL database because I wish to be able to edit it manually. I'll create a React application to edit segments, delete segments, etc. Additionally, I want to add this feature to the React application, where the information will be saved every 5 seconds using an AJAX call.
Now, I don't understand how I should store this object in the SQL database. Initially, I thought I would store the whole object as a string in a database. Whenever some change is made to the object, I'll send a JSON object from the React application, the backend will sanitize it and then replace the old stringified object in the database with the new sanitised string object. This way updating and deletion will happen with ease but there can be issues in case of searching. But I'm wondering if there are any better approaches to do this.
Could someone guide me on this?
Tech Stack
Frontend - React
Backend - Django 3.2.15
Database - PostgreSQL
Thank you
Now, I don't understand how I should store this object in the SQL database. Initially, I thought I would store the whole object as a string in a database.
If the data has a clear structure, you should not store it as a JSON blob in a relational database. While relational databases have some support for JSON nowadays, it is still not very effective, and normally it means you can not effectively filter, aggregate, and manipulate data, nor can you check referential integrity.
You can work with two models that look like:
from django.db import models
from django.db.models import F, Q
class Subtitle(models.Model):
text = models.CharField(max_length=128)
language = models.CharField(max_length=128)
class Segment(models.Model):
startTimestamp = models.DurationField()
endTimestamp = models.DurationField()
subtitle = models.ForeignKey(
Subtitle, on_delete=models.CASCADE, related_name='segments'
)
text = models.CharField(max_length=512)
class Meta:
ordering = ('subtitle', 'startTimestamp', 'endTimestamp')
constraints = [
models.CheckConstraint(
check=Q(startTimestamp__gt=F('endTimestamp')),
name='start_before_end',
)
]
This will also guarantee that the startTimestamp is before the endTimestamp for example, that these fields store durations (and not "foo" for example).
You can convert from and to JSON with serializers [drf-doc]:
from rest_framework import serializers
class SegmentSerializer(serializers.ModelSerializer):
class Meta:
model = Segment
fields = ['startTimestamp', 'endTimestamp', 'text']
class SubtitleSerializer(serializers.ModelSerializer):
segments = SegmentSerializer(many=True)
class Meta:
model = Subtitle
fields = ['text', 'language', 'segments']

django email list field

I want to create field that hods list of emails. My model is like this :
class Alerts(models.Model):
emails = MultiEmailField()
events = models.OneToOneField(Event, on_delete=models.CASCADE)
All good, but when this models is saved in DB it is like this
{
"id": 11,
"emails": "['ht#ht.com, bb#bb.com']",
"events": 13
}
The list in 'emails' key is represented as a string "['ht#ht.com, bb#bb.com']" , not as a list ['ht#ht.com, bb#bb.com']. I tried different method, parsers and approaches, but without success. Is there a way to save list in DB or not? If it is the second is there a way to receive back a list as a response, not a string, that is fine too. ( DB is MYSQL)
You can use ListCharField from django-mysql lib.
from django.db import models
from django_mysql.models import ListCharField
class Alerts(models.Model):
emails = ListCharField(
base_field=models.EmailField(...),
...
)
events = models.OneToOneField(Event, on_delete=models.CASCADE)
You can just split the string to get the emails in a list like this:
email_list = emails.replace("['", "").replace("']", "").split(", ")
Django 3.1 added models.JsonField which can be used for all DB backends. You can use this to store a list
class Alerts(models.Model):
emails = models.JsonField()
Building on the answer of Dacx. And from https://stackoverflow.com/a/1894296/5731101
How about:
import ast
class Alerts(models.Model):
raw_emails = MultiEmailField()
events = models.OneToOneField(Event, on_delete=models.CASCADE)
#property
def emails(self):
return ast.literal_eval(self.raw_emails)

Nested validation in Django Rest Framework

Using django rest framework I want to validate fields.
Correct input request:
{
test_field_a: {test_field_c: 25},
test_field_b: {}
}
My serializers.py (I don't have any associated models and the models.py itself):
from rest_framework import serializers
class TestSerializer(serializers.Serializer):
test_field_a = serializers.JSONField(label='test_field_a', allow_null=False, required=True)
test_field_b = serializers.JSONField(label='test_field_b', required=True)
test_field_c = serializers.IntegerField(label='test_field_c)
Wrong input request (which should state that int field is required) :
{
test_field_a: {test_field_c: 'wrong'},
test_field_b: {}
}
Now test_field_a and test_field_b are validated as required. But how to make validation of fields on different levels of the request? (in this case test_field_c)
JSONField just checks that a field contains correct JSON structure. You need to do it plus check values from this JSON.
There are several ways to do it:
You can write your own custom field type (it's nice if you are planning to do something similar in other serializers);
You can change field validation (try something like this):
from rest_framework import serializers
class TestSerializer(serializers.Serializer)::
test_field_a = serializers.JSONField(label='test_field_a', allow_null=False, required=True)
test_field_b = serializers.JSONField(label='test_field_b', required=True)
def validate_test_field_a(self, value):
"""
Check that test_field_a .
"""
if not isinstance(value.get('test_field_c'), int):
raise serializers.ValidationError("Some error message")
return value
You can try nested validation:
from rest_framework import serializers
class Test1Serializer(serializers.Serializer):
test_field_c = serializers.IntegerField(label='test_field_c')
class TestSerializer(serializers.Serializer):
test_field_a = Test1Serializer()
test_field_b = serializers.JSONField(label='test_field_b', required=True)
The serializer's JSONField does not have a validation for nested fields because it is not meant to nest explicitly declared fields and as far as I know, there is currently no way to specify a json schema to validate it.
What you can do is validate the field yourself by declaring a validate_test_field_a validation method.
For example:
def validate_test_field_a(self, value):
if 'test_field_c' not in value:
raise serializers.ValidationError('`test_field_c` is required')
return value
Generally, if you find yourself needing to validate the nested type inside the JSONField, then it is a sign of bad architecture and you should consider using nested serializers instead. Same applies to using JSONField in the model

Serializing JSON object effectively

This is a bit of a puzzle, I have these pseudo models:
class Country(models.Model):
name = models.CharField(unique=True)
class Region(models.Model):
name = models.CharField(unique=True)
country = models.ForeignKey(Country)
class SubRegion(models.Model):
name = models.CharField(unique=True)
region = models.ForeignKey(Region)
class Estate(models.Model):
name = models.CharField(unique=True)
sub_region = models.ForeignKey(SubRegion)
I am trying to JSON serialize their data as below. However i'm not sure how to do this effectively (avoiding too much database queries), suggestions are appreciated
{
CountryX: {
RegionX {
SubRegionX = [
"EstateX"
"EstateY",
"EstateZ"
],
SubRegionY = [ etc... ]
},
RegionY { etc... }
},
CountryY: { etc... }
}
I haven't tested this, but it should give you the idea. Start with the innermost object, use select_related to traverse the heirarchy, then loop over the innermost objects, adding the keys for the heirarchy as needed.
Just a note of warning, if there are Countries/Regions/Subregions without any estates, they won't be included in the JSON. If that's not OK, you'll need to query each of the models separately.
data = {}
for e in Estate.objects.select_related("sub_region__region__country"):
sub, region, country = e.sub_region, e.sub_region.region, e.sub_region.region.country
if country.name not in data:
data[country.name] = {}
if region.name not in data[country.name]:
data[country.name][region.name] = {}
if sub.name not in data[country.name][region.name]:
data[country.name][region.name][sub.name] = []
data[country.name][region.name][sub.name].append(e.name)
json_data = json.dumps(data)
This suggestion might not be exactly what you were looking for, but I've used it in a couple of situations where I needed quick-and-dirty JSON of an app's data.
Check out./manage.py dumpdata app_name (or app_name.model_name). This gives you JSON for all of the data in all of the tables of that app (or that model). The format may be a little different than you were thinking about, but it does include all of the PK and class info necessary to maintain ForeignKey relationships, and it spits them out in the order necessary to create the referenced object before you create the referencing object. Very handy.
If you want to invoke it from inside a script, look at django/core/management/commands/dumpdata.py to see how they do it.

Categories

Resources