I am trying to give a user the option to change his/her first/last name through a ModelForm. When I press submit, I get hit with the UNIQUE constraint failed: auth_user.username error. Here are my codes:
students/forms.py:
class EditProfileForm(UserChangeForm):
def clean_password(self):
# Overriding the default method because I dont want user to change
# password
pass
class Meta:
model = User
fields = (
'first_name',
'last_name',
)
students/views.py:
User = get_user_model()
def student_profile_view(request, slug):
if request.method == 'GET':
# forms
edit_name_form = EditProfileForm(instance=request.user)
context = {
'edit_name_form': edit_name_form,
}
return render(request, "students/profile.html", context)
class ChangeNameView(SuccessMessageMixin, UpdateView):
template_name = 'students/edit_profile.html'
model = User
form_class = EditProfileForm
success_message = "Your name has been updated"
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
form.instance.student_profile = StudentProfile.objects.get(slug=request.user.student_profile.slug)
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
"""If the form is valid, save the associated model."""
form.instance.username = self.request.user
self.object = form.save(commit=False)
return super().form_valid(form)
def get_success_url(self):
return reverse('students:student_profile_view', kwargs={'slug': self.object.student_profile.slug})
also fyi, User model is foreign key with StudentProfile.
students/models.py:
class StudentProfile(models.Model):
user = models.OneToOneField(User, related_name='student_profile', on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
avatar = models.ImageField(upload_to='student_profile/', null=True, blank=True)
description = models.CharField(max_length=120, null=True, blank=True)
objects = models.Manager()
def __str__(self):
return self.user.username
def get_absolute_url(self):
return reverse("students:student_profile_view", kwargs={"slug": self.slug})
I am pretty new to class based view so maybe I'm doing something wrong there?
I assume you do not have the user within the form so you need the form
def get_context_data (self, *args, **kwargs)
ctx = super().get_context_data(*args, **kwargs)
if self.request.method == 'POST':
ctx['form'] = EditProfileForm(instance=self.request.user)
and remove def form_valid()
Related
As a newbie in Django, I'm sure there is something obvious I'm not seeing. I have a user model with a one to one relationship to a userprofile model, where I'm storing the profile photo. I mixed DetailView and Formview because I want the user to go to his details page and update just the photo, but somehow its not working for me. I know I could do the job with UpdateView, but for didactic purposes, can anyone tell me why this is not working? I'm trying to updated the model fields in the form_valid method but this is not working, they just remain with the old values. I thought at the beginning it was the photo that could not be updated because of some errors on my side, but I've tried also updating other string fields and it doesnt work. Here the code: (the commented out fields are the places where I tried updating several model fields using get_object_or_404 and other functions)
class UserDetail(FormMixin, DetailView):
template_name = "users/user_detail.html"
model = User
form_class = forms.UserPhotoForm
def get_success_url(self):
return reverse('users:user_detail', args=[str(self.get_object().pk)])
def get_context_data(self, **kwargs):
user = self.get_object()
form = forms.UserPhotoForm(instance=user)
context = super().get_context_data(**kwargs)
context['user_rating'] = CotizacionReview.objects.filter(cotizacion__user=self.get_object()).aggregate(Avg('nota'))
context['form'] = form
return context
def form_valid(self, form):
form.save()
return super(UserDetail, self).form_valid(form)
def post(self, request, *args, **kwargs):
a = get_object_or_404(User, pk=self.get_object().id).userprofile
form = forms.UserPhotoForm(request.FILES['avatar'], instance=a)
# get_object_or_404(User, pk=self.get_object().id).apellido = '1234'
if form.is_valid():
# print(get_object_or_404(User, pk=self.get_object().id).userprofile.avatar)
# I tried updating several model fields here, but didnt work
# print(request.FILES['avatar'])
return self.form_valid(form)
else:
return self.form_invalid(form)
Here the model:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='profile_pics', default='profile_pics/default-user-icon-4.jpg', blank=True)
telefono = models.CharField(max_length=12, blank=True)
nombre = models.CharField(max_length=64, blank=True)
apellido = models.CharField(max_length=64, blank=True)
link = models.CharField(max_length=256, blank=True)
educacion = models.CharField(max_length=256, blank=True)
experiencia = models.TextField(max_length=512, blank=True)
birthdate = models.DateField(blank=True, null=True)
#receiver(post_save, sender=User)
def update_profile_signal(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
So I achieved it by using commit=False in my form_valid method:
class UserDetail(FormMixin, DetailView):
template_name = "users/user_detail.html"
model = User
form_class = forms.UserPhotoForm
def get_success_url(self):
return reverse('users:user_detail', args=[str(self.get_object().pk)])
def get_context_data(self, **kwargs):
user = self.get_object()
form = forms.UserPhotoForm(instance=user)
context = super().get_context_data(**kwargs)
context['user_rating'] = CotizacionReview.objects.filter(cotizacion__user=self.get_object()).aggregate(Avg('nota'))
context['form'] = form
return context
def form_valid(self, form):
user_instance = form.save(commit=False)
user_instance.avatar = form.cleaned_data['avatar']
user_instance.id = self.get_object().userprofile.id
user_instance.save(update_fields=['avatar'])
return super(UserDetail, self).form_valid(form)
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
But now I have another problem. Every time I update the photo, a new photo is saved to the database. Is there a way of doing this and deleting the old photo? or replacing it?
I'm looking to auto-fill a field when I submit a form so it is identical to another field.
I have a createView with a modelForm. The idea is the user writes his/hers name, which then when submitted fills out the display_name field as well. I know it's weird "why have two of the same", but it has a purpose.
Here's my code - I'm guessing I need to create a context that gets the display_name and sets it to be equal to the name?
views.py
class CreateIdentity(CreateView):
template_name = 'admin/CreateIdentity.html'
model = Identity
form_class = CreateIdentityForm
queryset = Identity.objects.all()
def get_context_data(self, **kwargs):
context = super(CreateIdentity, self).get_context_data(**kwargs)
context["user_id"] = User.objects.get(username=self.request.user)
return context
def form_valid(self, form):
form.instance.user_id = self.request.user
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("view_identity_created_with_slug", kwargs={'slug': self.object.slug})
models.py
class Identity(models.Model):
name = models.CharField(max_length=200, null=False)
display_name = models.CharField(max_length=200)
slug = models.SlugField(max_length=100)
user_id = models.ForeignKey(User, db_column="username", on_delete=models.CASCADE, null=False)
forms.py
class CreateIdentityForm(forms.ModelForm):
name = forms.CharField(required=True, widget=forms.TextInput(attrs={
'spellcheck': "false",
"class": "identity_name_input",
'placeholder': "Write your identity"}),
)
class Meta:
model = Identity
fields = ["name"]
def __str__(self):
return self.name
In the .form_valid(…) [Django-doc], you can specify the display_name based on the cleaned data of the form:
class CreateIdentity(CreateView):
template_name = 'admin/CreateIdentity.html'
model = Identity
form_class = CreateIdentityForm
queryset = Identity.objects.all()
def get_context_data(self, **kwargs):
context = super(CreateIdentity, self).get_context_data(**kwargs)
context["user_id"] = User.objects.get(username=self.request.user)
return context
def form_valid(self, form):
form.instance.user_id = self.request.user
form.instance.display_name = form.cleaned_data['name']
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("view_identity_created_with_slug", kwargs={'slug': self.object.slug})
I'm developing the web site where users can register and edit their information. I would like to use multi form in the single page. I can develop register form using CreateView. But when I use UpdateVIew, I don't know how to add initial value in multi form.
My code(view.py) is below. I can add form data as initial value, but not form2. Please tell me how to add form2 data as initial value.
▪️view.py
class UserUpdate(OnlyYouMixin, generic.UpdateView):
model = Profile
form_class = ProfileForm
second_form_class = ProfileNearStationForm
template_name = 'accounts/user_form.html'
def get_context_data(self, **kwargs):
context = super(UserUpdate, self).get_context_data(**kwargs)
if 'form' not in context:
context['form'] = self.form_class(self.request.GET, instance=self.request.user)
if 'form2' not in context:
context['form2'] = self.second_form_class(self.request.GET, instance=self.request.user)
return context
def get(self, request, *args, **kwargs):
super(UserUpdate, self).get(request, *args, **kwargs)
form = self.form_class
form2 = self.second_form_class
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.form_class(request.POST, instance=self.request.user)
form2 = self.second_form_class(request.POST, instance=self.request.user)
if form.is_valid() and form2.is_valid():
profile = form.save(commit=False)
profile.save()
station = form2.save(commit=False)
station.user = user
station.save()
messages.success(self.request, 'Settings saved successfully')
return HttpResponseRedirect(self.get_success_url())
else:
return self.render_to_response(
self.get_context_data(form=form, form2=form2))
def get_success_url(self):
return resolve_url('accounts:user_form', pk=self.kwargs['pk'])
▪️form.py
class ProfileForm(forms.ModelForm):
name = forms.CharField(required=True)
tel_number = forms.CharField(required=True)
class Meta:
model = Profile
fields = ('name', 'tel_number')
class ProfileNearStationForm(forms.ModelForm):
prefecture = forms.ModelChoiceField(queryset=areas_model.Prefecture.objects.all(), required=True)
railway = forms.ModelChoiceField(queryset=areas_model.Railway.objects.none(), required=True)
station = forms.ModelChoiceField(queryset=areas_model.Station.objects.none(), required=True)
memo = forms.CharField(required=False)
class Meta:
model = ProfileNearStation
fields = ('prefecture', 'railway', 'station', 'memo')
▪️model.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
tel_number = models.CharField(max_length=13)
class ProfileNearStation(models.Model):
prefecture = models.ForeignKey(areas_model.Prefecture, on_delete=models.CASCADE)
railway = models.ForeignKey(areas_model.Railway, on_delete=models.CASCADE)
station = models.ForeignKey(areas_model.Station, on_delete=models.CASCADE)
memo = models.CharField(max_length=255, null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
I have a model Course that has the following attr:
class Course(models.Model):
user= models.ForeignKey(
User,
on_delete=models.CASCADE,
)
# email= models.EmailField(default=user.email)
courseName= models.CharField(max_length=20, blank=False)
class Meta:
unique_together= ('user','courseName',)
def __str__(self):
return self.courseName
I have created a form where I want the user to enter just the courseName and after they POST it, I will add the requested user in the model as well.
This is my form which is getting passed on to the template via my ListView
forms.py
class CourseForm(forms.ModelForm):
class Meta:
model= Course
fields = ['courseName']
**Here is my views.py where I am struggling with **
class CoursesListView(ListView, FormMixin):
model = Course
form_class = CourseForm
template_name = "userApp/courseList.html"
def get_queryset(self):
return Course.objects.filter(user__exact=self.request.user)
def get_context_data(self,*args,**kwargs):
context= super(CoursesListView,self).get_context_data(*args, **kwargs)
context['courseForm'] = self.form_class
return context
def post(self,request, *args, **kwargs):
form = self.form_class(request.POST)
user = User.objects.get(username__exact=self.request.user)
**I want to add the user to my model.user field here**
return self.get(redirect, *args, **kwargs)
def get(self,request, *args, **kwargs):
self.object=None
self.form = self.get_form(self.form_class)
return ListView.get(self, request, *args, **kwargs)
So basically my question is how can I add the user in my model before calling form.is_valid().
something like this?
def post(self,request, *args, **kwargs):
form_data = copy.copy(request.POST)
form_data['user'] = User.objects.get(username__exact=self.request.user).pk
form = self.form_class(form_data)
# form handling follows
return self.get(redirect, *args, **kwargs)
This Answer was suggested by a user who deleted this answer. Didnt get his user id but whoever you were thanks a lot for the help!!!
Just use form.save(commit=False) and then make the necessary changes.
def post(self,request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
user = User.objects.get(username__exact=self.request.user)
instance = form.save(commit=False)
instance.user = user
instance.save()
Ok so I have a a generic CreateView with a couple of inlineformset_factory additions. Similar to the examples here:
Django class-based CreateView and UpdateView with multiple inline formsets
One is generating a new record on save with the parent model as expected, however a second which only contains boolean fields is not. I am not generating my own model form for either inline formset but using the default. As soon as i add a charfield field to the offending model the save works and i can see the new record, with only booleans fields in that inline formset (and model) i get nothing no record and no errors.
Edit code samples>
#models.py
class Mentor(models.Model):
title = models.CharField(max_length=15, choices=SALUTATION_CHOICES, blank=True, null=True)
first_name = models.CharField(blank=False, null=False, max_length=80)
last_name = models.CharField(blank=False, null=False, max_length=80)
class MentorSetting(models.Model):
mentor = models.OneToOneField('Mentor')
mentor_status = models.BooleanField(default=True)
question_status = models.BooleanField(default=True)
#forms.py
class MentorForm(ModelForm):
class Meta:
model = Mentor
fields = ['title','first_name','last_name',]
SettingsFormSet = inlineformset_factory(Mentor, MentorSetting,
fields=('mentor_status','question_status',),
extra=1,
can_delete=True)
#views.py
class MentorCreateView(CreateView):
template_name = 'mentor/mentor_form.html'
model = Mentor
form_class = MentorForm
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
settings_form = SettingsFormSet()
return self.render_to_response(
self.get_context_data(form=form,
settings_form=settings_form))
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
settings_form = SettingsFormSet(self.request.POST)
if (form.is_valid() and settings_form.is_valid()):
return self.form_valid(form, settings_form)
else:
return self.form_invalid(form, settings_form)
def form_valid(self, form, settings_form):
self.object = form.save()
settings_form.instance = self.object
settings_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_valid(self, form, settings_form):
return self.render_to_response(
self.get_context_data(form=form,
settings_form=settings_form))
If anyone can explain why would be awesome.
mathew