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.
Related
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.
To complete registration, I want users to complete secondary form. However secondary form is not submitting. I think user is not getting authenticated in the registration and then the secondary form is not submitting. The login() seems to not work.
# the form in this view that's not submitting
def agreements(request):
if request.method == "POST":
form = AgreementsForm(request.POST)
if form.is_valid():
user = request.user
agree = form.save(commit=False)
agree.save()
else:
raise ValidationError("Form is not valid. Try Again.")
else:
form = AgreementsForm()
return render(request, 'agree.html', {'form': form})
Here is the forms.py for the agreements:
class AgreementsForm(forms.ModelForm):
non_ent=forms.BooleanField(label='kdmkl kdldsk')
agreement1=forms.BooleanField(label='dmklsd. lkdfmld')
class Meta:
model = Agreements
fields = ('non_ent', 'agreement1')
def save(self, commit=True):
agree = super(AgreementsForm, self).save(commit=False)
agree.non_ent = self.cleaned_data['non_ent']
agree.agreement1 = self.cleaned_data['agreement1']
if commit:
agree.save()
return agree
Here is the initial registration view:
# register view which submits, but I think it's not authenticating the user
def registration(request):
if request.method == "POST":
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.is_active = True
user.save()
login(request, user, backend='django.contrib.auth.backends.ModelBackend')
return redirect('agreements_page')
else:
raise ValidationError("Form is not valid. Try Again.")
else:
form = CustomUserCreationForm()
return render(request, 'register.html', {'form': form})
Agreements Model:
class Agreements(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,blank=True, null=True)
non_ent = models.BooleanField(default=False, blank=True, null=True)
agreement1 = models.BooleanField(default=False, blank=True, null=True)
date = models.DateTimeField(default=datetime.now, blank=True, null=True)
def __str__(self):
return f'{self.user} ({self.date})'
You need to authenticate user first to login :
def registration(request):
if request.method == "POST":
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.is_active = True
user.save()
# authenticate user first
user = authenticate(request,username=form.cleaned_data['username'],password=form.cleaned_data['password'])
if user:
login(request, user)
return redirect('agreements_page')
Then in your secondary form you can save request.user like this .
I hope you have a OneToOne relation with user in your Agreement model.
form = AgreementsForm(request.POST)
if form.is_valid():
agree = form.save(commit=False)
agree.user = request.user
agree.save()
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)
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()
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()