Django rest framework reverse relation serializers exclude fields - serialization

I have two models Question and Options
class Question(models.Model):
question_identifier = models.CharField(max_length=255)
question_text = models.TextField(blank=False)
question_category = models.ManyToManyField('Category')
question_tags = models.CharField(max_length=255, blank=True)
class Options(models.Model):
question = models.OneToOneField('Question', related_name='options', blank=False, null=False)
option1 = models.CharField(max_length=255,blank=False,null=True)
option2 = models.CharField(max_length=255,blank=False,null=True)
option3 = models.CharField(max_length=255,blank=True,null=True)
option4 = models.CharField(max_length=255,blank=True,null=True)
I have written a serializer for Question model which serializes options as well (reverse relation). I want to omit the question field from the Options? Is there a way to achieve this?
My Question Serializer:-
class QuestionSerializer(serializers.ModelSerializer):
"""
Serializer for Question object from qna.models
"""
question_category = CategorySerializer(many=True,write_only=True)
class Meta:
model = Question
fields = ('id', 'options', 'question_identifier','question_text','question_tags','question_category')
depth = 1

You can achieve that by creating a separate serializer for an Options model.And use it instead of default field, without depth value.
class OptionsSerializer(serializers.ModelSerializer):
class Meta:
model = Options
fields = ('option1', 'option2', 'option3')
class QuestionSerializer(serializers.ModelSerializer):
question_category = CategorySerializer(many=True,write_only=True)
options = OptionsSerializer()
class Meta:
model = Question
fields = ('id', 'options', 'question_identifier','question_text','question_tags','question_category')

Related

Cannot resolve keyword into field error while using drf model serializer and search_fields within field set?

SerializerClass:
class VacancySerializer(serializers.ModelSerializer):
organization_small_name = serializers.CharField(source='organization.short_name', read_only=True)
class Meta:
model = Vacancy
fields = ['organization', 'description', 'specs', 'type', 'publication_date',
'is_published', 'withdrawal_data', 'organization_small_name', ]
read_only_fields = ['publication_date', 'is_published', 'withdrawal_data',]
ViewSet:
class VacancyViewSet(viewsets.ModelViewSet):
queryset = Vacancy.objects.all()
serializer_class = VacancySerializer
filter_backends = [filters.SearchFilter]
search_fields = ['organization_small_name']
...
Model:
class Vacancy(models.Model):
organization = models.OneToOneField(DictOrganization, on_delete=models.CASCADE, related_name='vacancies')
description = models.TextField('Описание')
specs = models.ManyToManyField(DictSpec, blank=True)
type = models.CharField('Тип', max_length=20, choices=VacancyType.choices(), default=VacancyType.PRACTICE.value)
publication_date = models.DateField('Дата публикации', null=True, blank=True)
is_published = models.BooleanField('Опубликовано', default=False)
withdrawal_data = models.DateField('Дата снятия с публикации', null=True, blank=True)
My goal is to make API search by 'organization_small_name' field
that is in VacancySerializer.
Server runs successfully, but as soon as i add ?search parameter, i get next error:
Why it doesn't recognize 'organization_small_name' field, even thought it is decribed in serializer, and how can i fix it?

How to do Django Rest Framework double nested serializer

I'm trying to build an api that have an endpoint which in the POST receives the JSON like bellow:
{
"title":"Quiz 1",
"questions":[
{
"description":"Question 1?",
"answers":[
{
"description":"Answer 1",
"true_or_false":true
},
{
"description":"Answer 2",
"true_or_false":false
}
]
},
{
"description":"Question 2?",
"answers":[
{
"description":"Answer 1",
"true_or_false":true
},
{
"description":"Answer 2",
"true_or_false":false
}
]
}
]
}
But I don't know how to build the serializers for double nested fields, like a list of answers within a question and a list of questions within a quiz. The code I made so far is below:
models.py
from django.db import models
class Quiz(models.Model):
title = models.CharField(max_length=200, blank=False)
def __str__(self):
return self.title
class Question(models.Model):
description = models.CharField(max_length=10000, blank=False)
quiz = models.ForeignKey(Quiz, related_name='questions', on_delete=models.CASCADE, default=None)
def __str__(self):
return self.description
class Answer(models.Model):
question = models.ForeignKey(Question, related_name='answers', on_delete=models.CASCADE)
description = models.CharField(max_length=1000, blank=False)
true_or_false = models.BooleanField(default=False, blank=False)
def __str__(self):
return self.description
views.py
class QuestionViewSet(viewsets.ModelViewSet):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
class AnswerViewSet(viewsets.ModelViewSet):
queryset = Answer.objects.all()
serializer_class = AnswerSerializer
class QuizViewSet(viewsets.ModelViewSet):
queryset = Quiz.objects.all()
serializer_class = QuizSerializer
class OnlyQuizViewSet(generics.ListAPIView):
def get_queryset(self):
queryset = Quiz.objects.filter(id=self.kwargs['pk'])
return queryset
serializer_class = QuizSerializer
serializers.py
class AnswerSerializer(serializers.ModelSerializer):
class Meta:
model = Answer
fields = ['id', 'description', 'true_or_false']
class QuestionSerializer(serializers.ModelSerializer):
answers = AnswerSerializer(many=True)
class Meta:
model = Question
fields = ['id','description','answers']
read_only_fields = ('quiz',)
def create(self, validated_data):
answers_data = validated_data.pop('answers')
question = Question.objects.create(**validated_data)
for answer in answers_data:
Answer.objects.create(question=question, **answer)
return question
class QuizSerializer(serializers.ModelSerializer):
questions = QuestionSerializer(many=True)
class Meta:
model = Quiz
fields = ['id', 'title', 'questions']
def create(self, validated_data):
questions_data = validated_data.pop('questions')
quiz = Quiz.objects.create(**validated_data)
for questions in questions_data:
Question.objects.create(quiz=quiz, **questions)
return quiz
With the above serializers, I'm getting the error below:
Direct assignment to the reverse side of a related set is prohibited. Use answers.set() instead.
So what's the right way to build double-nested serializers? So far I haven't been able to find anything to help me
I'm gonna assume you get that error when you try posting to the Quiz api, you'll have a problem when you want to create a question
Question.objects.create(quiz=quiz, **questions)
since this question also includes a list of answers that needs to be created first.
Try using the serializer you already have like this
class QuizSerializer(serializers.ModelSerializer):
questions = QuestionSerializer(many=True)
class Meta:
model = Quiz
fields = ['id', 'title', 'questions']
def create(self, validated_data):
questions_data = validated_data.pop('questions')
quiz = Quiz.objects.create(**validated_data)
for questions in questions_data:
question_serializer = QuestionSerializer({**questions, "quiz": quiz}) # might need to send quiz.id instead
question_serializer.is_valid()
question_serializer.save()
return quiz
and update your QuestionSerializer to use AnswerSerializer the same way (or you can create Answers explicitly there)

How to add a custom field in Flask-Marshmallow which is not a part of my SQLAlchemy Model

Suppose I have my Model class as below.
class BankAccount(db.Model):
a = db.Column(db.Integer, primary_key=True)
b = db.Column(db.String(80))
And my Schema looks like the below.
class CreateAccountSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = BankAccount
load_instance = True
ordered = True
But here I want to define a field c which doesnt exists in my Model class as it should only be used for dump(ing) values and not storing in the database.
Something like...
class CreateAccountSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = BankAccount
dump_only = ('c',)
load_instance = True
ordered = True
c = #somedefination
You could do something like this:
class CreateAccountSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = BankAccount
load_instance = True
ordered = True
c = fields.Method("get_data")
def get_data(self, obj):
if hasattr(obj, "data"):
return obj.data
return None
That would dump the additional field.
You could use it like this:
bank_account = db.session.query(BankAccount).first()
bank_account.data = "whatever"
schema = CreateAccountSchema()
schema.dump(bank_account)
More infos here.

How to set a fixed value of particular column to user in POST API in Django?

I am using DRF for creating API, using basic APIView. Below are the models and views.
models.py
from django.db import models
from django.utils import timezone
class PizzaOrder(models.Model):
name = models.CharField(max_length=120)
size = models.CharField(default='MEDIUM')
customer_name = models.CharField(max_length=120)
customer_address = models.TextField()
ordered_time = models.DateTimeField(default=timezone.now)
Now, I need two things in that.
First - I want to set STATUS as 'open' in the database while ordering pizza. User isn't able to see the STATUS column while saving.
Second - I want to get all orders, but it should show STATUS to the user now.
view.py
class PizzaOrderView(APIView):
def get(self, request):
orders = PizzaOrder.objects.all()
serializer = ArticleSerializer(orders, many=True)
return Response({"orders": serializer.data})
def post(self, request):
orders = request.data.get('orders')
serializer = ArticleSerializer(data=orders)
if serializer.is_valid(raise_exception=True):
article_saved = serializer.save()
What should I do to get my all two requirements?
First of all if you want to save status in the database you must include it as one of the fields in your model. I would also suggest adding the choices enumeration so that nothing unwanted can end up in database as a status (note that you can add any other choice besides open and closed):
class PizzaOrder(models.Model):
...
OPEN = 'open'
CLOSED = 'closed'
TYPE_CHOICES = (
(OPEN, 'Open'),
(CLOSED, 'Closed'),
)
status = models.CharField(max_length=120, choices=STATUS_CHOICES, default=OPEN)
Next thing to know is that the way data is presented to the user depends mostly on your serializers (would be useful if you could include those in your question aswell). So for serializing the PizzaOrder data you can use two different serializers (one which includes the status field, and one which does not):
class PizzaOrderSerializer(serializers.ModelSerializer):
class Meta:
model = PizzaOrder
fields = '__all__'
class PizzaOrderWithoutStatusSerializer(serializers.ModelSerializer):
class Meta:
model = PizzaOrder
fields = ['name', 'size', 'customer_name', 'customer_address', 'ordered_time']
you can user one serializer just just using the one serializer
class PizzaOrderSerializer(serializers.ModelSerializer):
class Meta:
model = PizzaOrder
fields = '__all__'
read_only_fields = ('status',)

Issue with complex Django join query

I can't find a valid way with Django OMR in order to get : ( a raw query is also fine )
the Sites.sitename which made the Analysis where (Analysi_Items.name='somename' and Analysis_Items.value='somevalue') and (Analysi_items_name='somename' and Analysis_Items.value='somevalue') and (Analysis_items.name='somename' and Analysis_Items.value='somevalue').
class Sites(models.Model):
region = models.CharField(max_length=1000)
province = models.CharField(max_length=1000)
sitename = models.CharField(max_length=1000, primary_key=True)
class Meta:
verbose_name_plural = "Sites"
def __unicode__(self):
return self.sitename
class Analysis_Items(models.Model):
code = models.ForeignKey('Analysis')
name = models.CharField(max_lenght=100)
value = models.CharField(max_length=20)
class Meta:
verbose_name_plural = "Analysis Type"
class Analysis(models.Model):
date = models.DateField()
site = models.ForeignKey('Sites')
def __unicode__(self):
return str(self.date)
class Meta:
verbose_name_plural = "Analysis"
Hope this is clear enough. thank you in advance!
Site.objects.filter(analysis__analysis_items__name='some_name', analysis__analysis_items__value='some_value')
You can keep adding additional parameters in the same keep AND'ing them all together.