Field 'id' expected a number but got '' - django

I am trying to display all the posts by a particular user in their profile page.. So if someone visits my profile page, they can see all of my posts and so on.
all_post_by_user = Log.objects.filter(author=username)
I am getting the post by a particular username.. author is defined in models.py
username is passed as an argument to the view..
I am getting the error
views.py:
#verified_email_required
#login_required
def profile(request, username):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=request.user)
p_form = ProfileUpdateForm(request.POST, request.FILES, instance=request.user.profile)
if u_form.is_valid and p_form.is_valid():
u_form.save()
p_form.save()
message = messages.success(request, f'Your profile has been updated')
return redirect('profile', username=username)
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
try:
profile = User.objects.get(username=username)
except User.DoesNotExist:
message = messages.warning(request,f'Profile not found for {username}')
return redirect('home')
profile = ''
print('profile name: ',profile.username)
all_post_by_user = Log.objects.filter(author=username)
print(all_post_by_user)
context = {
'u_form' : u_form,
'p_form' : p_form,
'profile' : profile,
'all_post_by_user' : all_post_by_user
}
return render(request, 'users/profile.html', context)
It works if I changed it to all_post_by_user = Log.objects.filter(author=request.user)
models.py:
class Log(models.Model):
title = models.CharField(blank=False, max_length=500)
content = models.TextField(blank=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
slug = models.SlugField(max_length=50, null=False, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE,null=True, blank=True)
image = models.ImageField(
upload_to='images', blank=True)

The problem with your queryset is you're trying to query against a related model's primary key but passing the author's name instead of their ID.
When you use author=<value> , under the hood Django is going to take this as author_id=<value>. If you want to filter the queryset using a field on the Author model, you need to use __ like so:
all_post_by_user = Log.objects.filter(author__name=username)
Now, when Django compiles the query you'll be filtering against the author's name rather than against the primary key.

You should filter with:
all_post_by_user = Log.objects.filter(author__username=username)
since we look for Log objects where the username of the author is the same as the given username.

Related

Django how to save value to model without being present on form post

I have the following Model/form/view:
Model
class Account(models.Model):
username = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=150)
identifier_type = models.ForeignKey(IdentifierType, on_delete=models.SET_NULL, null=True)
actflag = models.CharField(max_length=1, blank=True)
created_date = models.DateTimeField(blank=True, null=True)
comments = models.TextField(_(
'comments'), max_length=500, blank=True)
priority_type = models.ForeignKey(PriorityType, on_delete=models.SET_NULL, null=True)
deadline_date = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.name
Form
class PortfolioForm(forms.ModelForm):
portfolio = forms.CharField(widget=forms.Textarea)
class Meta:
model = Account
fields = ['name', 'comments', 'priority_type', 'deadline_date', 'identifier_type', 'portfolio']
View
def portfolios(request):
if request.user.is_authenticated:
if request.POST:
fm = PortfolioForm(request.POST)
user = User.objects.get(username=request.user)
if fm.is_valid():
messages.success(request, 'Portfolio has been created.')
fm.save()
return redirect('portfolios')
else:
fm = PortfolioForm()
context = {"name": request.user, "form": fm}
return render(request, 'portfolios.html', context)
else:
return redirect('login')
The form works fine with posting via my template, however you will notice there are some fields within my model that are not in my form I would like to fill in automatically without the user having to fill in - for example username field I would like this to be current user that submits the form and also created_date would like the current date time the user has submitted the form.
I tried to add the following to my view under if fm.is_valid(): attempting to save username as current user to the model but did not work:
Account.objects.username = request.user
How can I go about doing this? Thanks in advance
You can save these values after creating the Account object when you save the form. If you use the commit=False parameter in the save method, this does not hit the database and you can easy modify the Account object.
from django.utils import timezone
def portfolios(request):
if request.user.is_authenticated:
if request.POST:
fm = PortfolioForm(request.POST)
# user = User.objects.get(username=request.user)
if fm.is_valid():
account = fm.save(commit=False)
account.username = request.user
account.created_date = timezone.now()
account.save()
messages.success(request, 'Portfolio has been created.')
return redirect('portfolios')
else:
fm = PortfolioForm()
context = {"name": request.user, "form": fm}
return render(request, 'portfolios.html', context)
else:
return redirect('login')
You can use django forms instance for saving any predefined value without showing or render those fields to your users or html template. Here is an example how to automatically save your username and created_date fields .
if fm.is_valid():
fm = fm.save(commit=False)
fm.instance.username = request.user
fm.instance.created_date = timezone.now()
fm.save()

Django: Create profile page creates everything except Multiple Choice Field in the database

I am using the same form for profile_edit and create_profile functionality. It is updating the multi-choice values in the profile_edit page but does not create in create_profile.
Below is the form code in forms.py
class ProfileForm(ModelForm):
full_name = forms.CharField(required=True)
current_position = forms.CharField(required=True)
about_me = forms.Textarea(attrs={'required':True})
topic_name = forms.ModelMultipleChoiceField(Topic.objects.all())
class Meta:
model = Profile
fields =(
"full_name",
"current_position",
"about_me",
"topic_name",
)
Below is the views.py for profile creation
def create_profile(request, user_id):
if request.method == "POST":
form = ProfileForm(request.POST)
if form.is_valid():
form = form.save(commit=False)
user = get_object_or_404(User, id=user_id)
form.user = user
print(form.topic_name.all()) # Prints empty queryset
form.save()
return redirect("profile_view", user_id=user_id)
else:
context = {"form": form}
return render(request, "profile/create_profile.html", context)
else:
form = ProfileForm()
context = {
"form": form
}
return render(request, "profile/create_profile.html", context)
Below is Model.py
class Topic(models.Model):
topic = models.CharField(max_length=12)
def __str__(self):
return self.topic
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True,)
full_name = models.CharField(max_length=60, null=True)
current_position = models.CharField(max_length=64, null=True)
about_me = models.TextField(max_length=255, null=True)
topic_name = models.ManyToManyField(Topic)
def __str__(self):
return self.full_name
Both create_profile and edit_profile templates are exactly the same.
It saves everything except Multichoice field.
When you do save(commit=False),
you need to use mymodelform.save_m2m() below save(commit=True) on your ModelForm,
because many to many relationships cannot be saved without an ID.
see this docs
so in your views.py
if form.is_valid():
profile = form.save(commit=False)
user = get_object_or_404(User, id=user_id)
profile.user = user
profile.save()
form.save_m2m()
return redirect("profile_view", user_id=user_id)

Django - cannot assign - must be an instance

I tried phrasing this question recently and got totally confused. I've extended the default user model with this in my models.py:
class Biography(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
biography = models.TextField(max_length=500, blank=True,default='Details')
I've included this in the forms.py:
class EditProfileForm(forms.Form):
first_name = forms.CharField(label='First Name')
last_name = forms.CharField(label='Last Name')
biography = forms.CharField(label='Biography', widget=Textarea(attrs={'rows': 5}))
I have a view to edit the profile and want to add "biography" to it, but have absolutely no clue where to start. Here's the view:
def edit_profile(request):
user = request.user
products = Product.objects.filter(user=user)
form = EditProfileForm(request.POST or None, initial={'first_name':user.first_name, 'last_name':user.last_name})
if request.method == 'POST':
if form.is_valid():
user.first_name = request.POST['first_name']
user.last_name = request.POST['last_name']
user.save()
return render(request, 'profile.html', {'user':user, 'products':products})
context = {"form": form}
return render(request, "edit_profile.html", context)
I tried to replicate what is already there with this:
def edit_profile(request):
user = request.user
products = Product.objects.filter(user=user)
biography = Biography(user=user)
form = EditProfileForm(request.POST or None, initial={'first_name':user.first_name, 'last_name':user.last_name, 'biography':user.biography})
if request.method == 'POST':
if form.is_valid():
user.first_name = request.POST['first_name']
user.last_name = request.POST['last_name']
user.biography = request.POST['biography']
user.save()
return render(request, 'profile.html', {'user':user, 'products':products})
context = {"form": form}
return render(request, "edit_profile.html", context)
I definitely missed the point somehow. The last time I asked this question I was somewhat chastised for not knowing how to solve it. In all honesty I'm really new to Django and am amazed I got this far, but I'm stuck. I 'think' I need to create an instance but am not sure how.
What you should do differently:
Create Biography instance if it does not exist, or get from db.
Instantiate separate forms for different request methods
Use cleaned_data as input validation is one of the main purposes of forms
Always redirect after POST
Save User and Biography instances separately
And you don't need related products in form view. If only you are not going to somehow update them here.
For example:
def edit_profile(request):
user = request.user
biography, created = Biography.objects.get_or_create(user=user)
form = EditProfileForm(initial={
'first_name': user.first_name,
'last_name': user.last_name,
'biography': biography.biography
})
if request.method == 'POST':
form = EditProfileForm(data=request.POST)
if form.is_valid():
user.first_name = form.cleaned_data['first_name'] # use cleaned_data
user.last_name = form.cleaned_data['last_name']
biography.biography = form.cleaned_data['biography']
biography.save() # save Biography object
user.save() # save User object
return redirect(biography) # always redirect after successful POST. In this case Biography must have get_absolute_url() method
context = {'form': form}
return render(request, 'edit_profile.html', context)
Read more in documentation.
user.biography is an instance of Biography model, so what you should do here is get that instance and edit its attributes, like this:
bio = user.biography
bio.biography = request.POST['biography']
bio.save()

Django - Cannot assign "'Biography test'": "User.biography" must be a "Biography" instance

I have extended the user model with an extra field - biography. It appears in the admin panel as a new section. Here's a picture:
Here's the new model:
class Biography(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
biography = models.TextField(max_length=500, blank=True)
Here's the profile view:
def profile(request, username):
user = get_object_or_404(User, username=username)
products = Product.objects.filter(user=user)
if not request.user == user:
return render(request, 'no.html')
else:
return render(request, 'profile.html', {'user':user,'products': products})
I'm using a form to edit the profile - here's the view:
def edit_profile(request):
user = request.user
products = Product.objects.filter(user=user)
form = EditProfileForm(request.POST or None, initial={'first_name':user.first_name, 'last_name':user.last_name, 'biography':user.biography})
if request.method == 'POST':
if form.is_valid():
user.first_name = request.POST['first_name']
user.last_name = request.POST['last_name']
user.biography = request.POST['biography']
user.save()
return render(request, 'profile.html', {'user':user, 'products':products})
context = {"form": form}
return render(request, "edit_profile.html", context)
...and here's the form:
class EditProfileForm(forms.Form):
first_name = forms.CharField(label='First Name')
last_name = forms.CharField(label='Last Name')
biography = forms.CharField(label='Biography', widget=Textarea(attrs={'rows': 5}))
Here's a screenshot of the error message:
I'm mixing something up but I can't figure out what. Doesn't help that I'm new to this ...still trying!
As the error message says:
"User.Biography" must be a "Biography" instance.
In your edit_profile definition, you have the following assignment:
user.biography = request.POST['biography']
request.POST['biography'] is not a valid instance of Biography. So, you have to create a valid Biography instance, according to your Biography model, with the request.POST['biography'].
After that, you can assign your valid instance to user.biography.
I hope it had been useful for you.

Django - User creates User and Profile in same form

Hi have changed my view from this:
How to make User able to create own account with OneToOne link to Profile - Django
For some reason the profile_form is not saving the 'user' information to the database. I have checked the information in the print(profile_form.user) data and does show the username in the terminal. It is not however saving this foreign key to the database, it just leaves it Null.
To this:
Views.py
class IndexView(View):
template_name = 'homepage.html'
form = UserCreationForm
profile_form = ProfileForm
def post(self, request):
user_form = self.form(request.POST)
profile_form = self.profile_form(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
profile_form.save(commit=False)
profile_form.user = user
print(profile_form.user)
print(profile_form)
profile_form.save()
return render(request, self.template_name)
else:
return render(request, self.template_name, {'user_form': self.form,
'profile_form': self.profile_form})
def get(self, request):
if self.request.user.is_authenticated():
return render(request, self.template_name)
else:
return render(request, self.template_name, {'user_form': self.form, 'profile_form': self.profile_form})
Forms.py
class ProfileForm(ModelForm):
"""
A form used to create the profile details of a user.
"""
class Meta:
model = Profile
fields = ['organisation', 'occupation', 'location', 'bio']
Models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
organisation = models.CharField(max_length=100, blank=True)
occupation = models.CharField(max_length=100, blank=True)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
The user is an attribute of the instance, not the form.
user = user_form.save()
profile = profile_form.save(commit=False)
profile.user = user
print(profile.user)
profile.save()