update a Model field in DetailView - django

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?

Related

IntegrityError, NOT NULL constraint failed: contest_contestant.contest_id while updating record

I Have two models Contest and Contestant, users can start a contest, and other users can register as a contestant under any contest…
models.py
class Contest(models.Model):
contest_title = models.CharField(max_length=30)
contest_post = models.CharField(max_length=100)
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
class Contestant(models.Model):
contest = models.ForeignKey(Contest, on_delete=models.CASCADE, related_name='participating_contest')
contestant_name = models.CharField(max_length=10, blank=True, null=True)
votes = models.IntegerField(default=0)
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
Fans of any contestant can cast an unlimited amount of vote for their favourite contestant, anytime a fan votes i want to increment the Contestant.votes with the amount of votes being casted, i have tried using FBV but it is too complicated for my level of django, i am now using CBV, but i am getting an IntegrityError saying IntegrityError at /contests/contestant/2/ NOT NULL constraint failed: contest_contestant.contest_id
this error doesn’t make sense to me because contest_id has already being assigned to contestant upon creation, and i can even confirm it from the database
view.py
class ContestantCreateView(CreateView):
model = Contestant
fields = ['contestant_name']
def form_valid(self, form):
form.instance.contest_id = self.kwargs.get('pk')
return super().form_valid(form)
the voting logic is in side contestant detailView, which is being triggered by a POST request from a form
views.py
class ContestantDetail(DetailView, FormMixin):
model = Contestant
context_object_name = 'contestants'
template_name = 'contest/contestant_detail.html'
form_class = VoteForm
def get_success_url(self):
return reverse('contest:contestant-detail', kwargs={'pk': self.object.pk})
def get_context_data(self, *args, **kwargs):
context = super(ContestantDetail, self).get_context_data(*args, **kwargs)
context['vote_contestant'] = Contestant.objects.get(pk=self.kwargs.get('pk'))
return context
def post(self, request, *args, **kwargs):
form = self.get_form()
self.object = self.get_object()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form, *args, **kwargs):
contestant = Contestant.objects.get(pk=self.kwargs['pk'])
vote_count = form.cleaned_data['votes']
contestant.votes += vote_count
form.save()
messages.success(self.request, f'You have successfully casted your vote.')
return super().form_valid(form)
forms.py
class VoteForm(forms.ModelForm):
class Meta:
model = Contestant
fields = ['votes']
html form template
<form action="" method="post">
{% csrf_token %}
{% include 'contest/bs4_form.html' with form=form %}
<button type="submit">Vote Now</button>
</form>
below is my urls.py
path('contestant/<int:pk>/new/', views.ContestantCreateView.as_view(), name='new-contestant'),
path('contestant/<int:pk>/edit/', views.ContestantUpdateView.as_view(), name='edit-contestant'),
path('contestant/<int:pk>/', views.ContestantDetail.as_view(), name='contestant-detail'),
By making use of form.save() you are saving the form, since you did not pass an instance to the form, that means that form.save() will try to create a new record. You thus should omit form.save() from the form_valid(…) method:
from django.db.models import F
def form_valid(self, form, *args, **kwargs):
contestant = Contestant.objects.get(pk=self.kwargs['pk'])
vote_count = form.cleaned_data['votes']
contestant.votes = F('votes') + vote_count
contestant.save()
# no form.save()
messages.success(self.request, f'You have successfully casted your vote.')
return super().form_valid(form)

UNIQUE constraint failed: auth_user.username while updating user info

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()

Django - UpdateView with multi model forms - one submit

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)

How do I get my Django generic view to work

Am I going about this the right way? Having never used 'generic views' I am tying to use Django's generic.UpdateView view. When I 'hit' the 'update' button on the form, I get an invalid form response with message 'Library with this Slide name already exists'
Grateful for any help.
View:
class Slideview(generic.UpdateView):
model = Library
template_name = 'app1/slide_update.html'
fields = ['slide_name', 'reference_value','esd',
'current_mean', 'counts_averaged', 'status']
context_object_name = 'qc_slide'
#def get_queryset(self):
#slide_id = self.kwargs['pk']
#return Library.objects.filter(slide_name=slide_id)
def get_success_url(self):
return reverse('Slideview', args=[self.kwargs['pk']])
def get_context_data(self, **kwargs):
context = super(Slideview, self).get_context_data(**kwargs)
#form = self.get_form(self.get_form_class())
#context['form'] = form
return context
def post(self, request, *args, **kwargs):
print("Im in post")
form = self.get_form(self.get_form_class())
if form.is_valid():
#Code will go here which will query a second model
#perform a series of math calculations and then
#return the updated information
self.object = self.get_object()
self.object.save()
return self.form_valid(form)
else:
print("Form not valid")
self.object = self.get_object()
return self.form_invalid(form)
Model:
class Library(models.Model):
slide_name = models.CharField(max_length=5, primary_key=True)
reference_value = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal(0))
esd = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal(0))
current_mean = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal(0))
counts_averaged = models.IntegerField(default=0)
status = models.CharField(max_length=9)
def __str__(self):
return self.slide_name

CreateView with inline form with boolean only fields failing to create

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