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.
Related
I'm not really sure how I'm able to access the data of a nested serializer, with a one-to-many relation.
Here's my models:
class Album(models.Model):
id = models.CharField(max_length=255, null=True, blank=True)
name = models.CharField(max_length=255, null=True, blank=True)
class Title(models.Model):
name = models.CharField(max_length=255, null=True, blank=True)
album = models.ForeignKey(
Album,
related_name='titles'
)
then I have 2 serializers:
class AlbumSerializer(serializers.ModelSerializer):
titles = TitleSerializer(many=True)
class Meta:
model = Album
fields = ['id', 'name', 'titles']
def create(self, validated_data):
album = Album.objects.create(
id=validated_data.get('id'),
name=validated_data.get('name')
)
titles = validated_data.pop('titles')
for title in titles:
title['album'] = album
_title = Title(**title)
_title.save()
return album
class TitleSerializer(serializers.ModelSerializer):
class Meta:
model = Title
fields = ['name']
To deserialize and save I run the following:
album = AlbumSerializer(data=input_json)
album.is_valid()
album.save()
my problem is now, that I'm unable to access the items. Accessing the type of album.instance.titles gets me <class 'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager'>.
How can I get the titles out of it, or what am I doing wrong that I do not get a list of Titles in there?
You need to call all() on a RelatedManager to execute the DB query and get the results
album.instance.titles.all()
A RelatedManager is just like a normal model manager (Model.objects) that retrieves objects filtered by the relation
I have a model that looks like this:
class UssdCode(models.Model):
title = models.CharField(max_length=100)
code = models.CharField(max_length=100)
product = models.CharField(max_length=100)
How can I get the admin to alert me and reject my entry when I try to add a new object that has the same 'code' and 'product' as an object already in the database.
You make it unique together. Since django-2.2, you can use the UniqueConstraint [Django-doc] of the Django Constraint framework [Django-doc] for that:
# since Django-2.2
class UssdCode(models.Model):
title = models.CharField(max_length=100)
code = models.CharField(max_length=100)
product = models.CharField(max_length=100)
class Meta:
constraints = [
models.UniqueConstraint(fields=['code', 'product'], name='code_product')
]
Prior to django-2.2, you can use the unique_together meta option [Django-doc]:
# before Django-2.2 (still works on Django-3.0)
class UssdCode(models.Model):
title = models.CharField(max_length=100)
code = models.CharField(max_length=100)
product = models.CharField(max_length=100)
class Meta:
unique_together = [['code', 'product']]
models.py
class Product(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
price = models.DecimalField(decimal_places=5,max_digits= 1500)
summary = models.TextField()
featured = models.BooleanField()
def __str__(self):
return self.title
# return f'product title:{self.title}-product price:{self.price}'workok
class Meta:
ordering = ('-price',)
class Opinion(models.Model):
name = models.CharField(max_length=20)
email = models.EmailField(max_length=20)
body = models.TextField()
opinion_date = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='opinion_set')
def __str__(self):
return f'({self.name}) add opinion about ({self.product})'
forms.py:
from django.forms import ModelForm
from .models import Product #space after from keyword
class OpinionModelForm(ModelForm):
class Meta:
model = Product
fields = ['name','email','body','product']
invalid in code line :
fields = ['name','email','body','product'] #---- NOT WORK !!!
, but if i change above code to :
fields = "__all__" # ----it is WORKing ok without any problem !!
question : what is the error? I am not need all the fields in the Product model (like active boolean field), I need only 'name','email','body','product' fields .
According to the error and the code you provided the main problem is that you made a mistake in chosing model in serializer:
class OpinionModelForm(ModelForm):
class Meta:
model = Product
fields = ['name','email','body','product']
Serializer name is OpinionModelForm and listed fields belong to Opinion so I guess you actually wanted to serialize Opinion and no Product as you defined at this line:
model = Product
Simply change it to:
model = Opinion
Looking for solution of this problem I encountered some similar threads, but referring to older versions of Django/DRF and thus not working in my case.
There are these two models:
class CsdModel(models.Model):
model_id = models.CharField("Item ID", max_length=8, primary_key=True)
name = models.CharField("Item Name", max_length=40)
active = models.BooleanField(default=True)
def __str__(self):
return self.model_id
class CsdListing(models.Model):
model_id = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_id')
name = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_name')
(...)
EDIT: Serializers are defined this way:
class CsdModelSerializer(serializers.ModelSerializer):
model_id = serializers.RegexField(regex='^\w{2}\d{3}$', allow_blank=False)
name = serializers.CharField(min_length=6, max_length=50, allow_blank=False)
class Meta:
model = CsdModel
fields = '__all__'
class CsdListingSerializer(serializers.ModelSerializer):
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
def validate_session_id(self, value):
(...)
class Meta:
model = CsdListing
fields = '__all__'
What I'd like to see, is model_id and name from CsdModel displayed inside a form created based on CsdListing model. But instead, the ID is duplicated:
How should I rebuild the model(s) to have both ID and name displayed in the form?
You should have only one foreign key. But the listing serializer should then reference the model as a nested serializer.
class CsdListing(models.Model):
model = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='listing')
class CsdListingSerializer(serializers.ModelSerializer):
model = CsdModelSerializer()
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
I have the following three models structured around the premise of the Survey.
class Survey(models.Model):
...
id = models.UUIDField(_('Id'), primary_key=True, default=uuid.uuid4, editable=False,)
name = models.CharField(_('Name'), max_length=120, blank=True, unique=True)
slug = models.SlugField(_('Slug'), max_length=120, blank=True, unique=True)
description = models.TextField(_('Description'), blank=True)
...
Each Survey can have multiple questions SurveyQuestion:
class SurveyQuestion(models.Model):
...
survey = models.ForeignKey('surveys.Survey', on_delete=models.CASCADE, null=True, blank=True,)
And each SurveyQuestion can have multiple answers SurveyQuestionAnswer:
class SurveyQuestionAnswer(models.Model):
...
survey_question = models.ForeignKey('surveys.SurveyQuestion', on_delete=models.CASCADE, null=True, blank=True,)
For the sake of brevity, imagine my Survey serializers as being as simple as possible:
class SurveySerialializer(serializers.ModelSerializer):
class Meta:
model = Survey
fields = ('__all__')
Effectively, what I have is the following:
class Survey(APIView):
"""
Survey GET request endpoint: fetches Survey
"""
permission_classes = User
def get(self, request, survey_slug):
survey = Survey.objects.get(slug=survey_slug)
serializer = SurveySerializer(survey)
response = get_hug_response(message='Organisation Active Survey Fetched Successfully', data=serializer.data)
return Response(data=response, status=status.HTTP_200_OK)
But, as you could all probably tell, the corresponding surveys.get('slug') fetch only returns the fields in the Survey model. Ideally, I would like to have some sort of fetch for each SurveyQuestion, and within that nested the SurveyQuestionAnswers
Any pro-tips and pointers would be most appreciated.
I have tried a few things, that only throw errors. I'm struggling to know what this type of API relationship is called in DRF so I can't find appropriate example guides to base the same principles from...
Relevant versions:
Django==2.2.1
djangorestframework==3.9.3
Create two serializers, SurveyQuestionAnswerSerializer and SurveyQuestionSerializer
class SurveyQuestionAnswerSerializer(serializers.ModelSerializer):
class Meta:
model = SurveyQuestionAnswer
fields = '__all__'
class SurveyQuestionSerializer(serializers.ModelSerializer):
survey_questionanswers = SurveyQuestionAnswerSerializer(many=True, read_only=True, source="surveyquestionanswer_set")
class Meta:
model = SurveyQuestion
fields = '__all__'
class SurveySerializer(serializers.ModelSerializer):
survey_questions = SurveyQuestionSerializer(many=True, read_only=True, source="surveyquestion_set")
class Meta:
model = Survey
fields = '__all__'
For more info,
1. What is related_name used for in Django?
2. DRF Serializer's source argument