Currently i am using django rest_framework.I have two different class name as Customer and Customerinfo . My code is working properely . Now I want to insert value in the customer serializer. In CustomerSerializer it has Customerinfo field.
These are the serializer:
class CustomerinfoSerializer(serializers.ModelSerializer):
class Meta:
model = Choice
fields = ['id','phone']
class CustomerSerializer(serializers.ModelSerializer):
info = CustomerinfoSerializer(many=True, source='customerinfo_set')
class Meta:
model = Question
fields = ["id","name","info"]
How can i use post method to insert value ? sample values are:
{
"name": "user10",
"info":
[
{ "phone":12345
},
{ "phone":54321
}
]
}
If I understand you correctly your looking for writable nested serializers.
As documentation says,
by default nested serializers are read-only. If you want to support
write-operations to a nested serializer field you'll need to create
create() method.
You could try something similar to this (but you need to adjust your naming):
class CustomerSerializer(serializers.ModelSerializer):
info = CustomerinfoSerializer(many=True, source='customerinfo_set')
class Meta:
model = Question
fields = ["id", "name", "info"]
def create(self, validated_data):
info_data = validated_data.pop('info')
question = Question.objects.create(**validated_data)
for info in info_data:
Choice.objects.create(question=question, **info)
return question
Related
This is specifically for graphene-django (not just graphene) when performing a node(id: ...) {} query.
Assume a fixed schema with 2 (or more) different GraphQL object types using graphene_django.DjangoObjectType linked to the same django model:
import graphene_django
from .models import Org as OrgModel
class Org(graphene_django.DjangoObjectType):
class Meta:
model = OrgModel
fields = (
"id",
"name",
"billing"
)
class AnonymousOrg(graphene_django.DjangoObjectType):
class Meta:
model = OrgModel
fields = (
"id",
"name",
)
Let's assume a query to Org of ID 7eca71ed-ff04-4473-9fd1-0a587705f885.
btoa('Org:7eca71ed-ff04-4473-9fd1-0a587705f885')
'T3JnOjdlY2E3MWVkLWZmMDQtNDQ3My05ZmQxLTBhNTg3NzA1Zjg4NQ=='
{
node(id: "T3JnOjdlY2E3MWVkLWZmMDQtNDQ3My05ZmQxLTBhNTg3NzA1Zjg4NQ==") {
id
__typename
... on Org {
id
}
}
}
Return:
{
"data": {
"node": {
"id": "QW5vbnltb3VzT3JnOjdlY2E3MWVkLWZmMDQtNDQ3My05ZmQxLTBhNTg3NzA1Zjg4NQ==",
"__typename": "AnonymousOrg"
}
}
}
It returns the other object type 'AnonymousOrg:7eca71ed-ff04-4473-9fd1-0a587705f885', despite the relay ID specifying it was an Org object.
Is there a way in graphene-django to "hint" or provide detail to assure the return type if what's specified in the ID and its fragment?
Clarification on question
Other questions were discussing graphene, not specifically graphene-django, which is doing the type determinations in this case.
This is different from Django-graphene multiple types for the same model, as that one asked about how to handle field permissions and opened possibility to reconsidering schema structure (e.g. splitting between Public and Private API schemas) - that isn't a choice in this question.
Credit: This is graphene django models are based on the ones from the question at Django-graphene multiple types for the same model.
P.S. I created a bug issue on the tracker here: graphene-django#1291 named "DjangoObjectType using the same django model do not resolve to correct relay object". Supposedly this issue was resolved years ago, but communication lines got crossed.
The credit to this goes to #Boolangery (GitHub)'s workaround on May 25th, 2020.
class FixRelayNodeResolutionMixin:
#classmethod
def get_node(cls, info, pk):
instance = super(FixRelayNodeResolutionMixin, cls).get_node( info, pk)
setattr(instance, "graphql_type", cls.__name__)
return instance
#classmethod
def is_type_of(cls, root, info):
if hasattr(root, "graphql_type"):
return getattr(root, "graphql_type") == cls.__name__
return super(FixRelayNodeResolutionMixin, cls).is_type_of(root, info)
Usage, here is an example with a django User model with two Django Graphene objects pointing to it: PublicUserType and UserType:
from graphene_django import DjangoObjectType
class PublicUserType(FixRelayNodeResolutionMixin, DjangoObjectType):
class Meta:
model = User
interfaces = (graphene.relay.Node,)
fields = ['id', 'first_name', 'last_name']
class UserType(FixRelayNodeResolutionMixin, DjangoObjectType):
class Meta:
model = User
interfaces = (graphene.relay.Node,)
fields = ['id', 'first_name', 'last_name', 'profile']
today I was the same issue and I solved it as stackoverflow
For me works reorder the classes types from class type with less fields to class type with more fields.
So,
class AnonymousOrg(graphene_django.DjangoObjectType):
class Meta:
model = OrgModel
fields = (
"id",
"name",
)
class LiteOrg(graphene_django.DjangoObjectType):
class Meta:
model = OrgModel
fields = (
"id",
"name",
"billing"
)
class OrgAll(graphene_django.DjangoObjectType):
class Meta:
model = OrgModel
fields = '__all__'
So I like the idea of using class-based views and ModelSerializers but I have an issue with it for my particular use case. Maybe I am not using it as it's intended to be used.
class CarSerializer(serializers.ModelSerializer):
class Meta:
model = CarModel
fields = ['car_name']
# A car can have build for multiple years
class MakelHistorySerializer(serializers.ModelSerializer):
car = CarSerializer(many=True, read_only=True)
class Meta:
model = MakeHistoryModel
fields = ['model_year', 'car']
The response is:
{
"car": {
"car_name": "Fiesta"
},
"model_year": "2020"
}
My two model classes, CarModel and MakeHistoryModel have ["id", "car_name", "manufacturer"] and ["id", "car_id", "model_year", "country_id"] fields respectively.
What kind of a response I really want is:
{
"car_name": "Fiesta",
"model_year": "2020"
}
How would I do this?
You don't need to first serializer (CarSerializer).Just this serializer which has SerializerMethodField enough for your output:
class MakelHistorySerializer(serializers.ModelSerializer):
car_name = serializers.SerializerMethodField()
class Meta:
model = MakeHistoryModel
fields = ['model_year', 'car_name']
def get_car_name(self,obj):
return obj.car.name if obj.car_id else ''
# I don't know your model so to avoid NoneType error, I added this check
I have two models, one contains the other in a foreignKey relationship, I wanted to make an API that would return the combine of these two models, so I attempted to use nested Serializers to add the related model as well, but the data are not all on the same level, the related models is a object inside the first.
Here are the Models
class ModelOne(models.Model):
last_counter = models.IntegerField()
class ModelTwo(models.Model):
model_one = models.ForeignKey(ModelOne, on_delete=models.CASCADE)
category = models.CharField(max_length=64)
counter_type = models.CharField(max_length=32)
Here are the serializers
class ModelOneSerializer(serializers.ModelSerializer):
class Meta:
model = ModelOne
fields = "__all__"
class ModelTwoSerializer(serializers.ModelSerializer):
model_one= ModelOneSerializer(read_only=True)
class Meta:
model = ModelTwo
fields = "__all__"
This would return from the API in the form of
{
"category" : ...,
"counter_type" : ...,
"model_one" : {
"last_counter" : ...
}
}
But I don't want the response to be like that, I want it more like this
{
"category" : ...,
"counter_type" : ...,
"last_counter" : ...,
}
Is there a way to achieve this through serializers?
Use SerializerMethodField
from rest_framework.fields import SerializerMethodField
class ModelTwoSerializer(serializers.ModelSerializer):
last_counter = SerializerMethodField()
class Meta:
model = ModelTwo
fields = "__all__"
def get_last_counter(self, obj):
return ModelOneSerializer(obj.model_one).data['last_counter']
When creating custom fields(field_one for example) with SerializerMethodField, you have to create a method called get_field_one, for this method to be automatically detected by the serializer.
You can achieve what you want to do using SerializerMethodField from drf fields:
SerializerMethodField is a read-only field that computes its value at request processing time, by calling a method on the serializer class it is attached to. For example for your case it will look like this. Notice that the computed last_counter is added on the serialized model fields.
from rest_framework.fields import SerializerMethodField
class ModelTwoSerializer(serializers.ModelSerializer):
last_counter = serializers.SerializerMethodField()
class Meta:
model = ModelTwo
fields = ["category", "counter_type", "last_counter"]
def get_last_counter(self, obj):
return int(obj.model_one.last_counter)
SerializerMethodField accepts method_name, but it’s usually more convenient to use the default pattern for naming those methods, which is get_. Just make sure you‘re not overburdening your method fields with any heavy-lifting operations.
You can read more on the official documentation:enter link description here
I am trying to create a model in Django that has a many-to-many relationship to another model with extra fields. I am using the rest framework to provide CRUD operations on these and am having a chicken-and-egg scenario I believe...
The issue is that when I go to POST the new MainObject, it throws an error in the many-to-many part due to not having a MainObject id to point to. But I want it to point to the MainObject I am creating, which doesn't exist at time of POST'ing. I believe this to be an issue with the serializers, but am unsure of how to resolve it. I assume my assumptions might also be off in how I am formulating the POST data.
I am using Django 2.1.8
Model Code
class RelatedObject(models.Model):
...
class MainObject(models.Model):
related_objects = models.ManyToManyField(RelatedObject, through='ManyRelatedObject')
class ManyRelatedObject(models.Model):
main_object = models.ForeignKey(MainObject, on_delete=models.DO_NOTHING)
related_object = models.ForeignKey(RelatedObject, on_delete=models.DO_NOTHING)
other_attribute = models.BooleanField(...)
Serializer Code
class ManyRelatedObjectSerializer(serializers.ModelSerializer):
main_object = serializers.PrimaryKeyRelatedField(queryset=MainObject.objects.all())
related_object = serializers.PrimaryKeyRelatedField(queryset=RelatedObject.objects.all())
class Meta:
model = ManyRelatedObject
fields = '__all__'
class MainObjectSerializer(serializers.ModelSerializer):
related_object = ManyRelatedObjectSerializer(many=True)
class Meta:
model = MainObject
fields = '__all__'
POST Payload
( It is assumed that there exists a RelatedObject that has an id of 1)
{
"related_object": [
{
"related_object": 1,
"other_attribute": true
}
],
...
}
Response
{
"related_object": [
{
"main_object": [
"This field is required."
]
}
]
}
Goal Response:
{
"id": 1,
"related_object": [
{
"main_object": 1,
"related_object": 1,
"other_attribute": true
}
],
...
}
REST endpoint setup
class MainObjectViewSet(viewsets.ModelViewSet):
queryset = MainObject.objects.all()
serializer_class = MainObjectSerializer
Override the __init__() method of the MainObjectSerializer.
class MainObjectSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.context['request'].method == 'GET':
self.fields['related_object'] = ManyRelatedObjectSerializer(many=True)
related_object = ManyRelatedObjectSerializer(many=True)# remove this line
class Meta:
model = MainObject
fields = '__all__'
What this snippt do is, the serializer will render the response/output using ManyRelatedObjectSerializer serializer, if the request is a HTTP GET, otherwise it will render the stock mode(PrimaryKeyRelatedField)
For posterity, ended up hand-jamming this with poorly overridden create and update methods, due to time constraints. Seems ridiculous that django can't handle this scenario, seems like it's a fairly common use case...
TL;DR: What could be the reason the incoming data for one of my serializers does not get processed?
I'm working on a serializer for a nested relationship. The serializer should get a list of UUIDs, so that I can make many to many relationships. Here is the model:
class Order(
UniversallyUniqueIdentifiable,
SoftDeletableModel,
TimeStampedModel,
models.Model
):
menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
custom_choice_items = models.ManyToManyField(CustomChoiceItem, blank=True)
price = models.ForeignKey(MenuItemPrice, on_delete=models.CASCADE)
amount = models.PositiveSmallIntegerField(
validators=[MinValueValidator(MINIMUM_ORDER_AMOUNT)]
)
Here is the data (my post body) with which I hit the route in my tests:
data = {
"checkin_uuid": self.checkin.uuid,
"custom_choice_items": [],
"menu_item": self.menu_item.uuid,
"price": self.menu_item_price.uuid,
"amount": ORDER_AMOUNT,
}
response = self.client.post(self.order_consumer_create_url, self.data)
Note that the empty list for custom_choice_items does not change anything. Even if I fill it with values the same error occurs. And last but not least here are the serializers:
class CustomChoiceItemUUIDSerializer(serializers.ModelSerializer):
"""Serializer just for the uuids, which is used when creating orders."""
class Meta:
model = CustomChoiceItem
fields = ["uuid"]
....
# The serializer that does not work
class OrderSerializer(serializers.ModelSerializer):
menu_item = serializers.UUIDField(source="menu_item.uuid")
custom_choice_items = CustomChoiceItemUUIDSerializer()
price = serializers.UUIDField(source="price.uuid")
wish = serializers.CharField(required=False)
class Meta:
model = Order
fields = [
"uuid",
"menu_item",
"custom_choice_items",
"price",
"amount",
"wish",
]
The problem is now, that when I leave out many=True, I get the error:
{'custom_choice_items': [ErrorDetail(string='This field is required.', code='required')]}
And If I set many=True I just simply don't get any data. By that I mean e.g. the value of validated_data["custom_choice_items"] in the serializers create() method is just empty.
What goes wrong here?
I even checked that the data is in the request self.context["request"].data includes a key custom_choice_items the way I pass the data to this view!
EDIT: Here is the data I pass to custom_choice_items:
data = {
“checkin_uuid”: self.checkin.uuid,
“custom_choice_items”: [{“uuid”: custom_choice_item.uuid}],
“menu_item”: self.menu_item.uuid,
“price”: self.menu_item_price.uuid,
“amount”: ORDER_AMOUNT,
}
self.client.credentials(HTTP_AUTHORIZATION=“Token ” + self.token.key)
response = self.client.post(self.order_consumer_create_url, data)
When you post data using the test api client, if the data contains nested structure you should use format=json, like this:
response = self.client.post(self.order_consumer_create_url, data, format='json')
Did you override .create method in the serializer? Something like this should work:
from django.db import transaction
class OrderSerializer(serializers.ModelSerializer):
# your fields and Meta class here
#transaction.atomic
def create(self, validated_data):
custom_choice_items = validated_data.pop('custom_choice_items')
order = super().create(validated_data)
order.custom_choice_items.add(*custom_choice_items)
return order
By the way you don't really need to define CustomChoiceItemUUIDSerializer if is just the primary key of that.