Django edit user profile after success registration - django

I try to configure the project in such a way that the user goes through the primary registration, then logged in and from the personal cabinet could add additional information about himself. There are no problems with the initial registration, but when you try to change and supplement the information, there are no changes.
My UserProfile model:
models.py
from customuser.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50, blank=True)
surname = models.CharField(max_length=50, blank=True, default = None)
avatar = models.ImageField(upload_to = 'images/profile/%Y/%m/%d/', blank=True, null=True)
position = models.ForeignKey('Position',blank=True, default=None)
role = models.ForeignKey('Role', blank=True, default=None)
company = models.ForeignKey('Company',blank=True, default=None)
status = models.ForeignKey('Status', blank=True, default=None)
#receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
forms.py
from django import forms
from django.forms import ModelForm
from client.models import Profile
class UserProfileForm(ModelForm):
class Meta:
model = Profile
exclude = ['user']
views.py
from customuser.models import User
from client.models import Profile
from .forms import UserProfileForm
def edit_user(request, pk):
user = User.objects.get(pk=pk)
form = UserProfileForm(instance=user)
if request.user.is_authenticated() and request.user.id == user.id:
if request.method == "POST":
form = UserProfileForm(request.POST, request.FILES, instance=user)
if form.is_valid():
update = form.save(commit=False)
update.user = user
update.save()
return HttpResponse('Confirm')
else:
form = UserProfileForm(instance=user)
return render(request, 'client/edit.html', {'form': form})

UserProfileForm is form for Profile model, so instance passed to the form should be profile, not user. You shoud do something like this:
def edit_user(request, pk):
user = User.objects.get(pk=pk)
profile = user.profile
form = UserProfileForm(instance=profile)
if request.user.is_authenticated() and request.user.id == user.id:
if request.method == "POST":
form = UserProfileForm(request.POST, request.FILES, instance=profile)

Related

ModelForm inserts number in foreign key field

I have model from which I created a ModelForm:
models.py:
class City(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return f'{self.name}'
class Profile(models.Profile):
name = models.CharField(max_length=50)
user = models.OneToOneField(User, on_delete=models.CASCADE, unique=False)
location = models.ForeignKey('City', on_delete=models.SET_NULL, blank=True, null=True)
forms.py
from django import forms
from .models import Profile, City
class LocationField(forms.CharField):
def clean(self, value):
try:
city = City.objects.get(name=value)
except ObjectDoesNotExist:
city = City.objects.create(name=value)
return city
class ProfileForm(forms.ModelForm):
location = LocationField()
class Meta:
model = Profile
exclude = ['user']
views.py
def profile_update_view(request):
template_name = 'profiles/update.html'
user = request.user
profile = Profile.objects.get(user__id=user.id)
if request.method == 'GET':
form = ProfileForm(instance=profile)
else:
form = ProfileForm(request.POST, instance=profile)
if form.is_valid():
obj = form.save(commit=False)
obj.user = user
obj.save()
return redirect('profile_view')
context = {'form': form}
return render(request, template_name, context=context)
When I'm saving form, I'm satisfied how it's working, but when I load form again to update in, it fills LocationField() as an City pk integer, but I want it to load name instead. Is there a way to do this?
I've added in views.py:
if request.method == 'GET':
initial = {}
if profile.location:
initial = {'location': profile.location.name}
form = ProfileForm(instance=profile, initial=initial)
now it's working. But it's some workaround. I've thought there is some parameter maybe

Cross queries in django

I have two models as below
class Watched(Stamping):
user = models.ForeignKey("User", null=True, blank=True, on_delete=models.CASCADE,
default=None)
count = models.PositiveIntegerField()
class Link(Stamping):
...
user = models.ForeignKey(User, on_delete=models.CASCADE, default=None)
url = models.CharField(max_length=256, default=None)
watched = models.ForeignKey(Watched, null=True, blank=True, on_delete=models.CASCADE, default=None)
...
My forms.py
class SimpleLink(forms.Form):
url = forms.URLField(max_length=256)
A user can create a Link object and when some conditions are met, the object will be added to Watched. The Watched model contains objects created by different users.
Now I want to filter the Watched class and grab only the objects created by the requesting user in the Link model but I don't know how I can achieve that. Any help will be appreciated.
A sample of what I want to achieve is...
Watched.objects.filter(Link.objects.filter(user=request.user). I know my sample is crazy. But from the outside query, I want to grab the Link objects created by user making the request
You need to limit the queryset in your ModelForm. A ModelForm will thus look like:
from django import forms
class LinkForm(forms.ModelForm):
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user is not None:
self.fields['watched'].queryset = Watched.objects.filter(
link__user=user
)
class Meta:
model = Link
fields = ['url', 'watched']
In our view, we can then set the user object:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
#login_required
def some_view(request):
if request.method == 'POST':
form = LinkForm(request.POST, user=request.user)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('name-of-some-form')
else:
form = LinkForm(user=request.user)
return render(request, 'some-template.html', {'form': form})
For a class-based view, we can override the .get_form_kwargs(…) method [Djangod-doc]:
from django.contrib.auth.mixins import LoginRequiredMixin
class SomeView(LoginRequiredMixin, CreateView):
form_class = LinkForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)

IntegrityError at * NOT NULL constraint failed: main_post.owner_id

I want to create a PostModel(just like instagram) and while the form is created to connect the user to the model with One-to-one/foreign key relationship, anyway I'm getting a problem while trying to upload an image and the db doesn't updates.
I've tried this solution
...
# models.py
from django.contrib.auth.models import User
from django.conf import settings
class Post(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
description = models.CharField(max_length=255, blank=True)
image = models.ImageField(upload_to='images')
uploaded_at = models.DateTimeField(auto_now_add=True)
...
# forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('description', 'image', )
def save(self, commit=True):
if commit:
Post.save()
return Post
...
# views.py
def account(request):
post = PostForm(request.POST, request.FILES)
if request.method == "POST":
if post.is_valid():
post.save(commit=False)
post.owner = request.user
post.save(commit=True)
messages.success(request, f"you had successfully updated your profile image")
return redirect("main:account")
else:
for msg in form.error_messages:
messages.error(request, f"{msg}: {form.error_messages[msg]}")
return render(request = request,
template_name = "main/account.html",
context={'PostForm':post})
post = PostForm()
return render(request = request,
template_name = "main/account.html",
context={'PostForm':post})
You should not override the def save() method, this is fine as it is now, so:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('description', 'image', )
# no save
as for the view, you need to add the owner to the object, but here you are adding it to the form, and that thus has no effect (on the object):
from django.contrib.auth.decorators import login_required
#login_required
def account(request):
post = PostForm(request.POST, request.FILES)
if request.method == 'POST':
if post.is_valid():
post.instance.owner = request.user
post.save()
messages.success(request, f'you had successfully updated your profile image')
return redirect('main:account')
# …
I would also advise to rename post to post_form, since this is a form, not a post object.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].

Django actual value in choice field form

I have models like this:
class Projects(models.Model):
project = models.CharField(max_length=150, blank=True, null=True)
def __str__(self):
return self.project
class Profile(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
project = models.ForeignKey(Projects, on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return self.user.username
I also made a form in which I want to change current Project assigned Profile:
class ProjectForm(ModelForm):
class Meta:
model = Profile
fields = [
'project'
]
My view looks like this:
def change_project(request, user):
user = User.objects.filter(username=user)[:1].get()
profile = Profile.objects.get(user=user)
form = ProjectForm(request.POST, instance=profile)
if request.method == 'POST':
if form.is_valid():
form.save()
context = {'form': form}
return render(request, 'datafiller/change_project.html', context)
I can change the project using this form, but every time I want to do it again the form looks like this
How can I show in the form the current project instead of "------"?
You should not ground the ProjectForm in case you render the form with a GET requuest:
from django.shortcuts import get_object_or_404
def change_project(request, user):
profile = get_object_or_404(Profile, user__username=user)
if request.method == 'POST':
form = ProjectForm(request.POST, instance=profile)
if form.is_valid():
form.save()
else:
form = ProjectForm(instance=profile)
return render(request, 'datafiller/change_project.html', {'form': form})

Django: Extended User Model Cant Save

I have extended the django user model with another model called profile:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
chosenCharity = models.ForeignKey('meta.Charity', db_column='chosenCharityid', related_name='user_chosenCharity')
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
When I try to create a new user within the view with the code below i get the following error "(1048, "Column 'chosenCharityid' cannot be null")":
#transaction.atomic
def register(request):
selectedTeams = StraightredTeam.objects.filter(Q(teamid=request.session['team1id']) | Q(teamid=request.session['team2id'])).order_by('teamname')
request.POST.get('currentCharities')
next_url = request.POST.get('next', request.GET.get('next', reverse('straightred.payment')))
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
new_user = form.save()
I know when a user has already created I should be able to use:
user = User.objects.get(pk=user_id)
user.profile.chosenCharity = 12
user.save()
But I am unsure how to do this when creating the user. Any help would be appreciated.
Below is a copy of the registration form to help:
class RegistrationForm(BootstrapModelForm, UserCreationForm):
email_opt_in = forms.BooleanField(label='Receive DWAD e-mail updates', required=False)
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
# The default Django user model doesn't require these fields to be set
# but we do.
self.fields['email'].required = True
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email__iexact=email).exists():
raise ValidationError('There is already an account registered with this e-mail address.')
return email
class Meta:
model = User
fields = ['first_name', 'last_name', 'email', 'username']
Charity Model:
class Charity(models.Model):
name = models.CharField(max_length=50, unique=True)
website = models.URLField()
enabled = models.BooleanField(default=True)
def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
verbose_name_plural = 'charities'
don't create the Profile object in post_save signal. You cannot access the required charity id in create_user_profile method. So remove that part of code.
instead save the profile object right after you save your user object in your register view like this:
#transaction.atomic
def register(request):
selectedTeams = StraightredTeam.objects.filter(Q(teamid=request.session['team1id']) | Q(teamid=request.session['team2id'])).order_by('teamname')
request.POST.get('currentCharities')
next_url = request.POST.get('next', request.GET.get('next', reverse('straightred.payment')))
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
new_user = form.save()
charity_id = request.session['chosenCharityid']
# create profile object
Profile.objects.create(user=new_user, chosenCharity_id=charity_id)
EDIT:
I see that you are using another method save_user_profile to receive the post_save signal.
Don't use this either. It a round about way of doing a simple straight forward thing.
Using you own code sample:
user = User.objects.get(pk=user_id)
# here you are assigning a new charity id to the profile object
user.profile.chosenCharity = 12
# you save the user object on which nothing has changed
# instead you should save the profile object
user.save() # no need
user.profile.save() # direct and logical