Django RestFramework - parent-child model serializer with DB views? - django

I am trying to implement a serializer that returns a parent record with its children embedded in the response json object.
My model for the parent and child are both based on database views:
class ProductContributorView(models.Model): # its a model of a view
id = models.IntegerField(primary_key=True)
product_id = models.ForeignKey('ProductTitleView', on_delete=models.DO_NOTHING, related_name='contributors')
sequenceNumber = models.IntegerField()
name = models.CharField(max_length=180)
role = models.CharField(max_length=8, null=True)
description = models.CharField(max_length=1408)
class Meta:
managed = False
ordering = ['sequenceNumber',]
class ProductTitleView(models.Model):
id = models.IntegerField(primary_key=True)
isbn = models.CharField(max_length=80)
titleText = models.CharField(max_length=300)
class Meta:
managed = False
ordering = ['titleText', 'isbn',]
Here are the serializers:
class ProductContributorViewSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ProductContributorView
fields = ('id', 'product_id', 'sequenceNumber', 'name', 'role', 'description')
def create(self, validated_data):
contributor = ProductContributorView.objects.create(
id=validated_data['id'],
product_id=validated_data['product_id'],
sequenceNumber=validated_data['sequenceNumber'],
name=validated_data['name'],
role=validated_data['role'],
description=validated_data['description'])
return contributor
class ProductTitleViewSerializer(serializers.HyperlinkedModelSerializer):
contributors = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = ProductTitleView
fields = ('id', 'isbn', 'titleText', 'contributors')
Here are the views:
class ProductTitleViewList(generics.ListAPIView):
queryset = ProductTitleView.objects.all()
serializer_class = ProductTitleViewSerializer
class ProductContributorViewList(generics.ListAPIView):
queryset = ProductContributorView.objects.all()
serializer_class = ProductContributorViewSerializer
The basic idea is to have the contributors - author, illustrator, etc - returned with the book title based on the FK in the ProductContributorView view matching the id in the ProductTitleView.
When I run this, however, I get the following error:
1054, "Unknown column 'jester_productcontributorview.product_id_id' in 'field list'"
I didn't specify product_id_id in the field list, and I've also tried referring to the field as just product in the field list, but it still repeats the _id_id suffix. Hoping someone will point me to documentation where the FK naming conventions are explained or tell me what to change in the field list. Thanks!

You may just want to try renaming that product_id ForeignKey to just product.
This hints to why it may be broken, I suspect it's breaking somewhere in the serializers inspection of your models regarding the naming of the product_id field on the model.
When you define a ForeignKey on a model there are two properties available for that field. One is the property you define, the ForeignKey object, and you should use this to get the related model. Behind the scenes Django also creates another property which appends _id to the the foreign key's name, this property represents the IntegerField on the database which stores the relation. If you were to view the table in psql you will see the _id columns (and in your case, _id_id).

Related

Need to get the Foreign key value instead of ID in the database table using Django

model
class Project(models.Model):
project_name = models.CharField(max_length=20)
client= models.ForeignKey(Client,on_delete=CASCADE,related_name="Client1",default=None)
user=models.ManyToManyField(Default_User,related_name='users',default=None)
description=models.TextField()
type=models.TextField() #dropdown
start_date = models.DateTimeField(max_length=10)
end_date=models.DateTimeField(max_length=10)
technical_contact_name = models.CharField(max_length=30)
email=models.EmailField(max_length=254,default=None)
phone = PhoneField(blank=True)
delivery_head_contact_name=models.CharField(max_length=30)
class Meta:
db_table ='Project'
def __str__(self):
return self.project_name
model
class Job(models.Model):
job_name=models.CharField(max_length=50)
user= models.ForeignKey(Default_User,on_delete=CASCADE)
project = ChainedForeignKey(Project,chained_field="user", chained_model_field="user",related_name='projects',show_all=False, auto_choose=True, sort=True)
date = models.DateField(max_length=10,default=None)
class Meta:
db_table ='Job'
def __str__(self):
return '{}'.format(self.job_name)
serializers
class ProjectSerializers(serializers.ModelSerializer):
class Meta:
model= Project
fields= '__all__'
class Job_Serializers(serializers.ModelSerializer):
job = serializers.StringRelatedField()
class Meta:
model= Job
fields= ('id','job_name','user','project','date','job',)
I need to get the foreign key value displayed in the database table of Job model but as per the above code it is displaying the Foreign key ID. For example I linked the project model in the Job model and in the db table it is showing the Project_id as(1,2,3) but i need to return the values of that id as(app, learning etc). Please help me to get the values of the foreign key value instead of ID in the database table.
The database will by default take the unique field from the model and django provide id as unique key for models. It is for data consistency. So you can let that happen and in job serializera use SerializerMethodField to retrieve the value of project name based on instance of job objects.
Depends on what you want to achieve with it. If it is just to return another field value from project, then you can add it to the serializer as in below example. I am returning project_name as well.
class JobSerializers(serializers.ModelSerializer):
job = serializers.StringRelatedField()
project_name = serializers.SerializerMethodField()
class Meta:
model= Job
fields= ('id','job_name','user','project','date','job', 'project_name')
def get_project_name(self, job):
return job.project.project_name
If you want to return the whole project object then you have to include
project = ProjectSerializers()

When don't we use "__all__" in ModelSerializer in Django Rest Framework

This is just my curiosity but I will be very happy if anyone answers my question.
I am using Django Rest Framework but I'm a beginner. In serializers.py, I use ModelSerializer and "all" to fields attribute.
This is an example.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
And then, I just thought
when don't we use "__all__" in serializers.py??
As long as we create models.py in advance, I think we usually use all fields in each Model.
I would like you to teach me when we omit specific fields that come from each Model.
Thank you.
So the second question is a bit harder to explain in a comment:
If we use some fields of all fields in Model, how do we store information of the rest of fields?
Various cases:
Fields with defaults:
class Log(models.Model):
message = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
class LogSerializer(serializers.ModelSerializer):
class Meta:
model = Log
fields = ('message',)
For autogenerated, think user profile models via the post_save signal or calculated fields:
class OrderLine(models.Model):
order = models.ForeignKey(Order)
name = models.CharField(max_length=200)
quantity = models.IntegerField()
price = models.DecimalField()
class OrderLineSerializer(serializers.ModelSerializer):
order = serializers.PrimaryKeyRelatedField()
product = serializers.IntegerField()
class Meta:
model = OrderLine
fields = ('quantity', 'product', 'order')
In this case, the product is a primary key for a product. The serializer will have a save method that looks up the product and put it's name and price on the OrderLine. This is standard practice as you cannot reference a product in your orders, else your orders would change if you change (the price of) your product.
And derived from request:
class BlogPost(models.Model):
author = models.ForeignKey(User)
post = models.TextField()
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
fields = ('post',)
def create(self, validated_data):
instance = BlogPost(**validated_data)
instance.author = self.context['request'].user
instance.save()
return instance
This is pretty much the common cases.
There are many cases, but I think the two main ones are:
When you don't want all fields to be returned by the serializer.
When you need some method of the serializer to know its fields. In such case, you should traverse fields array, but it doesn't work if you use __all__, only if you have an actual list of fields.

Serialize many-to-many relation with intermediate model in Django Rest

I tried to check another topics, but didn't found a solution...
I have a many-to-many model, that have intermediate model with another field additional_field inside.
class BoardField(models.Model):
title = models.CharField(max_length=500, default='')
class Article(models.Model):
title = models.CharField(max_length=500, default='')
fields = models.ManyToManyField(BoardField, through='ArticleField', through_fields=('article', 'board_field'))
class ArticleField(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='task')
board_field = models.ForeignKey(BoardField, on_delete=models.CASCADE)
additional_field = models.CharField(max_length=200, blank=True, null=True)
I want serialize Article with structure:
[
"title":"Title",
"fields":[
{
"board_field": {
"title":"Title"
},
"additional_field":"Additional info"
}
]
]
So, I wrote serializer:
class BoardFieldSrl(serializers.ModelSerializer):
class Meta:
model = BoardField
fields = (
'title',
)
class ArticleFieldSrl(serializers.ModelSerializer):
board_field = BoardFieldSrl()
class Meta:
model = ArticleField
fields = (
'board_field',
'additional_field',
)
class ArticleListSrl(serializers.ModelSerializer):
fields = ArticleFieldSrl(many=True)
class Meta:
model = Article
fields = (
'title',
'fields',
)
But I always got an error:
Got AttributeError when attempting to get a value for field `board_field` on serializer `ArticleFieldSrl`.
The serializer field might be named incorrectly and not match any attribute or key on the `BoardField` instance.
Original exception text was: 'BoardField' object has no attribute 'board_field'.
I made another several examples, but they doesn't gave my result, that I need... My maximum - I got BoardField with levels, but without intermediate model...
Can you help me with serializer, that return structure, that I mentioned above? It must include intermediate model ArticleField and nested BoardField.
Try fields = ArticleFieldSrl(source='articlefield_set', many=True)
You didn't specified a related_name at M2M field so the default naming is applied which is 'Intermediate model name'_set and if you want to use the fields on M2M relation you have to tell the serializer where to look for.
EDIT:
Camel removed from articlefield_set, model name is always converted to lower case

Django Rest Framwork - Select only some fields in reverse lookup queryset

I'm trying to retrieve only some of the fields in the "Appointments" associated to a rental property "Unit". From the UnitSerializer, I call a SerializerMethodField() to do a reverse lookup for the "appointment" field. This works out well. However, the queryset returns all the fields (id, time, unit, staff, prospect) in each object, when I only need a few (id, time).
I tried .values() on the queryset like so:
queryset = instance.appointment_set.values('id', 'appointment_time')
But I get "Got KeyError when attempting to get a value for field unit on serializer AppointmentSerializer.\nThe serializer field might be named incorrectly and not match any attribute or key on the dict instance.\nOriginal exception text was: unit."
Note sure if you need all the code, but here's the essential.
Models
class Appointment(models.Model):
appointment_time = models.DateTimeField()
unit = models.ForeignKey(Unit, on_delete=models.CASCADE)
staff = models.ForeignKey(Staff, on_delete=models.CASCADE)
prospect = models.ForeignKey(Prospect, on_delete=models.CASCADE)
Serializers
class AppointmentSerializer(serializers.ModelSerializer):
class Meta:
model = Appointment
fields = ['id','appointment_time']
class UnitSerializer(serializers.ModelSerializer):
appointment = SerializerMethodField()
class Meta:
model = Unit
fields = ['id', 'address', 'appointment']
def get_appointment(self, instance):
cutoff = _datetime.date.today() + timedelta(hours=72)
queryset = instance.appointment_set.exclude(appointment_time__gt=cutoff)
return AppointmentSerializer(queryset, many=True).data
There is a better way to handle reverse relationship in serializer:
class UnitSerializer(ModelSerializer):
appointment = AppointmentSerializer(many=True, source='appointment_set')
class Meta:
model = Unit
fields = ['id', 'address', 'appointment']

Add Serializer on Reverse Relationship - Django Rest Framework

I have a Cart model and a CartItem model. The CartItem model has a ForeignKey to the Cart model.
Using Django Rest Framework I have a view where the API user can display the Cart, and obviously then I want to include the CartItem in the respone.
I set up my Serializer like this:
class CartSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
cartitem_set = CartItemSerializer(read_only=True)
class Meta:
model = Cart
depth = 1
fields = (
'id',
'user',
'date_created',
'voucher',
'carrier',
'currency',
'cartitem_set',
)
My problem is the second line, cartitem_set = CartItemSerializer(read_only=True).
I get AttributeErrors saying 'RelatedManager' object has no attribute 'product'. ('product' is a field in the CartItem model. If I exclude product from the CartItemSerializer I just get a new AttributeError with the next field and so on. No matter if I only leave 1 or all fields in the Serializer, I will get a error.
My guess is that for some reason Django REST Framework does not support adding Serializers to reverse relationships like this. Am I wrong? How should I do this?
PS
The reason why I want to use the CartItemSerializer() is because I want to have control of what is displayed in the response.
Ahmed Hosny was correct in his answer. It required the many parameter to be set to True to work.
So final version of the CartSerializer looked like this:
class CartSerializer(serializers.ModelSerializer):
cartitem_set = CartItemSerializer(read_only=True, many=True) # many=True is required
class Meta:
model = Cart
depth = 1
fields = (
'id',
'date_created',
'voucher',
'carrier',
'currency',
'cartitem_set',
)
It's important to define a related name in your models, and to use that related name in the serializer relationship:
class Cart(models.Model):
name = models.CharField(max_length=500)
class CartItem(models.Model):
cart = models.ForeignKey(Cart, related_name='cart_items')
items = models.IntegerField()
Then in your serializer definition you use those exact names:
class CartSerializer(serializers.ModelSerializer):
cart_items = CartItemSerializer(read_only=True)
class Meta:
model = Cart
fields = ('name', 'cart_items',)
It would be wise to share your whole code, that is model and serializers classes. However, perhaps this can help debug your error,
My serializer classes
class CartItemSerializer(serializers.ModelSerializer):
class Meta:
model = CartItem
fields = ('id')
class CartSerializer(serializers.ModelSerializer):
#take note of the spelling of the defined var
_cartItems = CartItemSerializer()
class Meta:
model = Cart
fields = ('id','_cartItems')
Now for the Models
class CartItem(models.Model):
_cartItems = models.ForeignKey(Subject, on_delete=models.PROTECT)
#Protect Forbids the deletion of the referenced object. To delete it you will have to delete all objects that reference it manually. SQL equivalent: RESTRICT.
class Meta:
ordering = ('id',)
class Cart(models.Model):
class Meta:
ordering = ('id',)
For a detailed overview of relationships in django-rest-framework, please refer their official documentation