Django Prefetch not fetching the related fields - django

I have four models that reference a CustomUser table.
class Profile(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
...
class Question(models.Model):
question = models.CharField(max_length=140)
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='answers')
answer = models.CharField(max_length=70)
class Response(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='responses')
question = models.ForeignKey(Question, on_delete=models.CASCADE)
answer = models.ForeignKey(Answer, on_delete=models.CASCADE)
Each question has multiple answers but each user can only pick one answer to each question.
Each user can answer multiple questions.
I have a ViewSet that authenticated users can access that is supposed to return a list of users with the same answers as them.
serializer_class = serializers.ProfileSerializer
def get_queryset(self):
user_answers = qmodels.Answer.objects.filter(response__user=self.request.user)
return models.Profile.objects.prefetch_related(
Prefetch(
'user__responses',
queryset=qmodels.Response.objects.filter(answer__in=user_answers),
to_attr='common_answers'
)
)
class ProfileSerializer(serializers.ModelSerializer):
common_answers = ResponseSerializer(many=True)
However, I'm getting the following error:
Got AttributeError when attempting to get a value for field `common_answers` on serializer `ProfileSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Profile` instance.
Original exception text was: 'Profile' object has no attribute 'common_answers'.
I've added a print after prefetch_related and it is showing the base records properly but there is no common_answers field

Related

Django: unable to save model - Err: "id" expected a number but got <django.db.models.fields.related.ForeignKey...>

I'm really out of ideas here, I tried everything.
Basically I'm just trying to save some item whereas the owner is a foreign key related to the default Django User Model. This same methods works for other views and models, where the association is identically. But here I get this error:
Exception Type: TypeError
Exception Value:
Field 'id' expected a number but got <django.db.models.fields.related.ForeignKey: owner>.
This is my model:
class User(AbstractUser):
pass
""" ... """
class Item(TimeStampMixin):
title = models.CharField(max_length=40)
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="Items")
last_bidder = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name="bidded_objects", default=owner)
etc=etc...
This is my view:
class Form_New_Item(ModelForm):
class Meta:
model = Item
fields = ['title', 'description', 'price', 'category', 'image_url']
def create_new_item(request):
if request.user.is_authenticated and request.method == "POST":
form = Form_New_Item(request.POST)
user = request.user
if form.is_valid():
new_item = Item(
owner=user,
title=form.cleaned_data['title'],
etc=etc...,
)
new_item.save()
return HttpResponseRedirect(reverse("index"))
Notice the error happens when I call new_item.save():
Thank you in advance for any help you can provide me.
I just solved it, I should have provided the entire Item class on the example sorry for that (I just edit the first message), now I see more clearly where the error was:
I have a property of the class that depends on another property.
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="listings")
last_bidder = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name="bidded_objects", default=owner)
So you see the second property last_bidder have as default the first one owner.
Despite the fact that the error Django report seems to be about owner property, the problem was on last_bidder one.
I solved it by setting null=True instead of a default with the first property:
last_bidder = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name="bidded_objects", null=True)

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

Serialization of nested models in Django Rest Framework

I have nested models and many to many relations. When I try to serialize them, results are not visible.
Tried everything in documentation and related names etc.
My base model is like this:
class Question(models.Model):
ques_code = models.CharField(max_length=12, null=True, default='Ques Code')
def __str__(self):
return self.ques_code
Child Model is:
class MCQuestion(Question):
answer_order = models.CharField(
max_length=30, null=True, blank=True,
choices=ANSWER_ORDER_OPTIONS,
help_text=_("The order in which multichoice "
"answer options are displayed "
"to the user"),
verbose_name=_("Answer Order"))
Then linked answer class with key as:
class Answer(models.Model):
mcquestion = models.ForeignKey(MCQuestion,related_name='answers', on_delete=models.CASCADE)
content = models.CharField(max_length=1000,
blank=False,
help_text=_("Enter the answer text that "
"you want displayed"),
verbose_name=_("Content"))
correct = models.BooleanField(blank=False,
default=False,
help_text=_("Is this a correct answer?"),
verbose_name=_("Correct"))
def __str__(self):
return self.content
Serializers are as:
class AnswerSerializer(serializers.ModelSerializer):
class Meta:
model = Answer
fields = ('content','correct')
class MCQuestionSerializer(serializers.ModelSerializer):
answers = AnswerSerializer(many=True, read_only=True)
#answers = serializers.SerializerMethodField()
quiz = QuizSerializer(many=True)
class Meta:
model = MCQuestion
fields = ('ques_code','answers')
Views are:
class QuestionViewSet(viewsets.ModelViewSet):
queryset = Question.objects.all()
serializer_class = MCQuestionSerializer
When I access API for questions, nested answers are not visible. I checked all documentation and checked and changed my code.
If I try using answers = serializers.SerializerMethodField() and define get_answers function for it, error comes saying "Question has no attribute of answers"
I think it is due to child and mother model system. It is searching attribute in Question, not in MCQuestion model. What can I do?
You were using the "wrong queryset-serializer combination" for the viewset class.
So, change the queryset reference in your view class as ,
class QuestionViewSet(viewsets.ModelViewSet):
queryset = MCQuestion.objects.all()
serializer_class = MCQuestionSerializer
Apart from that, I'm not sure you are aware of Django Model Inheritance. Anyway read it from here if necessary, Django Model Inheriitance

Django REST framework, dealing with related fields on creating records

Preliminary note: this is a rather newbie question, though I have not found a sufficient answer on StackOverflow; many similar questions, but not this one. So I am asking a new question.
The problem: I'm having difficulty creating records where one field is a foreign key to an existing record, and I do not know what I'm doing wrong in my code.
In my app there are two models in question, a one-to-many relationship between Company and BalanceSheet:
models:
class Company(models.Model):
cik = models.IntegerField(default=0, unique=True)
symbol = models.CharField(max_length=4, unique=True)
name = models.CharField(max_length=255, unique=True)
def __str__(self):
return self.symbol
class BalanceSheet(models.Model):
company = models.ForeignKey(Company,
null=True,
on_delete=models.CASCADE,
related_name='balance_sheets',)
date = models.DateField()
profit = models.BigIntegerField()
loss = models.BigIntegerField()
class Meta:
unique_together = (('company', 'date'),)
def __str__(self):
return '%s - %s' % (self.company, self.date)
serializers:
class BalanceSheetSerializer(serializers.ModelSerializer):
company = serializers.StringRelatedField()
class Meta:
model = BalanceSheet
fields = ('company','date','profit','loss')
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ('cik', 'symbol', 'name')
Views:
class BalanceSheetCreate(generics.CreateAPIView):
model = BalanceSheet
queryset = BalanceSheet.objects.all()
serializer_class = BalanceSheetSerializer
urls include:
url(r'^(?P<symbol>[A-Z]{1,4})/create-balance-sheet/$', views.BalanceSheetCreate.as_view(),
name='create_balance_sheet'),
To this point, I have zero problem reading data. However, when trying to create records, I get errors I don't understand:
curl http://localhost:8000/financials/AAPL/create-balance-sheet/ -X POST -d "company=AAPL&date=1968-04-17&profit=1&loss=1"
IntegrityError at /financials/AAPL/create-balance-sheet/
null value in column "company_id" violates not-null constraint
Dropping the company data from that curl command results in the same error.
How do I get around this error? I thought I was telling the api what company I'm interested in, both explicitly in the url and in the post data.
Using python3.6, django 1.11, and djangorestframework 3.7.7
You get the IntegrityError because your code will try to create a new BalanceSheet without a company. That's because StringRelatedField is read-only (see docs) and therefore it's not parsed when BalanceSheetSerializer is used in write mode.
SlugRelatedField is what you need here:
class BalanceSheetSerializer(serializers.ModelSerializer):
company = serializers.SlugRelatedField(slug_field='symbol')
class Meta:
model = BalanceSheet
fields = ('company','date','profit','loss')
To answer my own question, here's what I wound up with. Thanks again go to dukebody.
models:
class Company(models.Model):
cik = models.IntegerField(default=0)
symbol = models.CharField(max_length=4)
name = models.CharField(max_length=255)
def __str__(self):
return self.symbol
class BalanceSheet(models.Model):
company = models.ForeignKey(Company,
null=True,
on_delete=models.CASCADE,
related_name='balance_sheets',)
date = models.DateField()
profit = models.BigIntegerField()
loss = models.BigIntegerField()
class Meta:
unique_together = (('company', 'date'),)
def __str__(self):
return '%s - %s' % (self.company, self.date)
serializers:
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ('cik', 'symbol', 'name')
class BalanceSheetSerializer(serializers.ModelSerializer):
company = CompanySerializer(many=False)
class Meta:
model = BalanceSheet
fields = ('company', 'date', 'profit', 'loss')
def create(self, validated_data):
company_data = validated_data['company']
company, created = Company.objects.get_or_create(**company_data)
validated_data['company'] = company
sheet = BalanceSheet.objects.create(**validated_data)
return sheet
I also had to include the full company data within my curl statement as a nested dict.

Unable to sort a user ForeignKey in Django [duplicate]

This question already has answers here:
Django Admin: Ordering of ForeignKey and ManyToManyField relations referencing User
(4 answers)
Closed 7 years ago.
So I basically have a Django form to change ownership of an item, but users are showing up in a non-alphabetical and as of yet uncontrollable order.
class myModel(models.Model):
owner = models.ForeignKey(User, null=True, blank=True)
Since I am using a built in model I can't for the life of me figure out how to get "owner" field to sort by the username when calling the form below.
class OwnerChange(ModelForm):
required_css_class = 'required'
class Meta:
model = myModel
fields = ['owner']
In your model form can easily override the queryset.
class OwnerChange(forms.ModelForm):
required_css_class = 'required'
## Update the queryset to order by username
owner = forms.ModelChoiceField(queryset=User.objects.order_by('username'))
class Meta:
model = Cluster
fields = ['owner']
Don't forget to update your import to
from django import forms
Another option you can use to not update all forms. However, the above example is useful if you don't want to sort all instances in the model in all queryset by username
class myModel(models.Model):
owner = models.ForeignKey(User, null=True, blank=True)
class Meta:
ordering = ['owner__username']