I have the following setup in Django REST Framework:
models.py:
class Device(models.Model):
name = models.CharField()
latitude = models.FloatField()
longitude = models.FloatField()
altitude = models.FloatField()
class Event(models.Model):
id_device = models.ForeignKey(Device, related_name='events')
name = models.CharField()
date = models.CharField()
time = models.CharField()
class Values(models.Model):
id_device = models.ForeignKey(Device)
id_event = models.ForeignKey(Event, related_name='values')
x = models.FloatField()
y = models.FloatField()
z = models.FloatField()
time = models.IntegerField()
serializers.py:
class DeviceSerializer(serializers.ModelSerializer):
events = EventSerializer(many=True)
class Meta:
model = Device
fields = ('url', 'id', 'name', 'latitude', 'longitude', 'altitude', 'events')
def create(self, validated_data):
events_data = validated_data.pop('events')
device = Device.objects.create(**validated_data)
for event in events_data:
Events.objects.create(device=device, **event)
return device
class EventSerializer(serializers.ModelSerializer):
values = ValuesSerializer(many=True)
class Meta:
model = Event
fields = ('url', 'id', 'name', 'date', 'time', 'values')
def create(self, validated_data):
values_data = validated_data.pop('values')
event = Event.objects.create(**validated_data)
for value in values_data:
Values.objects.create(event=event, **value)
return event
class ValuesSerializer(serializers.ModelSerializer):
class Meta:
model = Values
fields = ('x', 'y', 'z', 'time')
When I try to post an event with some values assigned using a JSON file like this one:
{
"name": "name_example",
"date": "date_example",
"time": "time_example",
"values": [
{
"x": 1,
"y": 2,
"z": 3,
"time": 1
},
{
"x": 10,
"y": 20,
"z": 30,
"time": 2
},
{
"x": 100,
"y": 200,
"z": 300,
"time": 4
}
]
}
I get the error IntegrityError: NOT NULL constraint failed: drf_event.id_device_id
I'm new to this framework, so what can I do in order to post new events with values assigned to an existing device?
You haven't a key pointing to device in your EventSerializer. You miss the id_device.
class EventSerializer(serializers.ModelSerializer):
values = ValuesSerializer(many=True)
class Meta:
model = Event
fields = ('id_device', 'url', 'id', 'name', 'date', 'time', 'values')
And you need to add the key in your json:
{
"id_device": 1,
"name": "name_example",
"date": "date_example",
"time": "time_example",
"values": [
{
"x": 1,
"y": 2,
"z": 3,
"time": 1
},
{
"x": 10,
"y": 20,
"z": 30,
"time": 2
},
{
"x": 100,
"y": 200,
"z": 300,
"time": 4
}
]
}
Related
How do I update data in a linked django rest model
Here is my json
{
"doc_type": 1,
"warehouse": 4,
"date": "2022-06-09",
"number": 980,
"contragent": 2,
"comment": "testcom;mgment",
"items": [
{
"product": 7,
"buy_price": "168.00",
"sell_price": "500.00",
"quantity": 17
},
{
"product": 8,
"buy_price": "168.00",
"sell_price": "500.00",
"quantity": 17
}
]
}
I can't change the data in the "items" dictionary
If I submit only one "product" element, will the other one be deleted? And if, for example, three, will it be added?
this is what I have at the moment, records are created without problems, but I can only update the "ConsignmentNote" model
#serialisers.py
class ConsignmentNoteSerializer(serializers.ModelSerializer):
creator = serializers.HiddenField(default=serializers.CurrentUserDefault())
doc_type = serializers.IntegerField(read_only=True)
# contragent_detail = ContragentSerializer(source='contragent', read_only=True)
items = ConsignmentItemSerializer(many=True)
class Meta:
model = ConsignmentNote
fields = ['doc_type', "warehouse", 'date', 'number', 'contragent', 'comment', 'creator', 'items']
read_only_fields = ['id' ]
def create(self, validated_data):
items = validated_data.pop('items')
note = ConsignmentNote.objects.create(**validated_data, doc_type = 1)
for item in items:
product = item.pop('product')
item = ConsignmentItem.objects.create(consignmentnote=note, product=product ,**item)
return note
def update(self, instance, validated_data):
instance.date = validated_data.pop('date', instance.date)
instance.comment = validated_data.pop('comment', instance.comment)
return instance
Currently, I've the next models:
class Product(models.Model):
name = models.CharField('name', max_length=60)
class Variant(models.Model):
name = models.CharField('name', max_length=60)
class VariantValue(models.Model):
value = models.CharField('value', max_length=60)
variant = models.ForeigKey('variant', to=Variant, on_delete=models.CASCADE)
product = models.ForeigKey('product', to=Product, on_delete=models.CASCADE)
I want to get the next json result:
{
"name": "My product",
"variants": [
{
"variant_id": 1,
"values": ["Value 1", "Value 2", "Value 3"]
},
{
"variant_id": 2,
"values": ["Value 4", "Value 5", "Value 6"]
},
]
}
It's possible with a Serializer or do I've to make the json manually?
I only have the next ModelSerializer:
class ProductModelSerializer(serializers.ModelSerializer):
variant_list = serializers.SerializerMethodField('get_variant_list', read_only=True)
class Meta:
model = Product
fields = [..., 'variant_list']
def get_variant_list(self, obj):
variant_list = VariantValue.objects.filter(product_id=obj.id)
variant_list_grouped = variant_list.values('variant', 'value').annotate(count=Count('variant'))
res = []
for variant in variant_list_grouped:
pass
return []
Thanks!
class ProductModelSerializer(serializers.ModelSerializer):
variant_list = serializers.SerializerMethodField('get_variant_list', read_only=True)
class Meta:
model = Product
fields = [..., 'variant_list']
def get_variant_list(self, obj):
variant_list = VariantValue.objects.filter(product_id=obj.id)
return [{"variant_id": variant.variant_id, "values": VariantValue.objects.filter(variant_id = variant.variant_id).values("values", flat=True)} for variant in variant_list]
Using Krishna Singhal answer as a basis I achieved the following script
def get_variant_list(self, product):
variant_list = VariantValue.objects.filter(product_id=obj.id)
variant_list_grouped = variant_list.values('variant',).annotate(count=Count('variant'))
return [{"variant": variant['variant'], "values": VariantValue.objects.filter(
product_id=obj.id, variant_id=variant['variant']).distinct('value').values_list('value', flat=True)} for variant in variant_list_grouped]
I get the next json:
{
...,
"variantList": [
{
"variant": 1,
"values": [
"Value 1",
"Value 2"
]
},
{
"variant": 3,
"values": [
"Black",
"Red",
"Green"
]
},
{
"variant": 2,
"values": [
"S",
"M",
"G"
]
}
],
}
Create a new serializer for VariantValue and nest it thats it. eg:
# serializer
class VariantValueSerializer(serializers.ModelSerializer):
variant_id = serializer.IntegerField(source="id", read_only=True)
class Meta:
model = VariantValue
fields = ["variant_id", "values"]
class ProductModelSerializer(serializers.ModelSerializer):
variant_list = VariantValueSerializer(source="variantvalue_set", many=True, read_only=True)
class Meta:
model = Product
fields = [..., 'variant_list']
you dont have to manually format serializers are there for it.
I have a project with Django Rest Framework and I have a complex ViewSet that uses several models and serializers to compound a large and complex json. Everything works fine, but I notice that HotelSerializer, that is a ModelSerializer, is returning the stored value of field category, instead of the human readable value of its model choices.
This is the model:
class Hotel(models.Model):
ONE_STAR = '*'
TWO_STARS = '**'
THREE_STARS = '***'
FOUR_STARS = '****'
FIVE_STARS = '*****'
GRAND_TOURISM = 'GRAND_TOURISM'
NA = 'NA'
SPECIAL = 'SPECIAL'
ECO = 'ECO'
BOUTIQUE = 'BOUTIQUE'
HOTEL_CATEGORY_CHOICES = (
(ONE_STAR, _('*')),
(TWO_STARS, _('**')),
(THREE_STARS, _('***')),
(FOUR_STARS, _('****')),
(FIVE_STARS, _('*****')),
(GRAND_TOURISM, _('Grand Tourism')),
(NA, _('NA')),
(SPECIAL, _('Special')),
(ECO, _('Eco-Hotel')),
(BOUTIQUE, _('Boutique-Hotel'))
)
company = models.OneToOneField(Company, on_delete=models.CASCADE, primary_key=True, verbose_name=_('Company'))
code = models.CharField(max_length=10, verbose_name=_('Code'))
zone = models.ForeignKey(Zone, on_delete=models.PROTECT, related_name='hotels', verbose_name=_('Zone'))
category = models.CharField(max_length=20, choices=HOTEL_CATEGORY_CHOICES, verbose_name=_('Category'))
capacity = models.IntegerField(verbose_name=_('Capacity'))
position = models.DecimalField(max_digits=11, decimal_places=2, default=0.00, verbose_name=_('Position'))
in_pickup = models.BooleanField(default=False, verbose_name=_('In pickup?'))
is_active = models.BooleanField(default=True, verbose_name=_('Is active?'))
latitude = models.FloatField(null=True, blank=True, verbose_name=_('Latitude'))
longitude = models.FloatField(null=True, blank=True, verbose_name=_('Longitude'))
This is the serializer:
class HotelSerializer(serializers.ModelSerializer):
category = serializers.ChoiceField(choices=models.Hotel.HOTEL_CATEGORY_CHOICES)
class Meta:
model = models.Hotel
fields = ('company', 'code', 'zone', 'category', 'capacity', 'position', 'in_pickup', 'is_active', 'latitude', 'longitude')
depth = 4
def __init__(self, *args, **kwargs):
exclude = kwargs.pop('exclude', None)
super(HotelSerializer, self).__init__(*args, **kwargs)
if exclude is not None:
for field_name in exclude:
self.fields.pop(field_name)
This is the ViewSet function where I get the Hotel model information:
def get_hotel(self, company_id):
hotel = Hotel.objects.get(company=company_id)
import ReservationsManagerApp.serializers
return ReservationsManagerApp.serializers.HotelSerializer(hotel, exclude=('company',)).data
And this is the result I get:
"hotel": {
"code": "xxxx...",
"zone": {
"id": 1,
"name": "Zona hotelera",
"city": {
"id": 5,
"name": "Cancun",
"code": "998",
"state": {
"id": 2,
"name": "Quintana Roo",
"code": "98",
"country": {
"id": 1,
"name": "Mexico",
"code": "MX",
"calling_code": "52"
}
}
}
},
"category": "GRAND_TOURISM",
"capacity": 300,
"position": "1.00",
"in_pickup": true,
"is_active": true,
"latitude": null,
"longitude": null
},
You can see that the Category field returns the value 'GRAND_TOURISM', instead of its human readable version of its choices: 'Grand Tourism'. It even misses the translation, so it must return 'Gran Turismo'.
I don't know what I am missing.
This is correct behavior AFAIK, but it is easy to fix.
One solution is to just do
class HotelSerializer(serializers.ModelSerializer):
category = serializers.ChoiceField(choices=Hotel.HOTEL_CATEGORY_CHOICES)
company = CompanySerializer()
...
I'm using the Writable Nested Serializer to serialize my request. I had no problem serializing doing PUT/POST when the data is nested in 1 layer.
(i.e. {name:'personA', project:{ name:'projA', client:'clientA'}})
However, I ran into a problem when it is nested in 2 layers - I couldn't figure out on how to modify the update() function. Please help!
data sample
{
"id": 6106,
"name": {
"id": 213,
"name": "personA"
},
"project": {
"id": 1663,
"project": "ProjectA",
"client": {
"id": 72,
"name": "ClientA"
},
"project_manager": {
"id": 32,
"name": "personB"
}
},
"booking": 100,
"date": "2017-12-01"
}
serializers.py
class projectSerializer(serializers.ModelSerializer):
client = clientSerializer()
project_manager = userSerializer()
class Meta:
model = project
fields = ('id', 'project', 'client', 'project_manager')
class bookingListSerializer(serializers.ModelSerializer):
project = projectSerializer()
name = userSerializer()
class Meta:
model = bookingList
fields = ('id', 'name', 'project', 'booking', 'date')
def update(self, instance, validated_data):
project_data = validated_data.pop('project')
name_data = validated_data.pop('name')
try:
project_instance = project.objects.filter(**project_data)[0]
name_instance = user.objects.filter(**name_data)[0]
except IndexError:
raise serializers.ValidationError
# update the project if request is valid
instance.project = project_instance
instance.name = name_instance
instance.save()
return instance
views.py
# other viewsets...
class bookingListViewSet(viewsets.ModelViewSet):
queryset = bookingList.objects.all()
serializer_class = bookingListSerializer
i am new to DJango Python webservices and i am facing small issue with the serializers.here is my code.
**views.py**
#csrf_exempt
#api_view(['GET'])
def sqlservice(request):
if request.method == 'GET':
posts = tbl_dcs.objects.all()
for each in posts:
manid = each.managerid_id
manname = Managerid.objects.get(id = manid)
nameser = managerSerializer(manname,many=False)
print nameser.data
serializer = PostSerializer(posts, many=True)
return Response({"resource":serializer.data})
**models.py**
from __future__ import unicode_literals
from django.db import models
class Managerid(models.Model):
managername = models.CharField(max_length=25)
def __unicode__(self):
return self.managername
class tbl_dcs(models.Model):
name = models.CharField(max_length=25)
location = models.CharField(max_length=50)
address = models.TextField()
managerid = models.ForeignKey(Managerid,related_name='items')
phonenumber = models.IntegerField()
def __unicode__(self):
return self.name
**serilizer.py**
from rest_framework import serializers
from mysqlservice.models import tbl_dcs
from mysqlservice.models import Managerid
class managerSerializer(serializers.ModelSerializer):
class Meta:
model = Managerid
fields = ('id', 'managername')
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = tbl_dcs
fields = ('id', 'name','location','address','managerid','phonenumber')
if i run the above code the result is...
{
"resource": [
{
"id": 1,
"name": "ramesh",
"location": "hyd",
"address": "kphb,hyd",
"managerid": 10,
"phonenumber": 345345
},
{
"id": 2,
"name": "kpti",
"location": "kphb",
"address": "kphb,6th phase,hyd",
"managerid": 10,
"phonenumber": 45456
}
]
}
my resulting json i am unable to get the managername.How to get the managername based on managerid from the different table?.Please correct me if i am wrong anywhere.
my final json should be like below...
{
"resource": [
{
"id": 1,
"name": "ramesh",
"location": "hyd",
"address": "kphb,hyd",
"managerid": 10,
"phonenumber": 345345,
"managername":"xxxxx"
},
{
"id": 2,
"name": "kpti",
"location": "kphb",
"address": "kphb,6th phase,hyd",
"managerid": 10,
"phonenumber": 45456,
"managername":"yyyyy"
}
]
}
use serializers.SerializerMethodField to get manager name like below..
class PostSerializer(serializers.ModelSerializer):
managername = serializers.SerializerMethodField('get_manager_name')
class Meta:
model = tbl_dcs
fields = ('id', 'name','location','address','managerid','phonenumber','managername')
def get_manager_name(self, obj):
return obj.managerid.managername