How to serialize ForeignKey in Django rest? - django

I'm using the latest version of django rest framework.
I have this model:
class Subscriptions(models.Model):
subs_list = models.ForeignKey(SubsList, verbose_name='Subscription list', on_delete=models.CASCADE, related_name='subs_list') # идентификатор подписного листа
subscriber = models.ForeignKey(Subscribers, verbose_name='Subscriber', on_delete=models.CASCADE) # идентификатор подписчика
created_date = models.DateTimeField(verbose_name='Created date', auto_now=True) # дата добавления подписчика в подписной лист
deleted = models.NullBooleanField(verbose_name='Deleted') # True-удален из подписного листа, False/null-в подписном листе
How do I serialize it? The main question is how to serialize ForeignKey, that would be associated with the query related data, i.e. NOT:
"id": 29,
"created_date": "2018-03-01T14:28:41.237742Z",
"deleted": false,
"subs_list": 1,
"subscriber": 1
but like this
"id": 29,
"subs_list": {
"id": 1,
"uuid": "d183bab7-af26-48f8-9ef5-ea48e09a95a9",
"name": "TEST",
"description": "TEST",
"created_date": "2018-03-01T13:15:18.808709Z",
"deleted": null,
"user": 6
},
"subscriber": {
"id": 1,
"bot_id": "1",
"name_messenger": "11",
"username": "1",
"first_name": "1",
"last_name": "1",
"created_date": "9999-03-01T16:47:51.440000Z",
"subscribed": true,
"chat_bot": "1",
"phone": "1",
"user": 1
},
"created_date": "2018-03-01T14:28:41.237742Z",
"deleted": false
I have such a serializer:
...
class SubscriptionsSerializer(serializers.ModelSerializer):
subs_list = SubsListSerializer(read_only=True)
subscriber = SubscribersSerializer(read_only=True)
class Meta:
model = Subscriptions
fields = '__all__'
When get requests everything is ok, but how to update and add data is not clear, error:
IntegrityError at /subscriptions/subscriptions/
null value in column "subs_list_id" violates not-null constraint
DETAIL: Failing row contains (41, 2018-03-01 16:10:02.383625+00, f, null, null).
I struggle with this problem for a very long time, read all the related answers, but there is no clarity.

remove read_only=True and change your serializer.py as below
class SubscriptionsSerializer(serializers.ModelSerializer):
subs_list = SubsListSerializer()
subscriber = SubscribersSerializer()
class Meta:
model = Subscriptions
fields = '__all__'
def create(self, validated_data):
sub_lst = SubsList.objects.create(**validated_data['subs_list'])
subscriber = Subscribers.objects.create(**validated_data['subscriber'])
return Subscriptions.objects.create(subs_list=sub_lst, subscriber=subscriber, deleted=validated_data['deleted'])
And your creation payload will be like this,
{
"subs_list": {
"uuid": "d183bab7-af26-48f8-9ef5-ea48e09a95a9",
"name": "TEST",
"description": "TEST",
# etc etc
},
"subscriber": {
"bot_id": "1",
"name_messenger": "11",
"username": "1",
# etc etc
},
"deleted": null
}
I think you have several ForeignKey relationships are there, so you have to map those things carefully in create()
Similar way, you can override update() and which can be used while API updation too.
See this official doc for more info

Related

Django-Rest-Framework POST request to ManyToMany Field

I have a django model that is a message. It has a name which is a CharField, then also an array of users which is a ManyToManyField.
So This is what my API looks like:
[
{
"id": 13,
"content": "hej",
"date": "2019-07-09",
"sender": {
"id": 1,
"username": "william"
}
},
{
"id": 14,
"content": "hej william",
"date": "2019-07-09",
"sender": {
"id": 3,
"username": "ryan"
}
}
]
What I've tried to send via postman POST:
{
"content": "Hello",
"sender": {"username": "william"},
"date": "2019-09-02"
}
The Error I get:
sqlite3.IntegrityError: NOT NULL constraint failed: chat_message.sender_id
ManyToManyField(Userprofile=User):
class Message(models.Model):
sender = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name="sendermessage")
content = models.CharField(max_length=500)
date = models.DateField(default=date.today)
canview = models.ManyToManyField(UserProfile, blank=True, related_name="messagecanview")
class Meta:
verbose_name_plural = 'Messages'
def __str__(self):
return "{sender}".format(sender=self.sender)
Assuming that you have a MessageSerializer class implemented, you could override its create() method in order to support writable nested representations:
class MessageSerializer(serializers.ModelSerializer):
...
def create(self, validated_data):
sender_data = validated_data.pop('sender')
sender = UserProfile.objects.create(**sender_data)
return Message.objects.create(sender=sender, **validated_data)
You pop the sender data from the dictionary, create a UserProfile instance with the attributes in there and then attach it to your Message creation.
This will resolve your error since now there is a real sender created before the actual message has been saved.

Django-RestFramework Custom Seriliazation

I have problem in custom serialization and I look in documentation for hours, but I couldn't figure out what I will do. I have nested serializers object like below but I want to have non-nested object, can anyone help me with this?
Nested Object:
{
"id": 1,
"adDate": "20-08-2016",
"price": "30.50",
"city": "Istanbul",
"latitude": "28.987509",
"longitude": "41.040353",
"isPublished": true,
"book": {
"id": 1,
"bookName": "GameOfThrones",
"category": "Adventure",
"photo": "http://localhost:8000/media/advert_images/game_of_thrones.jpg",
"description": "Adventure Book",
"author": "Emre Yavuz",
"language": "Sangridce",
"publicationDate": "2023",
"publisher": "Deu_Yapim",
"edition": "22",
"page_number": 900
}
}
Non-Nested Object:
{
"id": 1,
"adDate": "20-08-2016",
"price": "30.50",
"city": "Istanbul",
"latitude": "28.987509",
"longitude": "41.040353",
"isPublished": true,
"bookName": "GameOfThrones",
"category": "Adventure",
"photo": "http://localhost:8000/media/advert_images/game_of_thrones.jpg",
"description": "Adventure Book",
"author": "Emre Yavuz",
"language": "Sangridce",
"publicationDate": "2023",
"publisher": "Deu_Yapim",
"edition": "22",
"page_number": 900
}
My Serializer Class:
class AdvertSerializer(serializers.ModelSerializer):
book = BookSerializer()
class Meta(object):
model = Advert
fields = ('id', 'adDate', 'price',"city","latitude","longitude","isPublished",'book','seller')
depth = 2
.
class BookSerializer(serializers.ModelSerializer):
photo = serializers.ImageField(max_length=None,use_url=True)
class Meta(object):
model = Book
I don't know why you would prefer a non-nested representation but in order to archive that, you need to specify book fields, one by one on your serializer using the source field attribute.
class AdvertSerializer(serializers.ModelSerializer):
book_name = serializer.CharField(source='book.bookName')
description = serializer.CharField(source='book.description')
# add the rest of the book fields in that way
class Meta(object):
model = Advert
fields = ('id', 'adDate', 'price',"city","latitude","longitude","isPublished",'book','seller')

Distinct field Rest Framework Django

I need to make a distinct with a field of my model and not how to make
My model is:
class CheckList(CoreModel):
date = models.DateTimeField(default=datetime.now, blank=True, null=True, verbose_name=_('Date'))
establishment = models.ForeignKey(Establishment, related_name="checklists", on_delete=models.CASCADE, null=True, verbose_name=_('Establishment'))
user = models.ForeignKey(ITManager, related_name="checklists", on_delete=models.CASCADE, null=True, verbose_name=_('User'))
class Meta:
verbose_name_plural = _("Checklist")
verbose_name = _("Checklists")
def __str__(self):
return str(self.date)
My serializer and view:
class CheckListSerializer(BulkSerializerMixin, serializers.ModelSerializer):
user = ITManagerSerializer()
class Meta:
model = CheckList
list_serializer_class = BulkListSerializer
fields = ['id', 'user', 'establishment', 'date']
class ChecklistBulkViewSet(BulkModelViewSet):
queryset = CheckList.objects.values('establishment', 'user', 'date').distinct()
model = CheckList
serializer_class = CheckListSerializer
filter_class = ChecklistFilter
The api return me:
"results": [
{
"id": 1,
"user": {
"id": 3,
"first_name": "Andres",
"last_name": "Gallardo",
"rut": "21312",
"email": null,
"user_name": "andres",
"password": null,
"user": 4,
"country": [],
"active": true
},
"establishment": 3,
"date": "2016-06-14T15:15:00Z"
},
{
"id": 2,
"user": {
"id": 2,
"first_name": "Ramiro",
"last_name": "Gutierrez",
"rut": "15616+",
"email": null,
"user_name": null,
"password": null,
"user": 2,
"country": [
{
"id": 1,
"name": "Argentina",
"code_area": null
}
],
"active": false
},
"establishment": 3,
"date": "2016-06-09T15:40:04Z"
}]
I need you just leave me an establishment with the same id
any suggestions??
Thanks !

Why django-rest-framework (using django-nonrel with mongodb) sets to null child objects PKs when updating an instance

It happens with any object instance that contains embbeded models.
When I attempt to update the father instance, it sets to null its childs PKs.
This is an extract of a json object already updated:
{
"pk": "558d023d153bd41930b3fcf0",
"checkgroup_id": "checkgroupid 5",
"name": "checkgroup five",
"description": "this an example description",
"control_config": {
"control_config_1": {
"pk": null,
"params": {
"param2": {
"pk": null,
"value": "value2",
"mandatory": false,
"default": "default"
},
"param1": {
"pk": null,
"value": "value1",
"mandatory": false,
"default": "default"
}
},
"exceptions": {
"exception1": {
"pk": null,
"description": "description example",
"params": [
{
"pk": null,
"value": "value1",
"mandatory": false,
"default": "default"
},
{
"pk": null,
"value": "value2",
"mandatory": false,
"default": "default"
}
]
}
}
}
},
and this is the Model:
class CheckGroup(models.Model):
"""It defines a set of controls to be applied. """
checkgroup_id = models.TextField(max_length=250)
name = models.TextField(max_length=250)
description = models.TextField(max_length=250)
control_config = DictField(EmbeddedModelField(ControlConfig))
controls = DictField(EmbeddedModelField(Control))
with its serializer defined as:
class CheckGroupSerializer(serializers.ModelSerializer):
""" Transforms CheckGroup into json
"""
control_config = serializers.DictField(child=ControlConfigSerializer())
controls = serializers.DictField(child=ControlSerializer())
pk = serializers.CharField()
class Meta:
""" It lets to choose the model that will be serialize and its fields
"""
model = CheckGroup
fields = ('pk', 'checkgroup_id', 'name', 'description', 'control_config', 'controls')
Well, I found out the reason on django-rest-framework docs.
The problem is that the framework does not deal with that, and should be implemented by us.
The doc says:
Because the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default ModelSerializer .create() and .update() methods do not include support for writable nested representations.

Django REST Meta data for M2M missing

On my json output I don't seem to get key value pairs on my m2m field attribute_answers. see the code below. How to I add in the attribute_answers fields?
json
{
"id": 20,
"name": "Jake",
"active": true,
"type": {
"id": 1,
"name": "Human",
"active": true,
"created": "2013-02-12T13:31:06Z",
"modified": null
},
"user": "jason",
"attribute_answers": [
1,
2
]
}
Serializer
class ProfileSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(slug_field='username')
attribute_answers = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Profile
depth = 2
fields = ('id', 'name', 'active', 'type', 'user', 'attribute_answers')
def restore_object(self, attrs, instance=None):
"""
Create or update a new snippet instance.
"""
if instance:
# Update existing instance
instance.name = attrs.get('name', instance.name)
instance.active = attrs.get('active', instance.active)
instance.type = attrs.get('type', instance.type)
instance.attribute_answers = attrs.get('attribute_answers', instance.attribute_answers)
return instance
# Create new instance
return Profile(**attrs)
If I understand your question correctly, you want a Nested Relationship. In your ProfileSerializer, you'll want:
attribute_answers = AttributeAnswerSerializer(many=True)
This will display all the attributes of each associated attribute_answer model and give you something like:
{
"id": 20,
"name": "Jake",
"active": true,
"type": {
"id": 1,
"name": "Human",
"active": true,
"created": "2013-02-12T13:31:06Z",
"modified": null
},
"user": "jason",
"attribute_answers": [
{"id": 1, "foo": "bar"},
{"id": 2, "foo": "bar2"}
]
}