Updating "about me" field - django

I'm having a great deal of difficulty trying to submit a quick form to update a user's "About Me" section. It's an optional field, users can leave it blank if they wish. I can't figure it out!
models.py:
class UserProfile(models.Model):
user = models.OneToOneField(User)
activation_key = models.CharField(max_length=40, blank=True)
key_expires = models.DateField(default=datetime.date.today())
about_me = models.CharField(max_length=500, blank=True, null=True, default='')
portfolio_site = models.URLField(max_length=200, blank=True, null=True, default='')
def __str__(self):
return self.user.username
forms.py:
class UserForm(forms.Form):
class Meta:
model = User
fields = ['first_name', 'last_name', 'password', 'email', 'username']
class ProfileForm(forms.Form):
class Meta:
model = UserProfile
fields = ['about_me', 'portfolio_site']
views.py:
#login_required(login_url='sign_in')
def update_about(request, user_id):
# Accquire submitted data and place to "data"
data = request.POST
# Isolate submitted data under "id_about_me", and place it to "about_me"
about_me = data.get('id_about_me')
new_about = UserProfile(id=request.user.id, about_me=about_me)
new_about.save()
return HttpResponse('Great Job!')
If I use "user_id=request.user.id", then it says:
IntegrityError at /update_about/1/
UNIQUE constraint failed: register_userprofile.user_id
If I use "id=request.user.id", then it says:
IntegrityError at /update_about/1/
NOT NULL constraint failed: register_userprofile.user_id
I can handle other updates just fine, but this one has me stumped!

I think it's because you're creating a new instance of your UserProfile model and assigning the same user_id to it, leading to the Unique constraint error. You should first retrieve your already existing model and modify it like so:
new_about = UserProfile.objects.get(user_id=user_request_id)
new_about.about_me = about_me
new_about.save()
Tell me if this works. If user_id is your auto-primary-key field, though, this shouldn't be an issue.

You need to create, or fetch a profile if it already exists:
profile,created = UserProfile.objects.get_or_create(user=request.user)
profile.about_me = about_me
profile.save()
But why don't you use the form?
from django.shortcuts import redirect, render
from .forms import ProfileForm
#login_required(login_url='sign_in')
def update_about(request, user_id):
form = ProfileForm(request.POST or None)
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
return redirect('/')
return render(request, 'update_profile.html', {'form': form})
Your template just has the normal form rendering logic:
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="submit">
</form>

Related

profile shape not partially saved

After the first registration, it transfers to a new form - you can enter a profile email, photo, bio, links to twitter, github and other social networks, 2 models participate: ProfileUser and User. When a person wrote what he wanted and pressed the button, after the transition he clicked on the user and then he was thrown to the profile page, but there, apart from the mail, nothing from the previously entered is shown, when trying to change the profile, nothing is shown either, there is nothing in the form, I went in the admin panel in the model, only the user is also set automatically when switching to creating a profile after registration, other data is simply not entered, please tell me how to fix it
The user appears to me because this is in the registration
profile = ProfileUser.objects.create(user=new_user)
I tried to redo it according to this article did not work
models.py
class ProfileUser(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
img = models.ImageField(null=True, blank=True, upload_to='static/images/', validators=[img__size])
birthday = models.DateField(blank=True, null=True)
about = models.TextField(max_length=1000, null=True)
#? social media
twitter = models.URLField(null=True, blank=True)
facebook = models.URLField(null=True, blank=True)
instagram = models.URLField(null=True, blank=True)
telegram = models.URLField(null=True, blank=True)
vk = models.URLField(null=True, blank=True)
reddit = models.URLField(null=True, blank=True)
github = models.URLField(null=True, blank=True)
def __str__(self):
return 'Profile for user {}'.format(self.user.username)
forms.py
class UserEditForm(ModelForm):
class Meta:
model = User
fields = ['email']
class ProfileEditForm(ModelForm):
class Meta:
model = ProfileUser
fields = ['birthday', 'img', 'about', 'twitter', 'facebook','instagram', 'telegram','vk','reddit', 'github']
views.py
Create Profile:
def createProfile(request):
UserForm = UserEditForm()
ProfileForm = ProfileEditForm()
if request.method == 'POST':
print(request.POST)
UserForm = UserEditForm(request.POST)
ProfileForm = ProfileEditForm(request.POST)
if ProfileForm.is_valid():
UserForm.save()
ProfileForm.save()
return redirect('base:home')
context = {'UserForm': UserForm,'ProfileForm': ProfileForm}
return render(request, 'base/profileForm.html', context)
Update Profile:
#login_required(login_url='base:login')
def updateProfile(request,pk):
user = User.objects.get(id=pk)
UserForm = UserEditForm(instance=user)
ProfileForm = ProfileEditForm(instance=user)
if request.method == 'POST':
ProfileForm = ProfileEditForm(request.POST,instance=user)
UserForm = UserEditForm(request.POST,instance=user)
if UserForm.is_valid():
UserForm.save()
ProfileForm.save()
return redirect('base:home')
context = {'user': user,'UserForm': UserForm,'ProfileForm': ProfileForm}
return render(request, 'base/profileForm.html', context)
profileForm.html
{% block content %}
<form class="login__form" method='POST' action="." enctype="multipart/form-data">
{% csrf_token %}
{{ProfileForm.media}}
{{UserForm.as_p}}
{{ProfileForm.as_p}}
<input type="submit" class="btn btn-min" value="Сохранить">
</form>
{% endblock content %}
You will have to pass the ProfileUser instance corresponding to the user to the ProfileEditForm in updateProfile.
ProfileForm = ProfileEditForm(instance=user.profileuser)

Django Formset: how to get the current user? (using django-extra-views)

I'm used to collecting the current logged in user in a CreateView and passing it to the form like so:
class MakeFantasyTeam(CreateView):
form_class = MakeFantasyTeamForm
[...]
def form_valid(self, form):
form.instance.team_manager = self.request.user
form.save()
return super(MakeFantasyTeam, self).form_valid(form)
However, this doesn't seem to work when using an InlineFormSetView as provided by django-extra-views. I get an error NOT NULL constraint failed: tournament_invite.invited_by_id and I'm not sure how to get the user.id passed on to the form.
My View:
class InvitePlayersView(InlineFormSetView):
template_name = 'invite_players.html'
model = Tournament
inline_model = Invite
form_class = InvitePlayerForm
pk_url_kwarg = 'tourney_id'
factory_kwargs = {'can_delete': False, 'extra': 1}
def formset_valid(self, formset):
tourney_id = self.kwargs['tourney_id']
formset.instance.invited_for = Tournament.objects.filter(id=tourney_id).get()
formset.instance.invited_by = self.request.user
formset.save()
return super(InvitePlayersView, self).formset_valid(formset)
def get_success_url(self):
return reverse('make_team', kwargs={'tourney_id': self.object.invited_for.id})
My Model:
class Invite(models.Model):
name = models.CharField(max_length=200, blank=True, null=True)
email = models.CharField(max_length=320, null=False, blank=False, validators=[EmailValidator],)
invited_by = models.ForeignKey(get_user_model(), on_delete=models.DO_NOTHING)
invited_for = models.ForeignKey(Tournament, on_delete=models.DO_NOTHING)
created_dt = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.email
def get_absolute_url(self):
return reverse('home')
My Form:
class InvitePlayerForm(forms.ModelForm):
class Meta:
model = Invite
fields = ('name', 'email',)
Any tips or hints much appreciated!
Thank you,
Jon
Edit: Just to clarify what I'm trying to do here; I want a user to submit a formset. The data of that formset should be stored in the model, and the userid of the submitting user should also be stored in the model. I don't seem to be able to pass on the userid though.
I am not sure what you exactly want to do here, As per my understanding you want to use the currently logged in user's information. To do so you can append the user's info in the session dictionary. After that you can use the information in templates or in other views too.
In authentication view
def login(request):
#your necessary data
request.session['user_id']=The_user_id
request.session['user_name']=The_userName
To access data in the template
{% request.session.user_id %}
{% request.session.user_name %}
To access data in other views
def myview(request):
user_id= request.session['user_id']
user_name= request.session['user_name']

Django form data isn't saving to database

i'm trying to allow a user to update their user profile with a city, description, website address etc.
Using Django 2.0, I have two forms in one view:
EditProfileForm (EPF) = Form for email, first and last name and password
The EditProfileForm seems to be able to save data. However, EditUserProfile seems to not.
EditUserProfile (EUP) = Form for further user info such as city, description, website address etc.
When entering the data and submitting the form, the data for EUP form doesn't appear to save or update the user information
I've also tried methods such as:
if form_EUP.is_valid():
obj = form_EUP.save(commit=False)
obj.user = request.user
obj.save()
and trying to create a similar custom save method to the format used in RegistrationForm but i've had no luck
I'm a bit of a beginner to the django framework so any ideas would be much appreciated, cheers.
views.py
def edit_profile(request):
if request.method == 'POST':
form_EPF = EditProfileForm(request.POST, instance=request.user)
form_EUP = EditUserProfile(request.POST, instance=request.user)
if form_EPF.is_valid():
form_EPF.save()
return redirect(reverse('accounts:view_profile'))
if form_EUP.is_valid():
form_EUP.save()
return redirect(reverse('accounts:view_profile'))
else:
form_EPF = EditProfileForm(instance=request.user)
form_EUP = EditUserProfile(instance=request.user)
args = {'form_EPF': form_EPF, "form_EUP": form_EUP}
return render(request, 'accounts/edit_profile.html', args)
forms.py
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = (
'username',
'first_name',
'last_name',
'email',
'password1',
'password2'
)
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
class EditProfileForm(UserChangeForm):
class Meta:
model = User
fields = (
'email',
'first_name',
'last_name',
'password',
)
class EditUserProfile(ModelForm):
description = forms.CharField(required=False)
city = forms.CharField(required=False)
website = forms.URLField(required=False)
class Meta:
model = UserProfile
fields = (
'description',
'city',
'website',
'image',
)
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
description = models.CharField(max_length=100, default='')
city = models.CharField(max_length=100, default='')
website = models.URLField(default='')
phone = models.IntegerField(default=0)
image = models.ImageField(upload_to='profile_image', blank=True)
def __str__(self):
return self.user.username
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
edit_profile.html
{% block body %}
<div class="container">
<h1>Edit profile</h1>
<form method="post">
{% csrf_token %}
{{ form_EPF.as_p }}
{{ form_EUP.as_p }}
<button type="submit">Submit</button>
</form>
</div>
{% endblock %}
Here:
if form_EPF.is_valid():
form_EPF.save()
return redirect(reverse('accounts:view_profile')) # !!!!!
if form_EUP.is_valid():
form_EUP.save()
return redirect(reverse('accounts:view_profile'))
You are returning just after saving the user form, so the profile form is indeed never saved (unless the user form is invalid xD).
You want:
if form_EPF.is_valid() and form_EUP.is_valid():
form_EPF.save()
form_EUP.save()
return redirect(reverse('accounts:view_profile')) # !!!!!
Also, you have this which won't work:
form_EUP = EditUserProfile(request.POST, instance=request.user)
you're passing a ̀Userasinstance, but the form expects aUserProfile`.
And - while it won't prevent your code from running - you definitly want to think twice about your naming. "EditProfileForm" edits the User so it should really be named "EditUserForm" (or "UserEditForm" etc), and the variables in your view aren't any better - ̀form_EPFandform_EUPrequires active thinking to parse the suffix and match it with the (already counter-intuitive) form class names, and you _don't_ want to have to do any effort to understand what a name means when reading code. Just use the all_lower form of the class instead, so assumin you renamed your form classes to a much sanerUserEditFormandProfileEditForm, the variables would beuser_edit_formandprofile_edit_form`. That's a couple additional keystrokes indeed but on average you spend about 1000 times more time reading code than writing it so it's really worth optimizing for reading.

Django ImageField won't upload in function based view, but it does in the admin

I've been trying to add some user uploaded profile picture to my website. It works fine when I do it from the admin, the image is showed and all the engines seems to be working fine (image going to the correct upload location and so on). The problem is when I try to do the same thing from my view.
I noticed that the print("upload_location") only appears when I do it from the admin. The weird thing is that all the other fields in my Profile model are working fine (like name "foo" is updated to "foobar") and not only in the admin, but in the view as well. The issue is only with the ImageField.
I believe it could have something to do with the way I'm handling the form.is_valid(), but I've been playing around with that and nothing changed (I know it is working to some extend, since HttpResponseRedirect is working.
Any ideas?
views.py
...
#login_required
def profile_update(request, username=None):
obj = get_object_or_404(User, username=username)
user = obj.profile
form = ProfileForm(request.POST or None, instance = user)
context = {
"form": form
}
if form.is_valid():
form.save()
return HttpResponseRedirect('/profiles/{username}'.format(username=user.user))
template = 'profile_update.html'
return render(request, template, context)
forms.py
from django import forms
from .models import Profile
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = [
"profilePic",
"nome",
...
]
def profile(self, request, user):
print('printing forms')
user.uf = self.cleaned_data['uf']
user.cidade = self.cleaned_data['cidade']
user.telefone = self.cleaned_data['telefone']
user.save()
models.py
...
User = settings.AUTH_USER_MODEL # 'auth.User'
def upload_location(instance, filename):
print("upload_location")
return "%s/%s" %(instance.user, filename)
class Profile(models.Model):
user = models.OneToOneField(User)
id = models.AutoField(primary_key=True)
width = models.IntegerField(default=0, null=True, blank=True,)
height = models.IntegerField(default=0, null=True, blank=True,)
profilePic = models.ImageField(
upload_to = upload_location,
blank=True, null=True,
verbose_name = 'Foto de Perfil',
width_field="width",
height_field="height",
)
...
template.html
...
<form action="" method="POST" enctype="multipart/form-data">{% csrf_token %}
{{ form|crispy }}
<input type="submit" value="Enviar" class="btn btn-primary"/>
</form>
...
You need to add FILES into the form.
form = ProfileForm(request.POST or None, request.FILES or None, instance = user)
Docs: https://docs.djangoproject.com/en/1.10/topics/http/file-uploads/

Is this approach correct to create forms using multiple models?

I had to create a form from which some details go to default.auth.user model and some to my custom model so after searching from various sources I did this:
Django Version :1.7
model.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
title_id = models.ForeignKey('Title')
mobile_number = models.CharField(max_length=10)
alternate_number = models.CharField(max_length=10)
date_of_birth = models.DateField()
profession_id = models.ForeignKey('Profession', null=True, blank=True)
house_no = models.CharField(max_length=100, blank=True, default='NA')
city_id = models.ForeignKey('City', null=True)
country_id = models.ForeignKey('Country', null=True)
state_id = models.ForeignKey('State', null=True)
locality_id = models.ForeignKey('Locality', null=True)
profile_picture_path = models.CharField(max_length=100, blank=True, default='NA')
forms.py:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(attrs={'id': 'password'}))
email = forms.CharField(widget=forms.TextInput(attrs={'id': 'email_id'}))
username = forms.CharField(widget=forms.TextInput(attrs={'id': 'username'}))
first_name = forms.CharField(widget=forms.TextInput(attrs={'id': 'first_name'}))
last_name = forms.CharField(widget=forms.TextInput(attrs={'id': 'last_name'}))
class Meta:
model = User
fields = ('email', 'username', 'first_name', 'last_name', 'password')
class ExtraDetailsForm(UserForm):
confirm_password = forms.CharField(widget=forms.PasswordInput(attrs=
{'id':'confirm_password'}),max_length=32,
required=True,)
class Meta:
model = UserProfile
fields = ('email', 'username', 'title_id', 'first_name', 'last_name',
'password', 'confirm_password',
'date_of_birth', 'mobile_number', )
My view.py is :
def register(request):
# A boolean vakue for telling whether the registration was successful
registered = False
if request.method == 'POST':
user_form = UserForm(data=request.POST)
additional_details_form = ExtraDetailsForm(data=request.POST)
if user_form.is_valid() and additional_details_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
additional_details = additional_details_form.save(commit=False)
additional_details.user = user
additional_details.save()
registered = True
else:
print(user_form.errors, additional_details_form.errors)
else:
user_form = UserForm
additional_details_form = ExtraDetailsForm
return render(request,
'users/register.html',
{'user_form' : user_form, 'additional_details_form': additional_details_form, 'registerered': registered})
regsiter.html:
{% if registerered %}
<p>Thank you for register. check ur email , entered email was</p>
{% else %}
<form action="/users/register/" method="post">{% csrf_token %}
{{ additional_details_form.as_p }}
<input type="submit" value="Register" />
</form>
{% endif %}
Now the good thing is that everything is working fine and details are being stored as they should be.
But the bad thing is I do not know whether it is a correct approach or not as I did not find any tutorial/resource where this approach is used?
This is correct approach and you do it almost right. Just couple notes:
if user_form.is_valid() and additional_details_form.is_valid():
In this line if user_form is invalid then validation for additional_details_form will not run. To always validate both change it to:
if all([user_form.is_valid(), additional_details_form.is_valid()]):
In else statement you set form class to *_form variables. It should be form instances instead:
user_form = UserForm()
additional_details_form = ExtraDetailsForm()
And it may be a good idea to wrap your save code into one transaction :-)
I would recommend that you use just one form here that contains all fields.
There is no benefit to using two forms, especially since one inherits the other, this is odd behaviour when you are then passing the POST data into each of them.
Consolidate the fields into a single form and then override the 'clean' method of the form to be able to check that the two password fields match.
You can create a single form to save data into one or many different models and this is especially useful in your case since you need to validate the data for these different models together.
Ok, firstly ExtraDetailsForm shouldn't inherit from UserForm because they are for different models. It should look something like this instead:
class UserForm(forms.ModelForm):
confirm_password = forms.CharField(widget=forms.PasswordInput(attrs=
{'id':'confirm_password'}),max_length=32,
required=True,)
class Meta:
model = User
fields = ('email', 'username', 'first_name', 'last_name', 'password',
'confirm_password')
class ExtraDetailsForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('title_id', 'date_of_birth', 'mobile_number')
Then in your view:
from django.contrib.auth import login
from django.shortcuts import redirect, render
def register(request):
user_form = UserForm(data=request.POST or None)
profile_form = ExtraDetailsForm(data=request.POST or None)
if all([user_form.is_valid(), profile_form.is_valid()]):
user = user_form.save(commit=False)
user.set_password(user.password)
user.save()
profile = profile_form.save()
# probably at this point you want to login the new user:
login(request, user)
# it's good practice to do a redirect here, after a successful
# form post, eg to display success page, as this will
# prevent accidental re-posting data if user reloads the page
return redirect('registration_success')
else:
print(user_form.errors, profile_form.errors)
return render(
request,
'users/register.html',
{
'user_form' : user_form,
'profile_form' : profile_form,
}
)
def registration_success(request):
return render('registration_success.html')
Finally you need to output both forms in the template:
<form action="/users/register/" method="post">{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<input type="submit" value="Register" />
</form>
and a new template registration_success.html:
<p>Thank you for registering. Check your email, entered email was: {{ request.user.email }}</p>