Automatically get user id to assignate to form when submitting

Hi everyone,

Following this topic : Django's template doesn't render as expected (template language issue)

I’m continuing to train on a basic “Blogging app” where people can signup, login and publish articles.

Consequently I’ve created another app “app_articles”.

models.py

from django.db import models
from app_accounts.models import User

class Article(models.Model):
    author = models.ForeignKey(User,on_delete=models.CASCADE)
    title = models.CharField(max_length=20, unique=True, blank=False)
    message = models.CharField(max_length=20, blank=False)

    def __str__(self):
        return self.title

views.py

from django.shortcuts import render, redirect
from .forms import publishArticleForm
from app_accounts.models import User

def publishArticle(request):
    
    if request.method == "POST":
        form = publishArticleForm(request.POST)

        if request.user.is_authenticated:
            # Issue is here, I can't really assign user id to "author"
            form["author"] = User.objects.get(pk=request.user.id)

            if form.is_valid():
                form.save()
            else:
                print("ERROR : Form is invalid")
                print(form.errors)
    
    return redirect("app_base:index")

forms.py

from django.forms import ModelForm
from .models import Article

class publishArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = [
            #Issue is here, because this field is required
            #But I want it to be linked to request.user.id
            "author",
            #The remaining fields will be shown through the form
            "title",
            "message",
            ]

Now the issue is that I get :

    form["author"] = User.objects.get(pk=request.user.id)
TypeError: 'publishArticleForm' object does not support item assignment

Do you have an idea on how to proceed ?

Thank you in advance

Take a look at the forms documentation for the initial property. You could also look at the second half of this section: https://docs.djangoproject.com/en/3.1/topics/forms/modelforms/#overriding-clean-on-a-modelformset

Hi @CodenameTim ! Thank you for your answer.

It seems that it didn’t fix the issue.

Here is my new view :

from django.shortcuts import render, redirect
from .forms import publishArticleForm
from app_accounts.models import User
from app_articles.models import Article

def publishArticle(request):
    
    if request.method == "POST":
        if request.user.is_authenticated:
            user = User.objects.get(pk=request.user.id)

            form = publishArticleForm(initial={
                "author":user, # will get current user id 
                "title":request.POST["title"],
                "message":request.POST["message"], 
                },
                instance=Article) # just a precision that it's related to the article model

            for field in form:
                print(field.value()) # Debug for loop

            if form.is_valid():
                form.save()
            else:
                print("ERROR : Form is invalid")
                print(form.errors)
    
    return redirect("app_base:index")

And here is the output :

1 # Correct
Test # Correct
Hello # Correct
ERROR : Form is invalid # Even if the three fields above are correct, I still get an invalid form, why ?

I think you’ll be better off doing more of the investigation work yourself rather than having someone else provide you with the quick answer.

You’ve gone too far with initial. Please slow down and read the forms documentation, then compare it to what you have. Then once you’ve spotted how to best use initial and request.POST then move onto the validation errors. Read what the errors are and search for them. Determine why they are being triggered then work backwards to get the form the correct information.

Hi @CodenameTim,

I understand what you advise me to do. I’ve been reading the documentation for the whole afternoon but couldn’t really understand where I’m not getting the point.

From what I “understood” I should override the clean() method.
But, the fact is, it doesn’t work.

I would be grateful if you could just give me some tips in order to understand what / why do such or such thing.

Jumping in here to toss my 2 cents into the mix, I’ve got the impression - quite probably incorrect - that you’re not quite “getting” forms yet. Not to worry, if your experience is anything like mine, you’ll have that “light bulb” moment where the switch is flipped and you’ll realize how the pieces all fit together.

I’d suggested spending a lot of time with the “Working with forms” page, with an emphasis on:

If you are looking for a more practical example, I can recommend working through the Django Girls tutorial. Yes, it’s relevant to the specific questions you’re asking.

Ken

1 Like

Hi @CodenameTim, @KenWhitesell,

Ok, so I’ve reread the doc as you advised me and with the same forms.py file :

from django.forms import (
    Form,
    ModelForm,
    ModelChoiceField,
    CharField,
)
from .models import Article
from app_accounts.models import User

class publishArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = [
            "title",
            "message",
        ]

I found two different solutions to my issue :

  1. Modifying a ModelForm field through object generation
from django.shortcuts import render, redirect
from .forms import publishArticleForm
from app_accounts.models import User
from app_articles.models import Article

def publishArticle(request):
    
    if request.method == "POST":
        if request.user.is_authenticated:

            form = publishArticleForm(request.POST)

            for field in form:
                print(field.value())

            if form.is_valid():
                obj = form.save(commit=False) # Return an object without saving to the DB
                obj.author = User.objects.get(pk=request.user.id) # Add an author field which will contain current user's id
                obj.save() # Save the final "real form" to the DB
            else:
                print("ERROR : Form is invalid")
                print(form.errors)
    
    return redirect("app_base:index")
  1. Directly use the ORM to create and save the object in the DB
from django.shortcuts import render, redirect
from .forms import publishArticleForm
from app_accounts.models import User
from app_articles.models import Article

def publishArticle(request):
    
    if request.method == "POST":
        if request.user.is_authenticated:

            form = publishArticleForm(request.POST)

            for field in form:
                print(field.value())

            if form.is_valid():
                new_article = Article.objects.create(
                    author = User.objects.get(pk=request.user.id),
                    title = form.cleaned_data["title"],
                    message = form.cleaned_data["message"],
                )
                new_article.save()
            else:
                print("ERROR : Form is invalid")
                print(form.errors)
    
    return redirect("app_base:index")

Both of these approaches worked for me.

I didn’t use the initial property at all.

I think that approach #1 is the cleanest out of the two but what do you think ?

2 Likes

Cool! You’ve got this!

So I could see using either solution in two different situations.

I agree with you - solution #1 seems cleaner to me when your form is a model form (which it is in this specific case). But it can be simplified a little.
request.user is your user object. There’s no need to issue a query to retrieve a User object you already have a reference to:

obj.author = request.user

or, what I believe works off the top of my head:

obj.author_id = request.user.id

Either (or both) should save you an unnecessary database query during execution.

Solution #2 might be the solution I would pick if the form wasn’t a model form, or a model form for a different model (with the same change as above - author=request.user to eliminate a query).

2 Likes