I have model of category and need to serialize to give beautiful output.
My model
class Categorie(models.Model):
name = models.CharField(max_length=50)
My serializer
class CategorieSerializer(serializers.ModelSerializer):
class Meta:
model = Categorie
fields = ['name']
My code
class JokesCategories(APIView):
def get(self, request):
categories = Categorie.objects.all()
serializer = CategorieSerializer(categories, many=True)
output_data = {}
for num, dictionary in enumerate(serializer.data):
output_data[num] = dict(dictionary)['name']
return Response(output_data)
I have output
{
"0": "animal",
"1": "career"
}
But need
[
0: "animal",
1: "career"
]
Help for you advices.
You can't. A JSON object always has strings as keys, not integers. This is part of the JSON specifications [json.org]. You can for example use jsonlint to check if a certain text is valid JSON, and the latter is not.
Python's JSON encoder thus will fallback on converting the integers into strings to produce a valid JSON blob. Your dictionary indeed produced integers as keys, but the serializer thus converts it to strings.
Here it however might make more sense to use a list, and not an object, since the keys (indices) start at 0 and are incremental. Using a dictionary/JSON object thus does not make much sense:
class JokesCategories(APIView):
def get(self, request):
categories = Categorie.objects.all()
serializer = CategorieSerializer(categories, many=True)
output_data = [data['name'] for item in serializer.data]
return Response(output_data)
Related
I need to make a Rest API which will take 3 inputs: input_list (list of srings sentences), from_lang (string), to_lang (string) and return list of string after fetching values from databse table.
Example:
input - {input_list: ['how are you', 'see you later', 'where are you'], from_lang: 'english', to_lang: 'spanish' }
output - {['cómo estás', 'nos vemos más tarde', 'Dónde estás']}
A service will call this API with list of sentences in any supported language, and in return they will get list of same length with translated sentence if it exist in database or null value if it doesn't exist.
How should I proceed?
What I have done is, I have created a serializer to handle/validate incoming request in serializers.py:
def supported_lang(value):
if value not in SUPPORTED_LANGUAGES:
print(value)
print(SUPPORTED_LANGUAGES)
raise serializers.ValidationError('Language not supported')
class TranslateSerializer(serializers.Serializer):
input_list = serializers.ListField(
child=serializers.CharField(allow_blank=False),
allow_empty=False
)
from_language = serializers.CharField(validators=[supported_lang])
to_language = serializers.CharField(validators=[supported_lang])
And I have defined a simple model for storing translations in model.py:
class TranslationModel(models.Model):
english = models.CharField(blank=False, max_length=MAX_LENGTH, unique=True)
spanish = models.CharField(blank=True, max_length=MAX_LENGTH)
italian = models.CharField(blank=True, max_length=MAX_LENGTH)
is_active = models.BooleanField(default=True)
Then in my views.py I have handled post requests like below
class TranslateView(views.APIView):
def post(self, request):
serializer = TranslateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serialized_data = serializer.validated_data
result = self.get_translations(serialized_data) # here i am confused
return Response(result)
So far so good, now I am confused how to fetch the data from model, and how to return it (following best practices).
I have defined a function get_translations() in views.py :
def get_translations(self, request):
output_list = [] # for return response
for sentence in request['input_list']:
if request['from_language'] == 'english':
queryset = TranslationModel.objects.filter(english=sentence)
elif request['from_language'] == 'spanish':
queryset = TranslationModel.objects.filter(hindi=sentence)
elif request['from_language'] == 'italian':
queryset = TranslationModel.objects.filter(telugu=sentence)
try:
resp = queryset.values()[0][request['to_language']]
except:
resp = ""
if not resp:
# print('empty response')
output_list.append(None)
else:
output_list.append(resp)
return output_list
I mainly have three confusions:
Is it good practice to use serializers.Serializer for handling/validating incoming requests
Should I use serializers for model as well, if yes how
How to pass filter value dynamically
Any help is appreciated.
Is it good practice to use serializers.Serializer for handling/validating incoming requests
Of course, It is. That's essentially one of the core concepts of seriaizers (Parsing, Validation and serialization)
Should I use serializers for model as well, if yes how
There is no need for that. You only need a serializer if data is being sent or received from the user which is not the case here.
How to pass filter value dynamically
Use dictionary unpacking
k = {request['from_language']: sentence}
queryset = TranslationModel.objects.filter(**k)
While parsing my request data from front-end and converting into JSON format using a serializer. I am getting some unexpected errors.
while request parsing pattern using serializers given as mentioned below, it shows me the following error:(I found the below error using: contact_serializer.errors)
{'address': {u'non_field_errors': [u'Invalid data. Expected a dictionary, but got str.']}}
I do not think it will work like this. You have to remember here is that if you input the values like this, it will ultimately be stored in DB, and it is hard coded values. Even if you insist to do it like this, then use a list of dictionary like this:
request.data['phone_number'] = [{'number': '9999999999'}]
request.data['cont_email'] = [{'email':'tim#gmail.com'}]
And update the serializer like this:
class CrmContactSerializer(serializers.ModelSerializer):
phone_number = PhoneNumberSerializer(source = 'contact_number', many=True)
cont_email = ContactEmailSerializer(source = 'contact_email', many=True)
class Meta:
model = RestaurantContactAssociation
fields = ('id','phone_number','cont_email','contact')
def create(self, validated_data):
phone_number = validated_data.pop('contact_number')
cont_email = validated_data.pop('contact_email')
restaurant = super(CrmContactSerializer, self).create(validated_data)
phone_instance = PhoneNumber(**phone_number)
phone_instance.restaurant = restaurant
phone_instance.save()
email_instance = ContactEmail(**phone_number)
email_instance.restaurant = restaurant
email_instance.save()
return restaurant
Reason for many=True is that one restaurant can have multiple numbers or emails(as it has one to many relationship with respective models).
Now, if you think of proper way of implementing, you can make phone_number and cont_email read only fields, so that it will be used when only reading, not writing:
class CrmContactSerializer(serializers.ModelSerializer):
phone_number = PhoneNumberSerializer(source = 'contact_number', read_only=True)
cont_email = ContactEmailSerializer(source = 'contact_email', read_only=True)
class Meta:
model = RestaurantContactAssociation
fields = ('id','phone_number','cont_email','contact')
In that way, validation error can be handled for phone number and cont email.
I've got a many-to-many model like Request ← RequestItem → Item, and I want the response from the Request API endpoint to include a list of Item IDs. I've got a working serializer method like this:
def to_representation(self, instance: Request) -> typing.Dict[str, Any]:
representation: Dict = super().to_representation(instance)
representation["items"] = [
item_id for item_id
in instance.requestitems_set.values_list("item_id", flat=True)
]
return representation
As you can see, this is horrible. What would be an idiomatic way of getting the exact same output?
It can be added quite straightforward like any other field in the serializer
class RequestSerializer(serializers.ModelSerializer):
....
items = serializers.SlugRelatedField(
source='requestitems_set',
slug_field='item_id',
read_only=True,
many=True,
)
And then of course add it to the list of fields
There are few related fields implemented by DRF. But API is open for you to implement your own one. I think this is more readable and clean solution.
class ItemIdRelatedField(serializers.RelatedField):
def to_internal_value(self, data):
pass
# implement if you need it.
def to_representation(self, value):
return value.item_id
And use it as field in serializer like this.
items = ItemIdRelatedField(many=True, source='requestitems_set', queryset=RequestItem.objects.all())
from given info, you could reduce the use of for loop as
def to_representation(self, instance: Request) -> typing.Dict[str, Any]:
representation: Dict = super().to_representation(instance)
representation["items"] = list(instance.requestitems_set.values_list("item_id", flat=True))
return representation
We are using Django Rest framework to serialise and deserialise json response from a third party API. I need to translate the string field values to a numeric value in our system.
The JSON response is given below
{
"result": "win" # Other values include "loss"/"inconclusive"/"pending"
}
Django Model
class Experiment(Model):
RESULTS = (
(1, 'win'),
(2, 'loss'),
(3, 'inconclusive'),
)
inferred_result = models.IntegerField(choices=RESULTS, null=True, blank=True)
Django Serializer Class
class ResultsSeializer(serializers.ModelSerializer):
# Need to convert "result" from "string" to "int" and vice versa.
class Meta:
model = models.Experiment
I want to convert the inferred_result integer value to string equivalent and vice versa. I could use this approach: Django rest framework. Deserialize json fields to different fields on the model if this is a good idea to convert integers to string. How do I convert string to int? I am new to django rest api and django.
To display string instead of int you can use get_FOO_display model's attribute. To convert string to int during creation process you can override create() method like this:
class ResultsSeializer(serializers.ModelSerializer):
inferred_result = serializers.CharField(source='get_inferred_result_display')
class Meta:
model = models.Experiment
fields = ('inferred_result',)
def create(self, validated_data):
dispplayed = validated_data.pop('get_inferred_result_display')
back_dict = {k:v for v, k in models.Experiment.RESULTS}
res = back_dict[dispplayed]
validated_data.update({'inferred_result': res})
return super(ResultsSeializer, self).create(validated_data)
Same way you need to override update() if you need.
I need to sort the values for each field alphabetically when they get returned by the API. It seems marshmallow's pre_dump method is the way to pre-process the data before serializing, but I haven't been able to figure this out. I've read the docs multiple times and Googled but haven't found the answer.
class UserSettingsSchema(UserSchema):
class Meta:
fields = (
"id",
"injuries",
"channel_levels",
"movement_levels",
"equipments",
"goals",
"genres"
)
#pre_dump
def pre_dump_hook(): # what do I pass in here?
# alphabetize field values, eg, all the genres will be sorted
equipments = fields.Nested(EquipmentSchema, many=True)
goals = fields.Nested(GoalSchemaBrief, many=True)
genres = fields.Nested(GenreSchemaBrief, many=True)
The answer given by PoP does not address the fact that I needed the values sorted. This is how I did it:
#post_load
def post_load_hook(self, item):
item['genres'] = sorted(item['genres'])
return item
As mentioned in the docs:
By default, receives a single object at a time, regardless of whether many=True is passed to the Schema.
Here's an example:
from marshmallow import *
class MySchema(Schema):
name = fields.String()
#pre_dump
def pre_dump_hook(self, instance):
instance['name'] = 'Hello %s' % instance['name']
Now you can do:
schema = MySchema()
schema.dump({'name': 'Bill'})
>>> MarshalResult(data={'name': 'Hello Bill'}, errors={})