I am trying to have my "address" API return the default value (empty string ""), but when I use default="" in the serializer field it returns null instead.
This is my main serializer (LocationSerializer)
class LocationSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(read_only=True)
business_id = serializers.PKRelatedField(Business.any.all(), "business")
city_id = serializers.UUIDField(required=True)
name = serializers.CharField(max_length=48, required=True)
tax_number = serializers.CharField(max_length=48)
phone = serializers.PhoneNumberField()
address = serializers.CharField()
address_details = AddressEmbeddedSerializer(default="", source="addresses")
footer = serializers.CharField()
is_active = serializers.BooleanField(required=True)
has_kds = serializers.BooleanField(read_only=True)
position = serializers.PointField()
image = serializers.ImageField()
# profile = LocationProfileSerializer()
permissions = PermissionSerializer(many=True, read_only=True)
payment_methods = PaymentMethodEmbeddedSerializer(many=True, read_only=True)
class Meta:
model = Location
fields = (
"id", "business_id", "city_id", "name", "tax_number", "phone", "address", "address_details", "footer",
"is_active", "has_kds", "position", "image", "permissions", "payment_methods"
)
and this is my nested serializer (AddressEmbeddedSerializer)
class AddressEmbeddedSerializer(serializers.ModelSerializer):
city = serializers.CharField(default="")
area = serializers.CharField(default="")
block = serializers.CharField(default="")
avenue = serializers.CharField(default="")
long = serializers.CharField(default="")
lat = serializers.CharField(default="")
class Meta:
model = LocationAddress
fields = (
"city", "area", "block", "avenue", "long", "lat"
)
The value that I am expecting is:
"address_details": {
"city": "",
"area": "",
"block": "",
"avenue": "",
"long": "",
"lat": ""
}
Instead what I am getting is:
"address_details": null
Please note that all CRUD operations are working, this is only a default value issue
The reason why "address_details": null is that the current object has no related addresses at a model object so the nested serializer won't be accessed and therefore your desired JSON output won't be generated.
Also, default is used when no value provided for the field with POST request not GET
I suggest that you handle address_details when it is null, but if you really need to do this you can do something at to_representation() or to_internal_value(), see this answer: https://stackoverflow.com/a/38606711/4984493
Also, you can use SerializerMethodField, so based on obj.addresses's value if it was null or not you return the desired JSON output you need.
Related
I`m trying to post this json object
{
"name": "Country Name",
"description": "Description here...",
"generalInfo": {
"capital": "Capital",
"population": "Population",
"highestPeak": "HighestPeak",
"area": "Area"
},
"timelineData": [
{
"name": "Name 1",
"ruler": "Ruler",
"dateStart": "dateStart",
"dateEnd": "dateEnd",
"description": "description"
},
{
"name": "Name 2",
"ruler": "Ruler",
"dateStart": "dateStart",
"dateEnd": "dateEnd",
"description": "description"
},
{
"name": "Name 3",
"ruler": "Ruler",
"dateStart": "dateStart",
"dateEnd": "dateEnd",
"description": "description"
}
]
}
But I`m receiving this error
ValueError at /countries/
Cannot assign "OrderedDict([('capital', 'Capital'), ('population', 'Population'), ('highestPeak', 'HighestPeak'), ('area', 'Area')])": "Country.generalInfo" must be a "GeneralInfo" instance.
Request Method: POST
Request URL: http://127.0.0.1:8000/countries/
Django Version: 4.0.3
Here are my models.py:
class GeneralInfo(models.Model):
capital = models.CharField(max_length=200)
population = models.CharField(max_length=200)
highestPeak = models.CharField(max_length=200)
area = models.CharField(max_length=200)
class TimelineData(models.Model):
name = models.CharField(max_length=200)
ruler = models.CharField(max_length=200)
dateStart = models.CharField(max_length=200)
dateEnd = models.CharField(max_length=200)
description = models.TextField(default='Description here...')
class Country(models.Model):
name = models.CharField(max_length=200, db_index=True)
description = models.TextField(default='Description here...')
generalInfo = models.ForeignKey(GeneralInfo, on_delete=models.CASCADE)
timelineData = models.ManyToManyField(TimelineData)
I have also overridden the create method for CountrySerializer and here are
my serializers.py:
class TimelineDataSerializer(serializers.ModelSerializer):
class Meta:
model = TimelineData
fields= '__all__'
class GeneralInfoSerializer(serializers.ModelSerializer):
class Meta:
model= GeneralInfo
fields = '__all__'
class CountrySerializer(serializers.ModelSerializer):
timelineData = TimelineDataSerializer(many=True)
generalInfo = GeneralInfoSerializer()
class Meta:
model= Country
fields = '__all__'
def create(self, validated_data):
timelineData = validated_data.pop('timelineData')
country = Country.objects.create(**validated_data)
for timelineItem in timelineData:
TimelineData.objects.create(country=country,**timelineItem)
return country
and views.py
class CountryList(viewsets.ModelViewSet):
serializer_class = CountrySerializer
queryset = Country.objects.all()
I am new to Django and Python so maybe I missed something. I've been stuck on this for a while now and wasn't able to find any solution myself
The error is because that OrderedDict needs to be an actual GeneralInfo Object..
I have not used serializers much (or at all tbh), but this would be my general guess from what I've seen from similar problems:
class CountrySerializer(serializers.ModelSerializer):
timelineData = TimelineDataSerializer(many=True)
generalInfo = GeneralInfoSerializer()
class Meta:
model= Country
fields = '__all__'
def create(self, validated_data):
timelineData = validated_data.pop('timelineData')
# pop out data (so it's not in final create), filter for object
generalInfoObj = GeneralInfo.objects.filter(**validated_data.pop('generalInfo')).first()
# ^ get or None (no crash, my preference)
# could also be:
# generalInfoObj = GeneralInfo.objects.get( ... )
# ^ - get or crash
# generalInfoObj, created = GeneralInfo.objects.get_or_create( ... )
# ^ get or create it
# debugger
print('generalInfoObj', type(generalInfoObj), generalInfoObj)
country = Country.objects.create(**validated_data, GeneralInfo=generalInfoObj)
for timelineItem in timelineData:
TimelineData.objects.create(country=country,**timelineItem)
return country
If generalInfo shows up like an array of tuples, you'd do something like this with List Comprehension
(adding this just in case)
generalInfoObj = GeneralInfo.objects.filter(**{i[0]:i[1] for i in validated_data.pop('generalInfo')}).first()
## breakdown:
# generalinfo = [('key0', val0'), ('key1', val1')]
dict = {}
for i in generalinfo:
# i = ('key', 'value')
dict[i[0]] = i[1]
# dict = {
# 'key0': 'val0',
# 'key1': 'val1',
# }
How do i get list of Many 2 Many field objects? I am just getting the ID.
This one is MainHost class which is having Many2Many field
class MainHost(models.Model):
host_id = models.IntegerField(verbose_name='HOST ID', primary_key=True)
group = models.ManyToManyField(MainGroup, related_name='hostgroups', through ='HostGroup')
This class is created for through keyword, contains all the relationship for Host and Group
class HostGroup(models.Model):
host = models.ForeignKey(MainHost, on_delete=models.CASCADE)
group = models.ForeignKey(MainGroup, on_delete=models.CASCADE)
This class contains all the Groups data
class MainGroup(models.Model):
group_id = models.IntegerField(verbose_name='GROUP ID', primary_key=True)
Serializer class
class MainGroupSerializer(serializers.ModelSerializer):
class Meta:
model = MainGroup
fields = (
'group_id',
'group_name',
....
'groupinv_path'
)
HostSerialzer class
class MainHostSerializer(serializers.ModelSerializer):
group = serializers.PrimaryKeyRelatedField(queryset=HostGroup.objects.all(), many=True)
class Meta:
model = MainHost
fields = (
'host_id',
'host_name',
'group'
)
class HostGroupSerializer(serializers.ModelSerializer):
host = MainHostSerializer()
group = MainGroupSerializer()
class Meta:
model = HostGroup
fields = (
'id',
'host',
'group'
)
read_only_fields = ['host', 'group']
def create(self, validated_data):
host_data = MainHost.objects.create(**validated_data.get('host'))
group_data = MainGroup.objects.create(**validated_data.get('group'))
conn = HostGroup.objects.create(
host=host_data,
group=group_data
)
return conn
MainHost API response
{
"host_id": 4087,
"host_name": "10.240.144.2",
"group": [
280,
285,
300
]
}
280, 285 and 300 are group objects from MainGroup I want to see the whole object content.
MainGroup Response is showing the complete object.
{
"group_id": 2,
"group_name": "test_environment_production",
"total_hosts": 1,
"total_groups": 0,
"description": "imported",
"group_variables": "{}",
"groupinv_path": ""
}
Even Response for GroupHost API is having complete Group Object.
{
"id": 1,
"host": {
"host_id": 4087,
"host_name": "10.100.144.2",
"group": [
280
]
},
"group": {
"group_id": 280,
"group_name": "aruba",
"total_hosts": 539,
.....
"groupinv_path": "TEC/GT/Swi/test.ini"
}
}
Can specify the nested representation by using the depth option as shown in the DRF docs here:
https://www.django-rest-framework.org/api-guide/serializers/#specifying-nested-serialization
Removing this line:
group = serializers.PrimaryKeyRelatedField(queryset=HostGroup.objects.all(), many=True)
from MainHostSerializer(serializers.ModelSerializer):
and adding depth = 2 it worked fine
Hello I have a serialiser and I'd like to have one field type for the GET requests and anther field for the POST requests.
These are my serialisers:
class TypeSerializer(serializers.Serializer):
id = serializers.CharField()
name = serializers.CharField(max_length=50)
colour = serializers.CharField(max_length=8)
class UserSerializer(serializers.Serializer):
id = UUIDField(format="hex_verbose")
name = serializers.CharField()
type = TypeSerializer()
So the response is something like this:
{
"id": "987328lf-93ad-21ba-2648-h2u7b95d5cf",
"name": "name",
"type": {
"id": "RANDOM_ID",
"name": "Type 1",
"colour": "#ffffff"
}
}
That's what I want on the GET, but on the POST I'd like to send a payload like this:
{
"name": "New name",
"type": "RANDOM_ID"
}
I will get a 500 error because type is expected to be a dictionary.
Anyone knows if its posible to have one field for GET and another field for POST without creating another serialiser?
You need to override to_internal_value method:
class UserSerializer(serializers.Serializer):
id = UUIDField(format="hex_verbose")
name = serializers.CharField()
type = TypeSerializer()
def to_internal_value(self, data):
type = data.get('type')
if type:
data['type'] = {'id': type}
ret = super(UserSerializer, self).to_internal_value(data)
# Probably here you will need to get type instance.
return ret
I think that you want to use this serializer for creating user so it needs so more iprovements:
# Inherit from ModelSerializer !
class UserSerializer(serializers.ModelSerializer):
id = UUIDField(format="hex_verbose")
name = serializers.CharField()
type = TypeSerializer()
class Meta:
model = User # Set model
fields = ('id', 'name', 'type') # Set fields
def to_internal_value(self, data):
type = data.get('type')
if type:
data['type'] = {'id': type}
ret = super(UserSerializer, self).to_internal_value(data)
# Here i change dict to instance
type = ret.get('type')
if type:
try:
ret['type'] = Type.objects.get(id=type['id'])
except Type.DoesNotExist:
raise ValidationError({'type': ['Type with the given ID does not exist.']}
return ret
One way of going about this is using 2 fields - a read field and a write field:
class TypeSerializer(serializers.Serializer):
id = serializers.CharField()
name = serializers.CharField(max_length=50)
colour = serializers.CharField(max_length=8)
class UserSerializer(serializers.Serializer):
id = UUIDField(format="hex_verbose")
name = serializers.CharField()
type_read = TypeSerializer(read_only=True)
# you could use a prrimary key related field
# instead if it is a model serializer;
type = serializers.CharField(write_only=True)
def to_representation(self, instance):
rep = super().to_representation(instance)
# rename type_read to type
rep['type'] = rep['type_read']
del rep['type_read']
return rep
I would like to return a given row and the row before and after it (sorted by file_created_time desc) when calling GET with a uid parameter representing the row.
URL ex: https://<domain>/api/videos/cbf02e8c-b2f5-4cd8-b3ec-87417eae2f7d?with_adjacent=true
{
"uid": "fd5d5936-8183-495f-9a9d-8ffca25a9bab",
"is_thumbnail": true,
"file_name": "2018-02-03_05-00-40.jpg",
"file_path": "thumbnails/2018-02-03_05-00-40.jpg",
"file_created_time": "2018-02-03T05:00:40-07:00",
"owner": "system_user",
"created_time": "2018-02-04T14:49:29.355156-07:00"
},
{
"uid": "cbf02e8c-b2f5-4cd8-b3ec-87417eae2f7d",
"is_thumbnail": true,
"file_name": "2018-02-03_01-09-30.jpg",
"file_path": "thumbnails/2018-02-03_01-09-30.jpg",
"file_created_time": "2018-02-03T01:09:30-07:00",
"owner": "system_user",
"created_time": "2018-02-04T14:49:30.464810-07:00"
},
{
"uid": "ed626576-cc9d-4434-9f44-93a4f8f525ad",
"is_thumbnail": true,
"file_name": "2018-02-03_00-59-15.jpg",
"file_path": "thumbnails/2018-02-03_00-59-15.jpg",
"file_created_time": "2018-02-03T00:59:15-07:00",
"owner": "system_user",
"created_time": "2018-02-04T14:49:32.611105-07:00"
}
Given the model:
class Videos(models.Model):
"""This class represents the Videos model."""
uid = models.UUIDField(
primary_key=True, default=uuid.uuid4, editable=False)
is_thumbnail = models.BooleanField(default=False)
file_name = models.CharField(unique=True, max_length=64)
file_path = models.CharField(unique=True, max_length=256)
file_created_time = models.DateTimeField()
owner = models.ForeignKey('auth.User',
related_name='videos',
on_delete=models.CASCADE)
created_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a human readable representation of the model instance."""
return "{}".format(self.file_name)
And View:
class DetailsView(generics.RetrieveUpdateDestroyAPIView):
"""This class handles the http GET, PUT and DELETE requests."""
queryset = Videos.objects.all()
serializer_class = VideosSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner)
lookup_field = 'uid'
I'm happy the adjust the view as necessary, that's just what I have now.
Edit:
To further complicate this, this model contains rows where is_thumbnail is either true or false. Thus, assuming I change my pk to an id, if I want to select an is_thumbnail = True row sorted by file_created_time, there's no guarantee that its adjacent rows are pk +/- 1.
Apart from my other answer, I found a better way (think so) to achieve the result.I'm only adding the key part of the snippet
....
vid_1 = Videos.objects.get(uid=input_uid)
vid_2 = vid_1.get_next_by_file_created_time()
vid_3 = vid_1.get_previous_by_file_created_time()
queryset = [vid_1, vid_2, vid_3]
return Response(VideosSerializer(queryset,many=True).data)
We could get these kinds of things only if DateTime field is not null.
for more, refer this official documentation and stackoverflow answer
Model
class makitVerifications (models.Model):
Matter_fk = models.ForeignKey(Matter,on_delete=models.CASCADE , related_name = "makit_validate")
user_fk = models.ForeignKey('auth.User',on_delete=models.CASCADE , related_name = 'verification_by' , )
Serializer
class UserVerifyCountSerializer (serializers.ModelSerializer):
# Tried makit_validate = makitSerializer (read_only = True)
class Meta :
model = makitVerifications
fields = ( 'user_fk', 'makit_validate', 'Matter_fk')
UserVerifyCountSerializer._declared_fields['makit_validate']= makitSerializer (read_only = True)
View
class UserVerfiyCountViewSet(generics.ListCreateAPIView):
serializer_class = UserVerifyCountSerializer
def get_queryset(self):
queryset = makitVerifications.objects.all()
return queryset
JSON
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"user_fk": 1,
"Matter_fk": 1
},
{
"user_fk": 1,
"Matter_fk": 1
}
]
The field "makit_validate" which is expected as nested object is missing in the JSON .How can I get nested object of the related model in json .
I also tried an example from the docs but the same field is missing from JSON
Also how to omit/disable fields from the objected being nested in the above serializer class
Makie Serilazer
class makitSerializer(serializers.ModelSerializer):
kamaz = kamazSerializer(many=True,read_only=True)
facilities = FacilitiesSerializer (many =True,read_only=True)
class Meta :
model = Makit
fields = '__all__'
## Makit Model ##
class Makit (models.Model):
pincode = models.IntegerField()
area = models.CharField(max_length=200)
street = models.CharField(max_length=200)
landmark = models.CharField(max_length=100,blank=True , null = True)
contact_no= models.BigIntegerField()
From doc here: Serializer fields
Try:
makit_validate = makitSerializer(source="the_field_you_find_makit" read_only = True)