Valentin
Valentin

Reputation: 191

How to get a username instead of an id?

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

Answers (1)

dirkgroten
dirkgroten

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

Related Questions