Using rest django rest framework for creating new object - django

I have the following models
class Peri(models.Model):
date = models.DateField()
customer = models.ForeignKey(Customer)
class PeriTask(models.Model):
#fields
peri = models.ForeignKey(Peri)
My serializers are the following
class PeriSerializer(serializers.HyperlinkedModelSerializer):
customer = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Peri
fields = ('id', 'date', 'url', 'peritasks', 'customer')
class PeriTaskSerialiazer(serializers.HyperlinkedModelSerializer):
tooth = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = PeriTask
fields = ('id', 'task_type', 'implant', 'furcation', 'bleeding1', 'bleeding2', 'bleeding3', 'plaque1', 'plaque2',
'gingival_margin1', 'gingival_margin2', 'gingival_margin3', 'probing_depth1', 'probing_depth2',
'probing_depth3', 'tooth', 'url', )
and my viewsets are
class PeriodontogrammaViewSet(ModelViewSet):
serializer_class = PeriSerializer
queryset = Peri.objects.all()
class PeriTaskViewSet(ModelViewSet):
serializer_class = PeriTaskSerialiazer
queryset = PeriTask.objects.all()
But when I try to create a new peri using the api it gives me the following integrity error
NOT NULL constraint failed: peri_peri.customer_id
My json data that beeing posted are
{"date": "2014-12-17",
"customer": 27
}
I haven't created a serializer for customer since I am not interested in having api for my other models.

In your serializer, you've set the customer key to read_only:
customer = serializers.PrimaryKeyRelatedField(read_only=True)
Try setting it to False or just simply removing this whole line (which seems superfluous to me)

Related

Django rest framework; how do you use the ID of a foreign key to create an instance through the serializer?

I have two serializers, one for Country and one for my model Foo, I want to save the object by using the foreign key for this model but it errors out whenever I try to validate.
I have this
class Actor(TLPWrappedModel, CommentableModel):
label = models.CharField(max_length=56, unique=True)
country_of_origin = models.ForeignKey(Country, on_delete=models.CASCADE)
class FooSerializer(serializers.ModelSerializer):
country_of_origin = CountrySerializer()
class Meta:
model = Actor
fields = [
'id',
'country_of_origin',
'label',
]
class Country(models.Model):
label = models.CharField(max_length=56, unique=True)
iso_code = models.CharField(max_length=3, unique=True)
class CountrySerializer(serializers.ModelSerializer):
class Meta:
model = Country
fields = [
'iso_code',
'label',
]
And this is what I'm trying to do
serializers = FooSerializer(data={'label': 'Foobar',
'country_of_origin': self.country.id})
serializers.is_valid()
print(serializers.errors)
print(serializers.validated_data)
serializers.save()
But I get this error {'country_of_origin': {'non_field_errors': [ErrorDetail(string='Invalid data. Expected a dictionary, but got int.', code='invalid')]}}
is it possible to use the ID of a foreign key to validate and create the object using the serializer?
We can update the to_represent of the FooSerializer to get the desired output
Try
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Actor
fields = [
'id',
'country_of_origin',
'label',
]
def to_representation(self, instance):
data = super().to_representation(instance)
data['country_of_origin'] = CountrySerializer(instance.country_of_origin)
return data
serializers = FooSerializer(data={'label': 'Foobar', 'country_of_origin': self.country})
serializers.is_valid(raise_expection=True)
serializers.save()
In this I have updated the code to assign the self.country as country_of_origin. Also, I am using the raise_expection in the is_valid method. This method will return the errors as 400 response.
Try
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Actor
fields = [
'id',
'country_of_origin',
'label',
]
You can safely drop defining the 'country of origin` in the FooSerializer
contry_of_origin would be an object, and you are passing an id for it.
Do you need a nested serializer? : country_of_origin = CountrySerializer()
For the example that you have given, I would suggest you to change it to PrimaryKeyRelatedField()
Your serializer would look like:
class FooSerializer(serializers.ModelSerializer):
country_of_origin = serializers.PrimaryKeyRelatedField()
class Meta:
model = Actor
fields = [
'id',
'country_of_origin',
'label',
]

Multiple slug_field in SlugRelatedField Django Rest Framework

In my Django application I am getting Json like this:
"sales_order": 102,
"transport_by": 4,
I want to expand the sales_order and replace it with it's owner's first_name + last_name.
So I tried using slugrelated field but I am not sure how to get two values out of it.
Here's what I tried:
class AtableSOSerializer(serializers.ModelSerializer):
owner = serializers.SlugRelatedField(read_only=True, slug_field='first_name'+' '+'last_name')
class Meta:
model = MaterialRequest
fields = "__all__"
class AtableFlowListSerializer(serializers.ModelSerializer):
class Meta:
model = AllotmentFlow
fields = "__all__"
class AllotmentTableSerializer(serializers.ModelSerializer):
flows = AtableFlowListSerializer(many=True)
sales_order = AtableSOSerializer(read_only=True)
class Meta:
model = Allotment
fields = "__all__"
But obvious error appeared:
AttributeError: 'User' object has no attribute 'first_name last_name'
How do I get the first_name + last_name in my JSON?
i had same problem as you :
in models.py add a proprety to your model then in your serializer make your slug_field is the proprety you created in your model.
#property
def full_name(self):
return self.first_name+" "+self.last_name
owner = serializers.SlugRelatedField(read_only=True, slug_field='full_name')

Django Rest Framework - Ordering a nested reverse lookup serializer

Is it possible to order/sort a serializer availability that is both a reverse lookup and nested inside unit serializer? For example, I wish to order availability by start_time instead of id.
Below is what I tried but I get .order_by('-start_time')' ^ SyntaxError: invalid syntax
Serializers
class AvailabilitySerializer(serializers.ModelSerializer):
staff = StaffSerializer()
class Meta:
model = Availability
fields = ['id','start_time', 'end_time','staff']
class ManagerSerializer(serializers.ModelSerializer):
class Meta:
model = Manager
fields = ['company', 'logo']
class UnitSerializer(serializers.ModelSerializer):
availability = AvailabilitySerializer(source='availability_set.order_by('-start_time')', many=True)
manager = ManagerSerializer()
class Meta:
model = Unit
fields = ['id', 'address', 'manager', 'availability']
You can override the field and provide the serializer with your own queryset and then return the data:
from rest_framework.serializers import SerializerMethodField
class UnitSerializer(serializers.ModelSerializer):
availability = SerializerMethodField()
manager = ManagerSerializer()
class Meta:
model = Unit
fields = ['id', 'address', 'manager', 'availability']
def get_availability(self, instance):
queryset = instance.availability_set.order_by('-start_time')
return AvailabilitySerializer(queryset, many=True).data
SerializerMethodField

Django Rest Framework Ordering Filter, order by nested list length

I'm using OrderingFilter globally through settings.py and it works great.
Now I would like to order on the size of a nested list from a ManyToManyField. Is that possible with the default OrderingFilter?
If not, is there a way I can do it with a custom filter, while keeping the query param ordering in the url (http://example.com/recipes/?ordering=). For the sake of consistency.
Oh and the ManyToManyField is a through table one.
These are my models.py:
class Recipe(models.Model):
name = models.CharField(max_length=255)
cook_time = models.FloatField()
ingredients = models.ManyToManyField(IngredientTag, through=Ingredient)
My serializers.py:
class IngredientTagSerializer(serializers.ModelSerializer):
class Meta:
model = IngredientTag
fields = ('id', 'label')
class IngredientSerializer(serializers.ModelSerializer):
class Meta:
model = Ingredient
fields = ('amount', 'unit', 'ingredient_tag')
depth = 1
class RecipeSerializer(serializers.ModelSerializer):
ingredients = IngredientSerializer(source='ingredient_set', many=True)
class Meta:
model = Recipe
fields = ('id', 'url', 'name', 'ingredients', 'cook_time')
read_only_fields = ('owner',)
depth = 2
And my views.py:
class RecipeViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows recipes to be viewed or edited.
"""
queryset = Recipe.objects.all().order_by()
serializer_class = RecipeSerializer
permission_classes = (DRYPermissions,)
ordering_fields = ('cook_time',) #Need ingredient count somewhere?
Thanks!
Try:
class RecipeSerializer(serializers.ModelSerializer):
ingredients = IngredientSerializer(source='ingredient_set', many=True)
ingredients_length = serializers.SerializerMethodField()
class Meta:
model = Recipe
fields = ('id', 'url', 'name', 'ingredients', 'cook_time')
read_only_fields = ('owner',)
depth = 2
def get_ingredients_length(self, obj):
return obj.ingredients.count()
Then order by ingredients_length
EDIT
In model.py, try this:
#property
def ingredient_length(self):
return self.ingredient_set.count()

Write operation in Reverse Relationship Django Rest Framework

I have models like this:
class Car(models.Model):
name = models.CharField(max_length=255)
class CarImage(models.Model):
car = models.ForeignKey(Car, related_name='photos')
photo = models.ImageField(upload_to='car/')
For the serializer I have:
class CarImageSerializer(serializer.ModelSerializer):
class Meta:
model = CarImage
class CarSerializer(serializer.ModelSerializer):
photos = CarImageSerializer()
class Meta:
model = Car
fields = ('id', 'name', 'photos',)
When the web interface for CarSerializer loads I get non_field_errors on the photos field by default. Is this kind of thing supported by DRF? If not what's the best way to do this?
P.S I am using generic CreateAPIView
Using docs you should do this from another way:
class CarSerializer(serializer.ModelSerializer):
photos = serializers.RelatedField(many=True)
class Meta:
model = Car
fields = ('id', 'name', 'photos',)
Maybe you can try this:
serializers.py
class CarImageSerializer(serializers.ModelSerializer):
class Meta:
model = CarImage
class CarSerializer(serializers.HyperlinkedModelSerializer):
photos = serializers.HyperlinkedRelatedField(many=True,
view_name='carimage-list')
class Meta:
model = Car
fields = ('id', 'name', 'photos',)
views.py
class CarImageList(ListCreateAPIView):
queryset = CarImage.objects.all()
serializer_class = CarImageSerializer
class CarList(ListCreateAPIView):
queryset = Car.objects.all()
serializer_class = CarSerializer
urls.py
url(r'^carimage/$', CarImageList.as_view(), name='carimage-list'),
url(r'^car/$', CarList.as_view(), name='car-list'),
You should take care about all needed imports. No guarantee, but you could give it a try.