Reputation: 191
I want to write a tweeter-like app and I'd like to get the author username instead of the author id. However now I'm having this error when I create a new Tweet: NOT NULL constraint failed: tweets_tweet.author_id
I first tried to use the get_field method in my seriliazer.
Here is the model:
class Tweet(models.Model):
slug = models.CharField(max_length=100, unique=True, null=True,
blank=True)
content = models.TextField(max_length=150)
datestamp = models.DateTimeField(auto_now_add=True)
nb_likes = models.PositiveIntegerField(default=0)
nb_comments = models.PositiveIntegerField(default=0)
author = models.ForeignKey(
User,
on_delete = models.CASCADE,
related_name = "tweets",
related_query_name = "post",
null=False
)
def save(self, *args, **kwargs):
self.slug = slugify(self.content[:10]) + '-' + randomString()
super(Tweet, self).save(*args, **kwargs)
Here is my Serializer :
class TweetSerializer(serializers.ModelSerializer):
author = serializers.SerializerMethodField()
def get_author(self, obj):
return obj.user.username
class Meta:
model = Tweet
fields = ["content", "author", "nb_likes", "nb_comments", "slug"]
def to_representation(self, obj):
representation = super().to_representation(obj)
if obj.nb_likes > 50:
representation['super_popular'] = True
return representation
and my JSON looks likes :
{
"content": "blablabla",
"author_username": "valentin",
"author": 1,
"nb_likes": 0,
"nb_comments": 0,
"slug": "blablabla-2159054817"
}
Upvotes: 2
Views: 2015
Reputation: 20682
As you can see here, SerializerMethodField
is a read-only field. So you've made author
read-only, whereas you still want to be able to create a Tweet
with an author
.
Now if you want to use username
instead of id
for both read and write, and assuming username
is unique, then this document shows you the options you have for related fields: The SlugRelatedField
with slug_field
set to author
would allow you do pass in "author" as username and return it also as "username":
author = SlugRelatedField(slug_field="username", queryset=User.objects.all())
Note that this means your input JSON needs to be changed, because right now the "author" key maps to an id.
But if you want to use the id
as input and the username
as output, then you need to make it a custom relational field, with different representation than internal value:
class TweetAuthorField(serializers.PrimaryKeyRelatedField):
def to_representation(self, value):
return value.username
I'm subclassing PrimaryKeyRelatedField
here, because it already does all the correct things for mapping data to the object using the primary key (the id), so there's no need to rewrite that code.
Upvotes: 4