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
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?
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