I have the standard django polls models
class Poll(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.question_text
class Choice(models.Model):
poll = models.ForeignKey(Poll, null=True, blank=True)
choice_text = models.CharField(max_length=200)
vote_count = models.IntegerField(default=0)
In the serialisers I have added a reference to foreign key as follows :
class PollSerializer(serializers.ModelSerializer):
choices = ChoiceSerializer(many=True, read_only=True, required=False)
class Meta:
model = Poll
fields = ('id', 'question_text', 'pub_date', 'choices')
I have created the choices specific to the questions by the django admin. In the poll list from django rest framework, I expect to see the list of choices associated with the poll. It only shows the Poll objects as follows.
Can someone guide me, as to what has gone missing, and how can I see the choices associated with the Poll?
Since reverse manager for choice objects is choice_set You should set source='choice_set' for choices field:
class PollSerializer(serializers.ModelSerializer):
choices = ChoiceSerializer(many=True, read_only=True, required=False, source='choice_set')
Apart from #neverwalkaloner's answer, you could do it this way also,
class PollSerializer(serializers.ModelSerializer):
choices_set = ChoiceSerializer(many=True, read_only=True, required=False)
class Meta:
model = Poll
fields = ('id', 'question_text', 'pub_date', 'choices_set')
Related
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
I made API with Django Restframework.
[models.py]
from django.db import models
class Article(models.Model):
article_no = models.AutoField(primary_key=True)
content = models.CharField(max_length=500, null=False)
password = models.CharField(max_length=20, null=False, default='1234')
date = models.DateTimeField(auto_now_add=True)
class Comment(models.Model):
article_no = models.ForeignKey('Article', on_delete=models.CASCADE)
content = models.CharField(max_length=50, null=False, default='')
password = models.CharField(max_length=20, null=False, default='1234')
date = models.DateTimeField(auto_now_add=True)
[views.py]
class ArticleDetail(APIView):
def get(self, request, article_no, format=None):
try:
article = models.Article.objects.get(article_no=article_no)
serializer = serializers.ArticleDetailSerializer(article)
return Response(status=status.HTTP_200_OK, data=serializer.data)
except models.Article.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
[urls.py]
urlpatterns = [
path('article/<int:article_no>', views.ArticleDetail.as_view(), name='article_detail'),
]
[serializers.py]
class ArticleDetailSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True)
comment = CommentSerializer(many=True, read_only=True)
class Meta:
model = models.Article
fields = (
'article_no',
'content',
'password',
'date',
'comment',
)
In serializers.py, I defined comment = CommentSerializer(many=True, read_only=True) and add it to fields.
And to test it, I add comment for article_no=1
But When I connect to /article/1, comment doesn't show anything.
I want to show all comments related it's article_no.
How can I fixed it?
Thanks.
Fixed source is here.
[serializers.py]
class ArticleDetailSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True)
class Meta:
model = models.Article
fields = (
'article_no',
'content',
'password',
'date',
'comments',
)
[models.py]
class Comment(models.Model):
article_no = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comments')
content = models.CharField(max_length=50, null=False, default='')
password = models.CharField(max_length=20, null=False, default='1234')
date = models.DateTimeField(auto_now_add=True)
When I connect to my server,
It only shows related comment's article_no.
But I want to show content and date also.
According to the Django documentation at https://docs.djangoproject.com/en/2.0/topics/db/queries/#backwards-related-objects you can access the list of objects by calling article_instance.comment_set.all() or you could set the related_name argument on the model on initialization
article_no = models.ForeignKey('Article', on_delete=models.CASCADE, related_name="comments")
and access is like article_instance.comments.all() or filter() or exclude()
There are quite a few options actually, and it depends on the use case, but for simplicity, in this case you may be able to just change the comment variable to comment_set, or you could change the related_name to comments and refer to it as such in your serializer.
required changes to ArticleDetailSerializer...
comment = CommentSerializer(many=True, read_only=True)
to
comments = CommentSerializer(many=True, read_only=True)
You also haven't created a CommentSerializer class, or you haven't posted it to the question.
example CommentSerializer....
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model=Comment
exclude=('article_no',)
I exclude the article_no and the remaining fields should be handled due to the assigned defaults on the django models.
The related object manager in Django returns a queryset that can be acted on like any other queryset. So you will want to consider whether an article might have an absurd amount of comments and limit the returned amount.
you can also use a SerializerMethodField and have more control over the returned queryset
comments = serializers.SerializerMethodField()
def get_comments(self, obj):
comments = obj.comments/comment_set.all()[:20] #return the first 20 comments
return CommentSerializer(comments/comment_set, many=True, read_only=True).data
now add comments/comment_set to the class Meta/fields tuple
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.
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.
I'm building a serializer in django using the django rest framework. I need to filter the query set for a nested model.
I found How do you filter a nested serializer in Django Rest Framework?, which seemed to have the answer, but when I implemented it there was no change in my data. The only difference I can see is that the serializer referencing the filtered list serializer has other fields as well.
The models (abbreviated for clarity):
class GCUser(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField()
is_member = models.BooleanField(default=False)
age = models.SmallIntegerField(blank=True, null=True)
session_key = models.CharField(max_length=100, db_index=True, blank=True, null=True)
class Connection(models.Model):
creation_date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(GCUser, related_name='user_connection')
event = models.ForeignKey(Event, related_name='event_connection')
role = models.CharField(max_length=8, choices=constants.Roles.ROLE_CHOICES,)
class Event(Game):
creation_date = models.DateTimeField(auto_now_add=True)
public = models.BooleanField(default=False)
start_time = models.DateTimeField(null=True, blank=True)
end_time = models.DateTimeField(null=True, blank=True)
gm_is_player = models.BooleanField(default=False,
help_text='Check if GM will be playing the game',
verbose_name='GM is a player')
gm_is_designer = models.BooleanField(default=False, help_text='Check if GM designed the game')
user_notes = models.TextField(blank=True, default='', verbose_name='Note to Scheduler')
scheduler_notes = models.TextField(blank=True, default='')
experience = models.CharField(max_length=3, choices=constants.ExpLevels.EXPERIENCE_CHOICES,
default=constants.ExpLevels.NOVICE,)
status = models.CharField(max_length=4, db_index=True,
choices=constants.Status.STATUS_CHOICES,)
Here's my code:
class FilteredListSerializer(serializers.ListSerializer):
def to_representation(self, data):
data = data.filter(status=constants.Status.ASSIGNED).order_by('start_time')
return super(FilteredListSerializer, self).to_representation(data)
class UserEventSerializer(serializers.ModelSerializer):
class Meta:
list_serializer_class = FilteredListSerializer
model = models.Event
fields = ('id', 'event_name', 'conflict_type', 'start_time', 'end_time')
class UserConnectionSerializer(serializers.ModelSerializer):
event = UserEventSerializer()
class Meta:
model = models.Connection
fields = ('get_role_display', 'conflict_type', 'event')
class GCUserSerializer(serializers.ModelSerializer):
user_connection = UserConnectionSerializer(many=True)
class Meta:
model = models.GCUser
fields = ('pk', 'first_name', 'last_name', 'email',
'is_member', 'age', 'user_connection')
PyCharm tells me that "class FilteredListSerializer must implement all abstract methods" but it doesn't actually throw an error. I put a breakpoint at the first line of the list serializer, but it doesn't get tripped.
I'm using Python 3.4 with django 1.7.
Thanks in advance for your help.
Edited to add: Looking into the serializer code, I realized what may be the key difference: my call has many=True, whereas the one from the previous post didn't. I tried taking out the model=, but as expected that threw an error, so apparently the "working" code in the earlier post didn't actually run as written.
So I am not sure how to use the method you are using, but, if I understand your question correctly, I believe you could do something like this:
class UserEventSerializer(serializers.ModelSerializer):
class Meta:
model = models.Event
fields = ('id', 'event_name', 'conflict_type', 'start_time', 'end_time')
class UserConnectionSerializer(serializers.ModelSerializer):
event = serializers.SerializerMethodField()
class Meta:
model = models.Connection
fields = ('get_role_display', 'conflict_type', 'event')
def get_event(self, obj):
if obj.event.status == constants.Status.ASSIGNED:
serializer = UserEventSerializer(obj.event)
return serializer.data
else:
serializer = UserEventSerializer(None)
return serializer.data
N.B. This assumes that you are trying to exclude Events from being serialized if their status is not assigned.
I hope this helps. If I didn't understand the problem, let me know.
it's not a bug or error. ModelSerializer has got already implemented all needed methods (most inhereted from Serializer class), but ListSerializer inherits from BaseSerializer and got implemented e.g.: .create() or .to_representation(), but not .update(). I've got some similar problems in PyCharm, when subclassing a Serializer. After implementing create, update and to_representation methods this issue was gone