I have a simple django application with the following models:
class Product(models.Model):
__metaclass__ = ABCMeta
title = models.CharField(max_length=50)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
slug = models.SlugField(max_length=100, unique=True)
price = models.IntegerField()
is_published = models.BooleanField(default=True)
#abstractmethod
def __str__(self):
pass
#abstractmethod
def get_absolute_url(self):
pass
class SupplyType(models.Model):
title = models.CharField(max_length=10)
slug = models.SlugField(max_length=100, unique=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('supply_type_detail', kwargs={'slug': self.slug})
class Processor(Product):
supply_type = models.ForeignKey(SupplyType, on_delete=models.CASCADE)
cores_amount = models.IntegerField()
threads_amount = models.IntegerField()
technological_process = models.IntegerField()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('processor_detail', kwargs={'slug': self.slug})
The corresponding serializers were written for them:
class SupplyTypeSerializer(ModelSerializer):
class Meta:
model = SupplyType
fields = '__all__'
class ProcessorSerializer(ModelSerializer):
class Meta:
model = Processor
fields = '__all__'
depth = 1
The corresponding views were also written (I will give only the views of creation for an example):
class ProcessorCreateAPIView(CreateAPIView):
model = Processor
serializer_class = ProcessorSerializer
class SupplyTypeCreateAPIView(CreateAPIView):
model = SupplyType
serializer_class = SupplyTypeSerializer
When I try to add "Supply Type" using POST request, it works successfully.
However, when I try to add a processor like this:
{
"title": "Intel Pentium Gold G6400",
"slug": "intel-pentium-gold-g6400",
"price": 19690,
"is_published" : true,
"cores_amount": 2,
"threads_amount": 4,
"technological_process": 14,
"supply_type": 1
}
I get an error:
django.db.utils.IntegrityError: null value in column "supply_type_id" of relation "store_processor" violates not-null constraint
DETAIL: Failing row contains (1, 2, 4, 14, null).
Ultimately, there are the following questions: how to fix this and how, in this case, to add a processor with the desired supply type through the API (still through the id) or in some other way?
As a result, when I do a GET request, I would like to get something like this:
{
"title": "Intel Pentium Gold G6400",
"slug": "intel-pentium-gold-g6400",
"price": 19690,
"is_published" : true,
"cores_amount": 2,
"threads_amount": 4,
"technological_process": 14,
"supply_type":
{
"id": 1,
"title": "OEM",
"slug": "oem"
}
And yeah, sorry for my english.
You need to use the concept of nested serializers. See below code
class ProcessorSerializer(ModelSerializer):
supply_type = SupplyTypeSerializer()
class Meta:
model = Processor
fields = '__all__'
As a result, when you do a GET request, you would get something like this:
{
"title": "Intel Pentium Gold G6400",
"slug": "intel-pentium-gold-g6400",
"price": 19690,
"is_published" : true,
"cores_amount": 2,
"threads_amount": 4,
"technological_process": 14,
"supply_type": {
"id": 1,
"title": "OEM",
"slug": "oem"
}
}
In order to create a Processor you will have to pass the supply_type dict object similar to what you get in the output. But since you want to pass the supply_type id instead, you could override the to_internal_value method as follows and set supply_type field as read_only:
def to_internal_value(self, data):
supply_type_id = data.get('supply_type')
internal_data = super().to_internal_value(data)
try:
supply_type = SupplyType.objects.get(id=supply_type_id)
except SupplyType.DoesNotExist:
raise serializers.ValidationError(
{'supply_type': ['Item does not exist']},
)
internal_data['supply_type'] = supply_type
return internal_data
Now you can create a Processor like this:
{
"title": "Intel Pentium Gold G6400",
"slug": "intel-pentium-gold-g6400",
"price": 19690,
"is_published" : true,
"cores_amount": 2,
"threads_amount": 4,
"technological_process": 14,
"supply_type": 1
}
The final code:
class ProcessorSerializer(serializers.ModelSerializer):
supply_type = SupplyTypeSerializer(read_only=True)
class Meta:
model = Processor
fields = '__all__'
def to_internal_value(self, data):
supply_type_id = data.get('supply_type')
internal_data = super().to_internal_value(data)
try:
supply_type = SupplyType.objects.get(id=supply_type_id)
except SupplyType.DoesNotExist:
raise serializers.ValidationError(
{'supply_type': ['Item does not exist']},
)
internal_data['supply_type'] = supply_type
return internal_data
Related
I have the following models:
class ContentUpload(BaseModel):
...
status = models.ForeignKey(CourseStatus, on_delete=models.CASCADE, related_name="content_status", null=True, blank = True)
class CourseStatus(BaseModel):
status_name = models.CharField(max_length=250)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.status_name)
super(CourseStatus, self).save(*args, **kwargs)
def __str__(self):
return str(self.status_name)
The following serializers:
class CourseStatusListSerializers(serializers.ModelSerializer):
class Meta:
model = CourseStatus
fields = ('id', 'status_name', 'slug')
def get_status(self, obj):
return CourseStatusListSerializers(obj.status, context={"request": self.context['request']}).data
When the ContentUpload.status is None it returns the following:
"status":{"status_name":"","slug":""}
My question is that how can I do it to give back an empty object? What is your best practice for this?
"status":{}
1- Rewrite your ContentUploadSerializer to the following:
class ContentUploadSerializer(serializers.ModelSerializer):
status = CourseStatusListSerializers(required=False)
class Meta:
model = ContentUpload
fields = ('id', 'status',)
2- Remove the get_status method.
The serialized ContentUpload object with the status field None will be as follows:
{
"id": 2,
"status": null
}
And with the status field not None it will be:
{
"id": 1,
"status": {
"id": 1,
"status_name": "Pending",
"slug": "pending"
}
}
You can read more about the nested serializers from this page.
https://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects
I am creating an API using Django Restframework which needs data from multiple models. I got many answers for my requirement but it isn't working.
I have my models as follows
class Task(models.Model):
title = models.CharField(max_length=200)
completed = models.BooleanField(default=False, blank=True, null=True)
def __str__(self):
return self.title
class Task_extended(models.Model):
task_id = models.ForeignKey(Task, on_delete = models.CASCADE,related_name='task_extendeds')
field_3 = models.CharField(max_length=200)
field_5 = models.CharField(max_length=200)
field_4 = models.BooleanField(default=False, blank=True, null=True)
def __str__(self):
return self.field_3
Here's my view function
#api_view(['GET','POST'])
def taskList(request):
tasks = Task.objects.all()
serializer = TaskSerializer(tasks, many =True)
return Response(serializer.data)
Serializer.py
class TaskSerializer(serializers.ModelSerializer):
task_extendeds = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Task
fields = "__all__"
depth = 1
I am getting the json as following
[
{
"id": 2,
"task_extendeds": [
1,
2,
3
],
"title": "Start Rest Framework",
"completed": false
}
]
What changes should I do to Serializers.py so that my json output is as following
[
{
"id": 2,
"title": "Start Rest Framework",
"completed": false,
"task_extendeds": [
{
"field_3": "Field 3 Data",
"field_4": "Field 4 Data",
"field_5": "Field 5 Data"
},
{
"field_3": "Field 3 Data",
"field_4": "Field 4 Data",
"field_5": "Field 5 Data"
},
{
"field_3": "Field 3 Data",
"field_4": "Field 4 Data",
"field_5": "Field 5 Data"
}
],
}
]
The depth = 1 attribute in meta class should have got the work done according to other stackoverflow questions, but it isn't working.
You use a subserializer, so:
class Task_extendedSerializer(serializers.ModelSerializer):
class Meta:
model = Task_extended
fields = ['field_3', 'field_4', 'field_5']
class TaskSerializer(serializers.ModelSerializer):
task_extendeds = Task_extendedSerializer(many=True)
class Meta:
model = Task
fields = '__all__'
In the view you can boost efficiency by prefetching the task_extendeds:
#api_view(['GET'])
def taskList(request):
tasks = Task.objects.prefetch_related('task_extendeds')
serializer = TaskSerializer(tasks, many=True)
return Response(serializer.data)
Note: Models in Django are written in PascalCase, not snake_case,
so you might want to rename the model from Task_extended to TaskExtended.
Write a Task_extendedSerializer first then use it in TaskSerializer
class Task_extendedSerializer(serializers.ModelSerializer):
class Meta:
model = Task_extended
fields = ('field_3', 'field_4', 'field_5')
class TaskSerializer(serializers.ModelSerializer):
task_extendeds = Task_extendedSerializer()
class Meta:
model = Task
fields = ('id', 'title', 'completed', 'task_extendeds')
Please help me improve my code.. im newly placed and learning things
i want to display separately product field in my code just like category and sub-category in my code but i don't know how to. Also please point out if there anything that i can improve in my code
Here is my output:
[
{
"Category": {
"c_id": 1,
"parent_id": 15
},
"Sub_category": {
"sc_id": 1,
"is_active2": 1
},
"p_id": 2,
"group_id": "",
"product_name": "Fruit",
"is_prescription_needed": 1,
}]
Whereas my expected format is:
[
{
"Category": {
"c_id": 1,
"parent_id": 15
},
"Sub_category": {
"sc_id": 1,
"is_active2": 1
},
"Product": { # How do i separate this "Product" key and value
"p_id": 2,
"group_id": "",
"product_name": "Fruit",
"is_prescription_needed": 1
}]
here is my code:
Serialziers.py
class ProductCategorySerializer(serializers.ModelSerializer):
c_id = serializers.SerializerMethodField()
category_image = serializers.SerializerMethodField()
class Meta:
fields = ["c_id","parent_id","category","category_image","is_active"]
model = Category
def get_c_id(self,obj):
return obj.id
def get_category_image(self,obj):
return obj.image
class ProductSubCategorySerializer(serializers.ModelSerializer):
sc_id = serializers.SerializerMethodField()
is_active2 = serializers.SerializerMethodField()
class Meta:
fields = ["sc_id","sub_category","is_active2"]
model = ProductSubCategory
def get_sc_id(self,obj):
return obj.id
def get_is_active2(self,obj):
return obj.is_active
class ProductSerializer(serializers.ModelSerializer):
Category = ProductCategorySerializer(source='category', read_only=True)
Sub_category = ProductSubCategorySerializer(source='sub_category', read_only=True)
product_image = serializers.SerializerMethodField()
p_id = serializers.SerializerMethodField()
class Meta:
fields = ["Category","Sub_category", "p_id", "group_id", "product_name", "is_prescription_needed",
"product_description", "product_keypoint", "product_type", "product_image", "pack", "pack_unit", "hsn",
"company", "brand_id", "composition", "drug_details", "uses",
"side_effects", "cope_side_effects", "how_it_works", "safety_advice", "what_if_forget",
"medical_constraints", "created_at", "created_by", "modified_at", "modified_by"]
model = Product
def get_product_image(self, obj: Product):
image = obj.image
if len(image) == 0:
return ""
return image
def get_p_id(self,obj):
return obj.id
View.py
class categoryProduct(generics.GenericAPIView):
def post(self, request):
params = request.query_params
category = params.get("category")
sub_category = params.get("sub_category")
kwargs = {}
if category:
kwargs['category'] = category
else:
kwargs['category'] = 1
if sub_category:
kwargs['sub_category'] = sub_category
else:
kwargs['sub_category'] = 1
queryset = Product.objects.filter(**kwargs)
all_response = ProductSerializer(queryset, many=True)
#AFTER PAGINATION
return Response(all_response)
models.py
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, db_column='category')
sub_category = models.ForeignKey(ProductSubCategory, on_delete=models.CASCADE, db_column='sub_category')
group_id = models.CharField(max_length=200)
product_name = models.CharField(max_length=500)
is_prescription_needed = models.IntegerField()
...
class Meta:
managed = False
db_table = 'product'
class Category(models.Model):
category = models.CharField(max_length=50)
image = models.CharField(max_length=500)
store_image = models.CharField(max_length=1000)
is_active = models.IntegerField()
parent_id = models.IntegerField()
class Meta:
managed = False
db_table = 'category'
class ProductSubCategory(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, db_column="category")
sub_category = models.CharField(max_length=250)
is_active = models.IntegerField()
class Meta:
managed = False
db_table = 'sub_category'
You can make a separate field that is a a SerializerField of a serializer you create with the proper fields you want displayed under the Product key. This is how your other field, like Category get appropriately created. That is to say: remove the fields from the ProductSerializer into a new serializer, then add a Product SerializerField with the new serializer containing those keys.
One other way you can do this is to create a .to_representation method to change the output to precisely the format you want.
class ProductSerializer(serializers.ModelSerializer):
...
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['Product'] = {
"p_id": representation.pop('p_id')
"group_id": representation.pop('group_id'),
"product_name": representation.pop('product_name'),
"is_prescription_needed": representation.pop('is_prescription_needed')
}
return representation
I'm trying to format data when querying my API. I can retrieve my data like that :
"results": [
{
"Cat1": [
{
"job": String,
"position": Integer
}
]
},
{
"Cat1": [
{
"job": String,
"position": Integer
}
]
},
{
"Cat2": [
{
"job": String,
"position": Integer
}
]
}
]
But I want something like that:
"results": [
{
"Cat1": [
{
"job": String,
"position": Integer
},
{
"job": String,
"position": Integer
}
]
},
{
"Cat2": [
{
"job": String,
"position": Integer
}
]
}
]
I use a serializer like this:
class CustomSerializer(serializers.ModelSerializer):
category = CatSerializer()
job = JobSerializer()
class Meta:
model = MyModel
fields = '__all__'
def to_representation(self, value):
return {
value.category.name: [{"job": value.job.name,
"position": value.position, }]
cat1 and cat2 are dynamics, they are from another table. I don't understand how to create my arrays properly using those serializers. The category is a #Property field in my model who's a foreign key of job.
My models:
class MyModel(models.Model):
CHOICES = [(i, i) for i in range(4)]
partner = models.ForeignKey(Partner, on_delete=models.CASCADE)
job = models.ForeignKey(
Job, on_delete=models.CASCADE)
position = models.IntegerField(choices=CHOICES)
#property
def category(self):
return self.job.category.domain
def __str__(self):
return '%s | %s | %s | position: %s' % (self.partner.name, self.domain.name, self.job.name, self.position)
class Job(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
code = models.CharField(
max_length=255, unique=True)
name = models.CharField(
max_length=255)
class Category(models.Model):
domain = models.ForeignKey(Domain, on_delete=models.CASCADE)
code = models.CharField(
max_length=5)
name = models.CharField(max_length=255)
hourly_rate = models.FloatField(
null=True, blank=True)
How should I deal with serializers to format my data properly ?
EDIT:
I ended with something like that, except for the ListSerializer.
I used 2 ModelSerilizers
class MyModelCustomSerializer(serializers.ModelSerializer):
position = serializers.IntegerField(read_only=True)
job = serializers.CharField(source='job.name', read_only=True)
class Meta:
model = MyModel
fields = ['job', 'position']
def to_representation(self, value):
return {"position": value.position,
"job": {"name": value.job.name, "slug": value.job.slug,
"title": value.job.seo_title}
}
And
class CategoryCustomSerializer(serializers.ModelSerializer):
models = MyModelustomerSerializer(many=True)
class Meta:
model = Category
fields = ['category', 'MyModel']
def to_representation(self, value):
filters = {'job__category__domain__name': value.name}
myModels = MyModel.objects.filter(**filters)
serializer = MyModelCustomSerializer(instance=myModels, many=True,)
return {value.name: serializer.data}
But if I try to use a jobSerializer who already exist instead of
"job": {"name": value.job.name, "slug": value.job.slug,
"title": value.job.seo_title}
},
I got this error: Object of type 'Job' is not JSON serializable, but it's working anyway because i don't need all fields
I would go the direction of implementing a custom ListSerializer for the ModelSerializer and overriding its to_representation method.
from rest_framework import serializers
from collections import OrderedDict
class CustomListSerializer(serializers.ListSerializer):
def to_representation(self, data):
iterable = data.all() if isinstance(data, models.Manager) else data
list_rep = OrderedDict()
for item in iterable:
child_rep = self.child.to_representation(item)
k, v = list(child_rep.items()).pop()
list_rep.setdefault(k, []).append(v)
return [
{k: v}
for k, v in list_rep.items()
]
Then set the model Meta to use it
class CustomSerializer(serializers.ModelSerializer):
category = CatSerializer()
job = JobSerializer()
class Meta:
model = MyModel
fields = '__all__'
list_serializer_class = CustomListSerializer
def to_representation(self, value):
return {
value.category.name: [{"job": value.job.name,
"position": value.position, }]
This my models
class Dictionary(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
parentId = models.UUIDField(editable=True, null=True)
name = models.CharField(max_length=100)
date_create = models.DateTimeField(auto_now=True)
date_end = models.DateTimeField(auto_now=False, null=True)
class Teacher(models.Model):
name = models.CharField(max_length=100)
message = models.CharField(max_length=300)
status = models.OneToOneField(Dictionary, on_delete=models.CASCADE)
this is my urls
from django.urls import path
from . import views
urlpatterns = [
path('get', views.GetViewSet.as_view({'get': 'list'})),
]
This is ViewSet
class GetViewSet(viewsets.ModelViewSet):
MyApiObj = null
#property
def api_object(self):
return namedtuple("ApiObject", self.request.data.keys())(*self.request.data.values())
def get_serializer_class(self):
GeneralSerializer.Meta.model = apps.get_model(app_label=self.MyApiObj.app, model_name=self.MyApiObj.object)
return GeneralSerializer
def post(self, request):
self.MyApiObj = self.api_object
return self.select_api()
def select_api(self):
queryset = QueryHelper.select(self.MyApiObj)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Serializer
class GeneralSerializer(serializers.ModelSerializer):
class Meta:
model = None
fields = '__all__'
My post parameters to django
{
"app":"leads",
"object":"Teacher",
"settings":{
},
"data":{
}
}
answer:
[
{
"id": 1,
"name": "John",
"message": "Hi everyone",
"status": "e3b86ed4-8794-413b-994c-b1ec0a43eebe"
}
]
Problem is Dictionary(status) model give me id(uuid) but i need whole object without creating new serializer for Dictionary. i do univeral serializer for all models in my app
Try this:
class DictionarySerializer(serializers.ModelSerializer):
class Meta:
model = Dictionary
fields = '__all__'
class GeneralSerializer(serializers.ModelSerializer):
status = DictionarySerializer(required=True)
class Meta:
model = None
fields = '__all__'
But it is not good for me because 1) Without other serializer 2) I need Universal serializer for all models and with child model in all models of my project. Help me please)
I need something like this
[
{
"id": 1,
"status": {
"id": "e3b86ed4-8794-413b-994c-b1ec0a43eebe",
"parentId": "dc6cf7da-b82c-11e9-a2a3-2a2ae2dbcce4",
"name": "Spravochnik1",
"date_create": "2019-08-06T09:30:49.355439Z",
"date_end": "2019-08-06T09:29:57Z"
},
"name": "John",
"message": "Hi everyone"
}
]
for nested serialization check full ref here
and for your case add depth = 1
class GeneralSerializer(serializers.ModelSerializer):
status = DictionarySerializer(required=True)
class Meta:
model = None
fields = '__all__'
depth = 1