Python Django: "Post.author" must be a "User" instance error - python

I am trying to assign username to author field in Post model , Django spews out the following error:
"Post.author" must be a "User" instance.
model:
class Post(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to='',null=True,blank=True)
image_url = models.CharField(max_length=200,default=None,null=True,blank=True)
date = models.DateTimeField(default=timezone.now)
content = models.TextField()
author = models.ForeignKey(User, null=False, blank=False,on_delete=models.CASCADE)
categories = models.ManyToManyField(Category)
published = models.BooleanField()
def __str__(self):
return self.title
view:
#login_required
def new_post(request):
# Add a new post
if request.method != 'POST':
# No data submitted, create a blank form
form = PostForm()
else:
# Post data submitted, process data
form = PostForm(data=request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.author = request.user.username
new_post.save()
return redirect('elogs:posts')
#Display a blank or invalid form
context = {'form':form}
return render(request,'elogs/new_post.html',context)
form:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title','content','image','image_url','published']
widgets = {
'title': forms.Textarea(attrs={'placeholder': 'Title..'}),
'content': forms.Textarea(attrs={'placeholder': 'What is on your mind?'}),
'categories': forms.TextInput()
}
I have solved this error just like this:
from django.contrib.auth import get_user_model
author = models.OneToOneField(get_user_model(),on_delete=models.CASCADE)

You're giving the username instead of the user itself:
new_post.author = request.user.username
A ForeignKey takes an instance of the given Model, User in this case.

Related

Django form.is_valid() issue

The idea is a user will create a post on this page and fill out the title and content.
Once they submit this form, a Post instance will be created with the inputs of the user for title and content. This instance will also have the id of the user associated to it.
I've done a print test which yields the outputs I'm looking for but it appears that the form is not valid. Any thoughts?
#Resolved - Thank you all
def post(self, request, locationname):
form = PostCreate(request.POST)
current_user = request.user
found_location = Location.objects.get(name = locationname)
if form.is_valid():
form = form.save(commit=False)
form.post_location = Location.objects.get(id = found_location.id)
form.author = Profile.objects.get(user_id = current_user)
form.save()
return HttpResponseRedirect(self.request.path_info)
#views.py
class LocationDetail(View):
def post(self, request, locationname):
current_user = request.user
user_profile = Profile.objects.get(user_id = current_user)
form = PostCreate()
found_location = Location.objects.get(name = locationname)
context = {"found_location": found_location, "form": form}
if request.method == "POST":
post_author = user_profile.user_id
post_title = request.POST['title']
post_content = request.POST['content']
print(post_author)
print(post_title)
print(post_content)
if form.is_valid():
Post.objects.create(
title = post_title,
content = post_content,
author_id = post_author,
)
form.save()
return render(request,'location-detail.html', context)
#forms.py + models.py
class PostCreate(ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
class Profile(Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length = 30)
profile_location = models.CharField(max_length = 80, blank=True, null=True)
join_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Post(Model):
author = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name="posts")
title = models.CharField(max_length = 30)
content = models.CharField(max_length=300)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.author} - {self.title}'
#template: location-detail.html
<form action="" method="POST">
{%csrf_token%}
{{form}}
<button type="submit">Submit</button>
</form>
You do not need to check if the request is a "POST" request since you are using class-based views. The post() method will be called by the dispatcher method inside of the base "View" class when you submit the form. Before checking if the form is valid, fill the instance of the form with the request data, then you can set the "author" field like so:
def post(self, request, locationname):
form = PostCreate(request.POST)
if form.is_valid():
# don't commit to database yet
form = form.save(commit=False)
# set author field
form.author = user_profile.user_id
# commit to database
form.save()
return redirect("<url_name>")
When .save(commit=False) is called, the form instance with the data is saved without committing to the database. Then, when the .save() method is called the second time, the data will be saved to the database. It is recommended to redirect to the desired url after a POST request. The <url_name> comes from the url in your 'urls.py' file that you want to redirect to.
More info can be found in the django docs here:
https://docs.djangoproject.com/en/3.2/topics/forms/
Try to revise this line:
user_profile = Profile.objects.get(user_id = current_user)
to
user_profile = Profile.objects.get(user_id = current_user.id)
Since the argument user_id needs an id value, not an object.

How can i set permission on url

I am new on Django,
I have implemented a valid form and now I want to set permission on URL.
When a form is submitted, then it redirects me to this URL
http://127.0.0.1:8000/success/
Without submitting a form I can manually type the name of the URL http://127.0.0.1:8000/success/ and it will take me to the same page.
How can I set permission on "success" url, so that user can not manually view the page unless the form is valid and submitted?
Do I need a decorator for this?
Model:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
profile_pic = models.ImageField(upload_to='ProfilePicture/', default="ProfilePicture/avatar.png", blank=True)
phone = models.IntegerField(default='0', blank=True)
email = models.EmailField(blank=True)
date_of_birth = models.CharField(max_length=50, blank=True)
address = models.TextField(blank=True)
date = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'Profile'
verbose_name_plural = 'Profiles'
ordering = ['-date']
'''Method to filter database results'''
def __str__(self):
return self.user.username
class CotCode(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
cot_code = models.IntegerField(default='0', blank=True)
date = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'CotCode'
verbose_name_plural = 'CotCode'
ordering = ['-date']
def __str__(self):
return self.user.username
Url:
path('cot/', TransferCOTView, name='transfer_cot'),
path('success/', SuccessfulView, name='successful_trans'),
Views:
#login_required
def TransferCOTView(request):
form = CotCodeForm(request.POST)
if request.method == "POST":
if form.is_valid():
cot_code = form.cleaned_data.get('cot_code')
try:
match = CotCode.objects.get(cot_code=cot_code)
return redirect('site:successful_trans')
except CotCode.DoesNotExist:
messages.info(request, "Wrong code")
else:
form = CotCodeForm()
context = {
'form':form,
}
return render(request, 'transfer_cotcode.html', context)
#login_required
def SuccessfulView(request):
return render(request, 'successful_transfer.html')
A simple option would be to set a session variable informing that the form was posted and valid and check it before displaying the success page. See session examples here https://docs.djangoproject.com/en/3.1/topics/http/sessions/#examples
e.g.
def formview(request):
# process form
request.session["form_filled"] = True
# redirect to success
def success(request):
if not request.session.get("form-filled"):
raise Http404("Form not filled")
del request.session["form_filled"] # success view visible once after form is filled
# return view template
You can also check if the user has COT code filled in the success view.
def SuccessfulView(request):
cot_code_exists = CotCode.objects.filter(user=request.user).count()
if not cot_code_exists:
raise Http404("Cot code does not exist for user")
return render(request, 'successful_transfer.html')

Can't save data when posting form in Django

I'm following a Django tutorial to build a small site which you can add pages and category in the site, I defined a Page model as below:
class Page(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
title = models.CharField(max_length=128)
url = models.URLField()
views = models.IntegerField(default=0)
def __str__(self):
return self.title
and defined a modelform as below:
class PageForm(forms.ModelForm):
title = forms.CharField(max_length=128, help_text="Please enter the title of this page")
url = forms.URLField(max_length=200, help_text="Please enter the URL of the page")
views = forms.IntegerField(widget=forms.HiddenInput(),initial=0)
category = forms.ModelChoiceField(queryset=Category.objects.all(),help_text="Please choose a category for the page")
class Meta:
model = Page
fields = '__all__'
and views to add_page is:
def add_page(request):
if request.method == 'POST':
form = PageForm(request.POST)
if form.is_valid():
page = form.save(commit=True)
print(page)
page.views = 0
page.save()
return index(request)
else:
print('error')
form.errors
else:
form = PageForm()
context_dict = {'form':form}
return render(request,'rango/add_page.html',context_dict)
But when I run the site and fill the information like this in the field:
choose category 'HTML' in the select window
fill 'w3c' in the name field
fill 'http://www.w3cshools.com' in the url field
and choose to 'create a page' but there's no response after clicking the button nor the data in the form is added into the database, I try to debug using print and found that the data can't pass through the validation which no process of the if block 'form.is_valid()' but I can't understand why and how to da modification.
my category model is defined as:
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
slug = models.SlugField(unique=True)
def save(self,*args,**kwargs):
self.slug = slugify(self.name)
super(Category, self).save(*args,**kwargs)
def __str__(self):
return self.name
category form:
class CategoryForm(forms.ModelForm):
name = forms.CharField(max_length=128, help_text="Please enter the category name.")
views = forms.IntegerField(widget=forms.HiddenInput(),initial=0)
likes = forms.IntegerField(widget=forms.HiddenInput(),initial=0)
slug = forms.CharField(widget=forms.HiddenInput(),required=False)
class Meta:
model = Category
fields = ('name',)
Instead of setting initial like you have here
views = forms.IntegerField(widget=forms.HiddenInput(),initial=0)
Just use this
views = forms.IntegerField(widget=forms.HiddenInput())
and then change this
form = PageForm(request.POST)
to this
form = PageForm(request.POST, initial={"views": "0"})
I am referring to this post.
Just exclude field 'category' in the PageForm and restart server can work.
Change your view like this,
def add_page(request):
if request.method == 'POST':
form = PageForm(request.POST)
if form.is_valid():
page = form.save(commit=False)
page.views = 0
page.save()
return redirect('index')
else:
render(request, 'rango/add_page.html', {'form':'form.errors'}
else:
form = PageForm()
context_dict = {'form':form}
return render(request,'rango/add_page.html',context_dict)
You should put commit=False, when saving the form, for adding additional data.

django unable to saved forms in database

I'm learning django and I'm trying to save the form using POST method and found its working fine, I'M not able to see the saved message in database(form is not submitted)
Models.py
class Post(models.Model):
title = models.CharField(max_length=200)
description = models.TextField(max_length=10000)
pub_date = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=200, unique=True)
def __unicode__(self):
return self.title
def description_as_list(self):
return self.description.split('\n')
class Comment(models.Model):
title = models.ForeignKey(Post)
comments = models.CharField(max_length=200)
def __unicode__(self):
return '%s' % (self.title)
Forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'description')
editPostedForm = modelformset_factory(Post, PostForm)
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('comments',)
exclude = ('title',)
Views.py
def detail(request, id):
posts = Post.objects.get(id=id)
comments = posts.comment_set.all()
forms = CommentForm
if request.method == 'POST':
form = CommentForm(request.POST, instance=posts)
print form
if form.is_valid():
form.save(commit=False)
form.save()
else:
print form.errors
else:
form = PostForm()
return render(request, "detail_post.html", {'forms':forms,'posts': posts,'comments':comments})
Why is the post message is not being saved. I got status code 200 in console, also i get the entered data, but the form is not being saved...Any help is much appreciated
I think the problem is that your form excludes title field, but it's required according to Comment definition. You need to give the title to comment instance then save it:
def detail(request, id):
posts = Post.objects.get(id=id)
comments = posts.comment_set.all()
forms = CommentForm
if request.method == 'POST':
form = CommentForm(request.POST,instance=posts)
print form
if form.is_valid():
# create a comment instance in memory first
comment = form.save(commit=False)
# fill out the title field
comment.title = posts
comment.save()
else:
print form.errors
else:
form = PostForm()
return render(request, "detail_post.html", {'forms':forms,'posts': posts,'comments':comments})
Also, I don't know why you use plural form for one instance, like posts should be post because you use objects.get(), make your code more readable would save some confusion for other people.

How to get value from Django authentication forms and use that value as input to another form?

I have a form used to create new posts in a blog, and one of the fields in that form is the owner, which means who is posting it, but that should be taken from login view when user provides his/her login information I don't want the user selecting who is the owner from a list, it should be automatically populated and fixed with his username. I tried different ways, fixing the value on my forms.py but didn't work, it doesn't recognizes the variable. This is my forms.py:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username', 'email', 'password')
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('website', 'picture')
class CreatePostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'body','datposted', 'category','owner')
Here is what I have on models:
class Post(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
datposted = models.DateTimeField('date posted')
category = models.ForeignKey('Category')
owner = models.ForeignKey('UserProfile')
def __str__(self):
return '%s' % self.title
This is my view:
def create_post(request):
if request.method == 'POST':
form = CreatePostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.datposted = datetime.datetime.now()
#post.owner = request.user()
post.save()
return HttpResponseRedirect('/posts/')
else:
return HttpResponse("Favor. Verifique os campos necessarios")
else:
form = CreatePostForm()
f = {'form' : form}
return render(request,'create_post.html',f)
Could you please help with more details?
I think you just have to remove the 'owner' field from the form and make sure that you handle populating the owner in your view
class CreatePostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'body','datposted', 'category')
view
def create_post(request):
if request.method == 'POST':
form = CreatePostForm(request.POST)
if form.is_valid():
Posts = form.save(commit=False)
# replace below with however you get user profile
Posts.owner = get_user_profile_from_logged_in_user()
Posts.save()
return HttpResponseRedirect('/posts/')
else:
return HttpResponse("Favor. Verifique os campos necessarios")
else:
form = CreatePostForm()
f = {'form' : form}
return render(request,'create_post.html',f)
This case is covered in django docs:
https://docs.djangoproject.com/en/1.7/topics/forms/modelforms/#the-save-method

Categories