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 :
- 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")
- 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