djangoformsdjango-modelsdjango-formsdjango-model-field

Initialize django ModelForm user field with current logged user


I'm trying to initialize some fields of my NewArticleForm with static data. In particular, I want to set the author field with the current logged user/author, and shouldn't be modifyable. This page is reachable only from logged user, and the information is also stored in the url:

path('<int:user_id>/create', views.add_article, name='insert'),

forms.py:

class NewArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ['author','title', 'content', 'pub_date']
    
    pub_date = forms.DateTimeField(initial=timezone.now())

    def save(self, commit=True):
        article = super(NewArticleForm, self).save(commit=False)
        if commit:
            article.save()
        return article

models.py:

from django.db import models
from django.contrib.auth.models import User

class Article(models.Model):
    author =  models.ForeignKey(User, on_delete=models.CASCADE)
    pub_date = models.DateTimeField()
    title = models.CharField(max_length=50)
    content = models.TextField()

    def __str__(self):
        return self.title

    def get_year(self):
        return self.pub_date.year

    def get_month(self):
        return self.pub_date.month

views.py:

@login_required
def add_article(request, user_id):
    if request.method == 'POST':
        form = NewArticleForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, 'Articolo inserito con successo!')
            return redirect('/blog_app/')
        else:
            messages.warning(request, 'Qualche campo non è corretto, operazione fallita.')

    form = NewArticleForm()
    return render(request, template_name='blog_app/insert.html', context={'insert_form':form})

How can I set author with the current logged user?


Bonus question: Why pub_date field, which is a DateTimeField, is displayed as text type? I can't change it.


Solution

  • Request.user is already included via the request argument of the view. Because login is required you know it won't be an anonymous user.

    You can thus refer to it in your view.py

    @login_required
    def add_article(request, user_id):
        if request.method == 'POST':
            form = NewArticleForm(request.POST)
            if form.is_valid():
                #We can add things to the form.instance before saving
                #request has been passed as an argument to the view so we can get the user also
                form.instance.author = request.user
                #save the result and create a new db record
                form.save()
                messages.success(request, 'Articolo inserito con successo!')
    

    Because you have this info already, and you can't override the author value, it doesn't make sense to include it as field in the form.

     class NewArticleForm(forms.ModelForm):
        class Meta:
            model = Article
            fields = ['title', 'content', 'pub_date']
    

    Additionally, because the page requires login, and it's always going to refer to the logged in user, you don't need to include the userid in the URL as you can always get the value either by request.user in the view or {{request.user}} in the template. Revealing a user_id is a bit of a security risk as it reveals db userinfo.

    Bonus answer:

    I'd imagine DateTimeField uses text as input to support the widest range of browsers. input type = date is relatively new, and, unfortunately, the default browser widgets are not terribly accessible, whereas text usually is.