Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Python

Using a ForeignKey to pull author's name

I am having trouble figuring out how to get the author's full name instead of their username when linking a Post to the User. Currently, it returns the username, which I presume has something to do with the User's str method. I found out that I can add an 'if int' statement to it which would return a formatted string with the author's full name, but is there a more preferred way of doing this?

class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL)

Edit:

# models

class MyUser(AbstractBaseUser):
    username = ...
    first_name = ...
    last_name = ...

    __str__(self):
        return self.username

class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL)

3 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 67,990 Points

The ForeignKey is used to relate one model to another, it's not used to access attributes on other models.

In your case, with a given Post instance, say post, you can use one of the built-in methods to User to get the full name:

author_fullname = post.author.get_full_name()

See link for other methods available.

I'm not sure I understand. With the code above, when I select the author for the blog post in my admins panels (using django), the names that appear are usernames - instances of the User model. Instead of having the usernames come up, I'm trying to get their actual names. I think the reason the usernames are coming up is because of the str returning self.username. So I think I just have to edit that to return the full name instead and that's it?

Chris Freeman
Chris Freeman
Treehouse Moderator 67,990 Points

Sorry. I didn't realize you were referring to the Django admin views. These views are not trivial to modify but it can be done. I found this blog post with instructions: Pretty options for Django Authuser

Okay, it is working properly now on the form, but for the PostAdmin in admin.py, the list_display is still referring to the Post model's author attribute which continues to return the username. So it seems I have to make a modification somewhere with the models. I presume the only way is to change the __str__ on the User model?

Chris Freeman
Chris Freeman
Treehouse Moderator 67,990 Points

Overwriting the __str__ method for User model in the core code can be troublesome. The better approach is to follow the instructions in the blog post to create a new AdminForm which calls custom UserModelChoiceField which has the format you are looking for. The blog uses "full_name (username)" format.

I'm still having issues with this :\

# models
class MyUser(AbstractBaseUser):
    username = ...
    first_name = ...
    last_name = ...

    __str__(self):
        return self.username

class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL)


# forms
from django.forms import ModelChoiceField
from django import forms

from blog.models import MyUser, Post

class UserModelChoiceField(ModelChoiceField):
    """
    Custom ModelChoiceField for representing User select boxes in Admin
    """
    def label_from_instance(self, obj):
        return "{}".format(obj.get_full_name())


class PostAdminForm(forms.ModelForm):
    author = UserModelChoiceField(MyUser.objects.all()
                                .order_by('last_name', 'first_name'))

    class Meta:
        model = Post
        fields = '__all__'


# admin
from blog.models import MyUser, Post

class PostAdmin(admin.ModelAdmin):
    form = PostAdminForm

    list_display = ['title', 'author', ]
    list_filter = ['author', ]

    def save_model(self, request, obj, form, change):
        if getattr(obj, 'author', None) is None:
            obj.author = request.user
        obj.save()

admin.site.register(Post, PostAdmin)

Here is a screenshot:

img

http://imgur.com/v7DfyGU Top image is current result. Bottom image is desired result.

[MOD: made image inline -cf]

Chris Freeman
Chris Freeman
Treehouse Moderator 67,990 Points

Where is the "title" field shown in the display_list coming from?

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 67,990 Points

Though the Django docs mention ForeignKeys will be displayed as their __str__ value, would it work to add an additional field instead of trying to replace the default __str__ value? Based on code examples from the same link, perhaps you can try adding an additional field.

# Modify the class
class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL)

    # Add helper function for Admin display
    def author_full_name(self):
        return self.author. get_full_name()
    author_full_name.short_description = 'Full Name'    

# In admin.py
class PostAdmin(admin.ModelAdmin):
    list_display = ('author_full_name', 'author')