Search code examples
pythondjangodjango-modelsdjango-formsblogs

Django - How Do I Set A Default Value In A Form To Be The Current User?


quick beginner Django question because I haven't been able to find an answer that directly solves what i'm after, or doesn't add in a bunch of overcomplicated functionality I don't need with its answer.

I have a basic Blog setup, with a model for users and their associated posts, and a form for creating new posts. However all I want is for the "Author" field on the form to be automatically populated with the currently logged in user, rather than being a drop down list of all the registered users. My Model:

class Post(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    body = models.TextField()
    post_date = models.DateField(auto_now_add=True)
    category = models.CharField(max_length=255)
    site = models.CharField(max_length=255)
    def __str__(self):
        return self.title + ' | ' + str(self.author)
    def get_absolute_url(self):
        return reverse('home')

My Form:

class PostForm(forms.ModelForm):
 class Meta:
    model=Post
    fields = ('title', 'author', 'category', 'site', 'body')
    widgets = {
        'title': forms.TextInput(attrs={'class': 'form-control'}),
        'author': forms.Select(attrs={'class': 'form-control' ,'readonly': 'readonly'}),
        'category': forms.Select(choices=choice_list,attrs={'class': 'form-control'}),
        'site': forms.Select(choices=site_choice_list,attrs={'class': 'form-control'}),
        'body': forms.Textarea(attrs={'class': 'form-control'})
    }

My View:

class AddPostView(CreateView):
model = Post
form_class = PostForm
template_name = 'add_post.html'

To reiterate, I simply want the 'author' field in the post to be read-only and populated with the current logged in user. Rather than the user being able to select from a list of users.

Thank you in advance, Let me know if I can provide anything else to help you help me :)


Solution

  • You should disable the field, not just add a readonly attribute to the widget, since a "hacker" can forge a malicious HTTP request that sets the author to another user:

    class PostForm(forms.ModelForm):
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.fields['author'].disabled = True
        
        class Meta:
            model = Post
            fields = ('title', 'author', 'category', 'site', 'body')
            widgets = {
                'title': forms.TextInput(attrs={'class': 'form-control'}),
                'author': forms.Select(attrs={'class': 'form-control'}),
                'category': forms.Select(choices=choice_list,attrs={'class': 'form-control'}),
                'site': forms.Select(choices=site_choice_list,attrs={'class': 'form-control'}),
                'body': forms.Textarea(attrs={'class': 'form-control'})
            }

    We can then use this form in a view with:

    from django.contrib.auth.mixins import LoginRequiredMixin
    
    class AddPostView(LoginRequiredMixin, CreateView):
        model = Post
        form_class = PostForm
        template_name = 'add_post.html'
    
        def get_initial(self):
            return {'author': self.request.user}

    The LoginRequiredMixin mixin [Django-doc] guarantees that only users that have logged in can see (and interact with) the view.