Can't reuse request.user.id while saving a form

Hi everyone,

As usual I’m trying to go deeper into Django internals.

This time I’m trying to save a form in the db. This form is for an Article

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=40, blank=False)
    content = models.CharField(max_length=240, blank=False)

Whose author is

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

class User(AbstractUser):
    username = None
    email = models.EmailField(max_length=40, unique=True, blank=False)
    password = models.CharField(max_length=40, blank=False)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []

The issue is when the user publishes a new article. Indeed, I always get the following error message when my custom save method is triggered :

ERROR : Failed to retrieve current user

forms.py

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

class publishArticleForm(Form):
    title = CharField(max_length=40, required=True)
    content = CharField(max_length=40, required=True)

    # Issue is here
    def save(self, request):
        print("CURRENT USER ID = " + str(request.user.id)) # Works

        try:
            # Doesn't work whereas it works while in manage.py shell
            current_user = User.objects.get(pk=request.user.id) 
        except:
            print("ERROR : Failed to retrieve current user")
        else:
            new_article = Article.objects.create()
            new_article.author = User.objects.get(pk=request.user.id)
            new_article.title = self.cleaned_data["title"]
            new_article.content = self.cleaned_data["content"]

Any Idea ?

Thanks in advance

This is just a nitpicky comment, but you don’t have to query the user and assign its result to current_user because the user object is already appended to the request object. Instead, you could just do current_user = request.user.

— EDIT —
I may be incorrect here, but I didn’t think the request object was a parameter on the Form.save() function.

1 Like

Hi @Suttonium !

Thanks for your answer, you are right from a performance point of view it is better to directly get current user through request.user

Thus, it still doesn’t work argh. And I don’t want to use ModelForm because I want to understand how to cleanly build a save() method for a Form.

I just edited my comment above :slight_smile:

It’s ok, I pass the request as a parameter inside the view :

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

        if form.is_valid():
            form.save(request)

    return redirect("app_base:index")

Okay, gotcha. Can you output the request.user object to the console inside the view, please, so we can see what’s going on?

I output a basic :

print(request.user)

and got :

test@mail.com

Okay, so that’s looking fine. Can you output the request.user and request.user.id in the forms.save method, please?

Here it is :

USER = test@mail.com
USER ID = 1

Alright, well if that data is coming back fine, then I’m assuming there is something wrong with your query. You should be able to remove that query altogether and just assign current_user to request.user. Try doing that and see if your error still occurs.

You’re chasing the wrong objective here.

It makes no sense to try and save a form that isn’t a model form. That non-model form isn’t “connected” to anything that can be saved.

If you have a view that is using a form that isn’t a model form, then you are effectively by definition saying that you’re going to process the data from that form in your view.

So, if that’s the route you’re taking (non-model form), then you want to create the model instance in your view, assign the form fields to it in your view, and save it from your view.

Hi @KenWhitesell, I changed my code accordingly :

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

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

        if form.is_valid():
            print("CURRENT USER => " + str(request.user))
            print("CURRENT USER ID => " + str(request.user.id))
            new_article = Article.objects.create()
            new_article.author = request.user
            new_article.title = form.cleaned_data["title"]
            new_article.content = form.cleaned_data["content"]

    return redirect("app_base:index")

But I got :

CURRENT USER => test@mail.com
CURRENT USER ID => 1
django.db.utils.IntegrityError: NOT NULL constraint failed: app_articles_article.author_id

The create method saves the object to the database. In this case, since you’re not passing any parameters to the method, you’re trying to create an “empty” Article instance.

You are right !

The correct way to use create() was like this :

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

        if form.is_valid():
            new_article = Article.objects.create(
                author=request.user,
                title=form.cleaned_data["title"],
                content=form.cleaned_data["content"],
            )

    return redirect("app_base:index")
1 Like