I have the following Foreignkey relationship between two models:
class Text(models.Model):
textcontent = models.CharField(max_length=100)
class Comment(models.Model):
text = models.ForeignKey(ModelA,
on_delete=models.CASCADE,
null=True,
blank=True)
commentContent = models.CharField(max_length=100)
So, a text can have multiple comments, but a comment is assigned to only one text.
In serializers.py I have the following:
class TextSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Text
fields = ('url',
'id',
'comment_set'
)
As you see, I want to show also the set of comments belonging to one text via 'comment_set'.
But when I create a text instance (without providing comments) I get the following on the commandline :
"comment_set": [
"This field is required."
],
Why is it required ? I have set the blank & null arguments to True.
How I can solve this?
You can set read_only_fields--[DRF-Doc] in the meta class, as
class TextSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Text
fields = ('url', 'id', 'comment_set')
read_only_fields = ('comment_set',)
Related
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
I have three models, currently i am using an url like so to do updates and get content:
http://localhost:8000/manuscripts-api/manuscriptlibrary/28/
My question relates to the approach i should use so that i can include in my ManuscriptItem model the IDs of the related Libraries and Settings. What kind of fields can i add to the ManuscriptItem model that does this?
My models:
class ManuscriptItem(models.Model):
"""Represents a single manuscript's content"""
author = models.ForeignKey('accounts_api.UserProfile', on_delete=models.CASCADE)
title = models.CharField(max_length=255, blank=True)
content = models.CharField(max_length=99999999, blank=True)
def __str__(self):
"""Django uses when it needs to convert the object to a string"""
return str(self.id)
class ManuscriptLibrary(models.Model):
"""Represents a single manuscript's library"""
manuscript = models.OneToOneField(ManuscriptItem, on_delete=models.CASCADE)
bookmarks = models.CharField(max_length=99999999)
history = models.CharField(max_length=99999999)
def __str__(self):
"""Django uses when it needs to convert the object to a string"""
return str(self.manuscript)
class ManuscriptSettings(models.Model):
"""Represents a single manuscript's settings"""
manuscript = models.OneToOneField(ManuscriptItem, on_delete=models.CASCADE)
citation_suggestions = models.BooleanField(default=False)
terminology_suggestions = models.BooleanField(default=False)
paper_suggestions = models.BooleanField(default=False)
def __str__(self):
"""Django uses when it needs to convert the object to a string"""
return str(self.manuscript)
My serializers:
class ManuscriptItemSerializer(serializers.ModelSerializer):
"""A serializer for manuscript items."""
class Meta:
model = models.ManuscriptItem
fields = ('id', 'author', 'title', 'content')
extra_kwargs = {'author': {'read_only': True}}
class ManuscriptLibrarySerializer(serializers.ModelSerializer):
"""A serializer for a manuscript's library."""
class Meta:
model = models.ManuscriptLibrary
fields = ('id', 'manuscript', 'bookmarks', 'history')
class ManuscriptSettingsSerializer(serializers.ModelSerializer):
"""A serializer for a manuscript's settings."""
class Meta:
model = models.ManuscriptSettings
fields = ('id', 'manuscript', 'citation_suggestions', 'terminology_suggestions', 'paper_suggestions')
You don't necessarily need to add any new fields to the ManuscriptItem model. You can access the id of the related ManuscriptLibrary and ManuscriptSettings objects by defining the related_name property of the foreign key.
class ManuscriptLibrary(models.Model):
manuscript = models.OneToOneField(ManuscriptItem, on_delete=models.CASCADE, related_name='library')
class ManuscriptSettings(models.Model):
manuscript = models.OneToOneField(ManuscriptItem, on_delete=models.CASCADE, related_name='setting')
Once this is migrated, you can use manuscript_item.library to access the related library object, and manuscript_item.setting to access the related setting. Accessing ids can be done via manuscript_item.library.id.
Edit: To display the ids in the serialized object, you can modify your ManuscriptItemSerializer as given below
class ManuscriptItemSerializer(serializers.ModelSerializer):
library = ManuscriptLibrarySerializer(required=False)
setting = ManuscriptSettingsSerializer(required=False)
class Meta:
model = models.ManuscriptItem
fields = ('id', 'author', 'title', 'content', 'library', 'setting', )
by the docs one_to_one
your ManuscriptItem instance has two property manuscriptlibrary -- instance of the ManuscriptLibrary model and manuscriptsettings instance of the ManuscriptSettings model. So you can get the id by manuscriptlibrary.pk and manuscriptsettings.pk, but may be best solution for greater readability you can use related_name as arjun27 write.
My Model:
class Font(ValidateVersionOnSaveMixin, models.Model):
id = models.UUIDField(primary_key=True, editable=True)
name = models.CharField(max_length=100, blank=False, null=False)
class Glyph(ValidateVersionOnSaveMixin, models.Model):
id = models.UUIDField(primary_key=True, editable=True)
unit = models.CharField(max_length=100, blank=False, null=False, unique=True)
font = models.ForeignKey(Font, on_delete=models.CASCADE)
I want to post the following JSON to add a Glyph to an already existing Font (having the fontId as ID) object.
{
fontId: "4a14a055-3c8a-43ba-aab3-221b4244ac73"
id: "40da7a83-a204-4319-9a04-b0a544bf4440"
unit: "aaa"
}
As there is a mismatch between the ForeignKey Field font and the JSON propertyfontId I am adding source='font' in my Serializer:
class FontSerializer(serializers.ModelSerializer):
class Meta:
model = Font
fields = ('id', 'name')
class GlyphSerializer(serializers.ModelSerializer):
fontId = FontSerializer(source='font')
class Meta:
model = Glyph
fields = ('id', 'unit', 'fontId' )
But the result is an BAD REQUEST Error:
{"fontId":{"non_field_errors":["Invalid data. Expected a dictionary, but got str."]}}
Update
The following Serializer gave me the result I was looking for.
class GlyphSerializer(serializers.ModelSerializer):
fontId = serializers.PrimaryKeyRelatedField(
queryset=Font.objects.all(),
required=True,
source='font',
write_only=False
)
class Meta:
model = Glyph
fields = ('id', 'unit', 'version', 'fontId')
You can user model_to_dict method as bellow:
from django.forms.models import model_to_dict
model_to_dict(obj)
You have defined fontId as being a serialized object (FontSerializer). But that serializer in turn is defined as having both an id and a name. Where as your json dictionary is posting only an id. You would have to change that to a dictionary that contains both an id and a name
{
fontId: {id: "4a14a055-3c8a-43ba-aab3-221b4244ac73",name: "some name" },
id: "40da7a83-a204-4319-9a04-b0a544bf4440"
unit: "aaa"
}
The reason you are getting this error is that during deserialization process, DRF calls .is_valid(raise_exception=True) before you can call serializer.save(validated_data). And non_field_errors lists any general validation errors during this process. In your GlyphSerializer, your FontSerializer is a nested serializer, which correlates to a Python dictionary. So it will raise an error like you encountered for any non-dictionary data types.
You could create a subclass of GlyphSerializer called GlyphCreateSerializer
class FontSerializer(serializers.ModelSerializer):
class Meta:
model = Font
fields = ('id', 'name')
class GlyphSerializer(serializers.ModelSerializer):
fontId = FontSerializer(source='font')
class Meta:
model = Glyph
fields = ('id', 'unit', 'fontId' )
class GlyphCreateSerializer(GlyphSerializer):
fontId = serializers.CharField()
And you can use GlyphCreateSerializer for the POST request on your Viewset.
i'm trying to get nested object fields populated, however the only thing being returned is the primary key of each object (output below):
{
"name": "3037",
"description": "this is our first test product",
"components": [
1,
2,
3,
4
]
}
How do I have the component model's fields populated as well (and not just the PKs)? I would like to have the name and description included.
models.py
class Product(models.Model):
name = models.CharField('Bag name', max_length=64)
description = models.TextField ('Description of bag', max_length=512, blank=True)
urlKey = models.SlugField('URL Key', unique=True, max_length=64)
def __str__(self):
return self.name
class Component(models.Model):
name = models.CharField('Component name', max_length=64)
description = models.TextField('Component of product', max_length=512, blank=True)
fits = models.ForeignKey('Product', related_name='components')
def __str__(self):
return self.fits.name + "-" + self.name
serializers.py
from rest_framework import serializers
from app.models import Product, Component, Finish, Variant
class componentSerializer(serializers.ModelSerializer):
class Meta:
model = Component
fields = ('name', 'description', 'fits')
class productSerializer(serializers.ModelSerializer):
#components_that_fit = componentSerializer(many=True)
class Meta:
model = Product
fields = ('name', 'description', 'components')
#fields = ('name', 'description', 'components_that_fit' )
The documented approach doesn't seem to be working for me, and gives me the following error (you can see the lines following the standard approach commented out in the serializers.py entry above:
Got AttributeError when attempting to get a value for field 'components_that_fit' on serializer 'productSerializer'.
The serializer field might be named incorrectly and not match any attribute or key on the 'Product' instance.
Original exception text was: 'Product' object has no attribute 'components_that_fit'.
Update based on answer
Thanks to #Carlton's answer below, here's what is working for me:
serializers.py was changed and now looks like this:
class productSerializer(serializers.ModelSerializer):
components = componentSerializer(many=True)
class Meta:
model = Product
fields = ('name', 'description', 'components')
By calling the field components_that_fit, you're having the serialiser look for an attribute by that name. (There isn't one, hence your error.)
Two ways to fix it:
Call the field components, but declare it as components = componentSerializer(many=True)
Set source='components' field option when declaring the components_that_fit field.
The reason you get primary keys is that, unless declared explicitly, relations default to PrimaryKeyRelatedField.
I hope that helps.
I have the following models:
class Category(models.Model):
name = models.CharField()
... # fields omitted
class Prediction(models.Model):
conversation = models.ForeignKey(Conversation)
category = models.ForeignKey(Category)
... # fields omitted
class Conversation(models.Model):
sid = models.CharField()
... # fields omitted
Now I'm trying to create a model serializer for Category that would return me the following serialized object:
{
"name":"blah",
"conversations":[
"2af22188c5c97256", # This is the value of the sid field
"073aef6aad0883f8",
"5d3dc73fc8cf34be",
]
}
Here is what I have in my serializer:
class CategorySerializer(serializers.ModelSerializer):
conversations = serializers.SlugRelatedField(many=True,
read_only=True,
source="prediction_set",
slug_field='conversation.sid')
class Meta:
model = models.Class
fields = ('class_name', 'conversations')
However, this doesn't work because somehow django doesn't allow me to set slug_field to be a field that's within an object field. Any suggestions on how to accomplish this?
You are modelling a Many-to-Many relationship between Categorys and Conversations with a explicit table called Prediction. The django way of doing this would be to explicitly state the Many-to-Many on either side of the relationship and specify Prediction as the "through-model":
Shamelessly stolen example from this question:
class Category(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=255, blank=True,default=None)
desc = models.TextField(blank=True, null=True )
...
class Post(models.Model):
title = models.CharField(max_length=255)
pub_date = models.DateTimeField(editable=False,blank=True)
author = models.ForeignKey(User, null=True, blank=True)
categories = models.ManyToManyField(Category, blank=True, through='CatToPost')
...
class CatToPost(models.Model):
post = models.ForeignKey(Post)
category = models.ForeignKey(Category)
...
This shows a good way to set up the relationship.
Equally shamelessly stolen from the answer by #Amir Masnouri:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('name','slug')
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('id','{anything you want}','categories')
depth = 2
This shows a nice way of achieving the nested serialization behavior that you would like.