How to set SlugRelated field to a field within an object field - django

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

How to deserialize nested django object

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

Django Check for duplicates with 2 model fields

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']]

fields in class Meta got invalid

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

How to display many fields' values with ForeignKey relationship?

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)

Django REST framework - reverse ForeignKey relations

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