Django forms.ModelMultipleChoiceField not saving anything - django

I have had so many problems with forms.ModelMultipleChoiceField and widget = forms.SelectMultiple I'm hoping someone can put me out of my misery.
I'm using the JQuery Chosen multiple select box (similar to Select2) to render out the interest field in the ProfileUpdateForm on the font-end.
At first the request.POST was not executing at all, the submit button wasn't clickable and I got the error An invalid form control with name='interest' is not focusable which I have now patched by adding
def __init__(self, *args, **kwargs):
super(ProfileUpdateForm, self,).__init__(*args, **kwargs)
self.fields['interest'].required = False
to my ProfileUpdateForm.
Although I can now click the submit button and see
[2019/02/18 08:26:53] HTTP POST /profile 302
and all other form information updates ...nothing happens to the forms.ModelMultipleChoiceField interest field.
If you select an interest then press submit, the SelectMultiple field is empty on page refresh and nothing is stored / saved. I've checked the admin and nothing has updated in that section.
What is the best way to proceed to diagnose the problem?
forms.py
class ProfileUpdateForm(forms.ModelForm):
interest = forms.ModelMultipleChoiceField(
queryset=Interest.objects.all(),
widget = forms.SelectMultiple(attrs={
'name': "interest",
'data-placeholder': "Choose your interests",
'class': 'chosen-select',
'multiple tabindex': '4',
}))
def __init__(self, *args, **kwargs):
super(ProfileUpdateForm, self,).__init__(*args, **kwargs)
self.fields['interest'].required = False
class Meta:
model = Profile
fields = ['interest', 'age', 'bio']
views.py
def profile_view(request):
if request.method == 'POST':
p_form = ProfileUpdateForm(request.POST,
request.FILES,
instance=request.user.profile)
if p_form.is_valid():
p_form.save()
messages.success(request, f'Your account has been updated!')
return redirect('profile')
else:
p_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'p_form': p_form,
}
return render(request, 'users/profile.html', context)
profile.html
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<!-- edit Profile model info -->
{{ p_form|crispy }}
<button type="submit" value="submit">Update Profile</button>
</form>
<script>
$(".chosen-select").chosen();
$('button').click(function() {
$(".chosen-select").val('').trigger("chosen:updated");
});
</script>
models.py
class Interest(models.Model):
interest_name = models.CharField(max_length=30)
class Meta:
ordering = ['interest_name']
def __str__(self):
return self.interest_name
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
interest = models.ManyToManyField(Interest)
bio = models.CharField(max_length=200)

Related

upload image in forms Django

I am trying to upload image from form but whenever I submit everything got saved in database other than image field.But when I try to do samething from admin panel it works.
models.py
class Post(models.Model):
title = models.CharField(("Title"), max_length=100)
title_image = models.ImageField(
("Title Image"),
upload_to='static/Images/TitleImages/',
max_length=None,
blank = True,null = True)
Forms.py
class AddPostForm(ModelForm):
class Meta:
model = Post
fields = ['title','title_image']
Views.py
class AddPostView(LoginRequiredMixin,CreateView):
model = Post
template_name = 'MainSite/add_post.html'
fields = '__all__'
def dispatch(self, request, *args, **kwargs):
if request.user.is_anonymous:
messages.error(request,"You need to login to access this page")
return redirect('/')
elif request.user.is_superuser:
if request.method == "POST":
form = AddPostForm(request.POST)
if form.is_valid():
form.save()
messages.success(request,"POST added successfully")
return redirect('/')
else:
print("error")
else:
print("method is not post")
form = AddPostForm()
return render(request,'MainSite/add_post.html',{'form':form})
else :
messages.error(request,"You need to have superuser permission to access this page")
return redirect('/')
addpost.html
<form action= "" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form|crispy}}
<button class="btn btn-primary profile-button" style = "width:150px;"type="submit" >Add Post</button></div>
</form>
my model have 2 things title and title_image but whenever I submit only title is saved and when I do through admin panel it works.
I dont know what I am doing wrong here any advice will be helpful.
Thanks in advance
You've to pass request.FILES in order to save files
if request.method == "POST":
form = AddPostForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.success(request,"POST added successfully")
return redirect('/')

Django form's not valid

This question might be asked alot in stackoverflow but i couldn't find the answer.
Take a look at code:
# models.py
class Message(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.CharField(max_length=200, null=True, blank=True)
room = models.ForeignKey(Room, on_delete=models.CASCADE, blank=True, null=True)
posted = models.TimeField(auto_now_add=True)
def __str__(self):
return self.body
views.py:
class RoomInsideView(View):
template_name = 'room/room_inside.html'
form_class = SendMessageForm
room = None
def get(self, request, room_id, room_slug):
self.room = Room.objects.get(id=room_id)
if self.room.is_private:
return redirect('room:private_room_auth', self.room.id)
form = self.form_class()
context = {
'room': self.room,
'form': form,
}
return render(request, self.template_name, context)
def post(self, request, room_id, room_slug):
form = self.form_class(request.POST)
if form.is_valid():
new_msg = Message(body=form.cleaned_data['body'])
new_msg.user = request.user in
all_messages = Message.objects.filter(room=self.room)
messages.error(request, 'form not valid', 'warning')
return render(request, self.template_name, {'form': form, 'message': all_messages})
forms.py:
class SendMessageForm(forms.ModelForm):
class Meta:
model = Message
fields = ('body',)
widgets = {
'body': forms.TextInput(attrs={'class': 'form-control',
'placeholder': 'Send'}),
}
template:
<form method="post" action="" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.body.errors }}
{{ form.body }}
<input type="submit" value="Send" class="btn btn-primary">
</form>
as I added a messages.error if form is not valid it's returning form not valid and I can't find where am I doing wrong
You always add the warning, regardless whether the form is valid or not, this does not make much sense.
That being said, you are writing too much boilerplate code, you can use a CreateView which will eliminate most of the boilerplate code:
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
class RoomInsideView(View):
template_name = 'room/room_inside.html'
form_class = SendMessageForm
success_url = reverse_lazy('name-of-some-view')
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['room'] = get_object_or_404(Room, pk=self.kwargs['room_id'], is_private=False)
return context
def form_invalid(self, form):
messages.error(request, 'form not valid', 'warning')
return super().form_invalid(form)
def form_valid(self, form):
form.instance.room_id = self.kwargs['room_id']
form.instance.user = self.request.user
return super().form_valid(form)
The name-of-some-view should be replaced with the name of the view where the view should redirect to in case of a successful POST request, this is done to implement the Post/Redirect/Get architectural pattern [wiki].
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

ModelForm doesn´t validate

I just cant make my modelform to validate. I call it from view, and GET prints it right, but when POST occurs it doesn´t validate.
Allways getting
ValueError Exception Value: The view
gestionPartesMedicos.views.partes_medicos_add didn't return an
HttpResponse object. It returned None instead.
form´s name attributes correspond to model´s and form´s.
---UPDATED---
This my model:
class MedicalList(models.Model):
worker= models.ForeignKey(worker, on_delete=models.CASCADE)
description=models.CharField(max_length=255)
upload=models.FileField(upload_to=user_directory_path, null=False, blank=False)
created_at=models.DateTimeField(auto_now_add=True)
this my form class:
class MedicalListForm(forms.ModelForm):
worker = forms.ModelChoiceField(
queryset=Worker.objects.none(),
empty_label=None,
widget=forms.Select(attrs={'class': 'form-control'})
)
description=forms.CharField(
widget=forms.Textarea(attrs={'class': 'form-control'})
)
upload=forms.FileField(
widget=forms.ClearableFileInput(attrs={'class': 'form-control'})
)
class Meta:
model = MedicalList
fields = (
'worker',
'description',
'upload',
)
def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user_id', None)
super().__init__(*args, **kwargs)
self.fields['worker'].queryset = Worker.objects.filter(user_id=user_id)
And this my view in trouble:
def medical_list_add(request):
if request.method == "POST":
form = MedicalListForm(request.POST,request.FILES,user_id=request.user)
if form.is_valid():
form.save()
return redirect('medical_list')
else:
form = MedicalListForm(user_id=request.user)
return render(request, 'medical_list_add.html', {'form': form})
The form in template:
<form method="POST">
{% csrf_token %}
<div class="form-group">
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Guardar</button>
<a class="nav-item linking" href = "{% url 'medical_list' %}">Cancel</a>
</div>
</form>
this is the response I get:
Request information USER 12345
GET No GET data
POST Variable Value csrfmiddlewaretoken
'2zG3amQlZlPsrytMtF91ZiJQDZ679E2Zgrx3YxcOPzcNj6dNCl101Lj0UV96STLY'
worker '14' description 'pm' upload 'medical.pdf'
Might it be around Model field created_at? just trying to guess, totally lost.
thanks in advance
The main problem is that your view does not return a HTTP response in case the form was invalid. You should unindent the render(…) call with:
def medical_list_add(request):
if request.method == "POST":
form = MedicalListForm(request.POST,request.FILES,user_id=request.user)
if form.is_valid():
form.save()
return redirect('medical_list')
else:
form = MedicalListForm(user_id=request.user)
# &downarrow;&downarrow; both for GET and a failed POST
return render(request, 'medical_list_add.html', {'form': form})
Furthermore the form fields are specified at class level. By constructing a form field in the __init__ method, this will not use that
class MedicalListForm(forms.ModelForm):
worker = forms.ModelChoiceField(
queryset=Trabajador.objects.none(),
empty_label=None,
widget=forms.Select(attrs={'class': 'form-control'})
)
description=forms.CharField(
widget=forms.Textarea(attrs={'class': 'form-control'})
)
upload=forms.FileField(
widget=forms.ClearableFileInput(attrs={'class': 'form-control'})
)
class Meta:
model = MedicalList
fields = (
'worker',
'description',
'upload',
)
def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user_id', None)
super().__init__(*args, **kwargs)
self.fields['worker'].queryset = worker.objects.filter(user_id=user_id)
If your form handles files, you should set the enctype=… parameter to multipart/form-data:
<form method="POST" enctype="multipart/form-data">
…
</form>

How do I update a CustomUser value via UpdateView using a hidden logic

I've spent several hours on this and I'm not able to see any signs as to why the change on the flag is not getting through.
Please note the change form already works for all exposed fields, i.e. the user can go in and change the name or country already and it will get saved after clicking on update profile.
What I'm now trying to do is to also change the confirmed_email flag to True (but without telling or letting the user see it) whenever the client makes an update to the form.
To do this I check if the user was logged in using Linkedin (or any Social account for that matter) via something along the lines of ""if user.social_auth.exists()"". That said, it's not that i can't fulfill this function, it's that even when i use a silly condition that i know it's true the field "email_confirmed" will still NOT change to True in the back-end.
Thanks so much in advance. I appreciate your time.
PS. I'm a noob with Django but loving it.
Models.py
class CustomUser(AbstractUser):
id = models.BigAutoField(primary_key=True)
email = models.EmailField(unique=True)
email_confirmed = models.BooleanField(default=False)
country = models.CharField(max_length=30,choices=COUNTRY, null=True, blank=False)
first_name = models.CharField(max_length=50, null=False, blank=False, default="")
last_name = models.CharField(max_length=50, null=False, blank=False, default="")
Views.py
class SignUpView(CreateView):
form_class = CustomUserCreationForm
success_url = reverse_lazy('home')
template_name = 'signup.html'
...
class UpdateProfileView(UpdateView):
form_class = CustomUserChangeForm
success_url = reverse_lazy('home')
template_name = 'update_profile.html'
def get_object(self, queryset=None):
return self.request.user
Forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = ('first_name', 'last_name','country',)
class CustomUserChangeForm(UserChangeForm):
password = None
class Meta:
model = CustomUser
fields = ('first_name', 'last_name','country',)
update_profile.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %}Home{% endblock title %}
{% block content %}
{% if user.is_authenticated %}
<h2>Update Profile</h2>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-success" type="submit">Update</button>
</form>
{% else %}
<p>You are not logged in</p>
Log In |
Sign Up
{% endif %}
{% endblock content %}
My main attempt was to adding another leg in the view (see def ChangeActiveStatus below).
class UpdateProfileView(UpdateView):
form_class = CustomUserChangeForm
success_url = reverse_lazy('home')
template_name = 'update_profile.html'
def get_object(self, queryset=None):
return self.request.user
def ChangeActiveStatus(request):
if request.method == "POST":
form = self.form_class(request.POST)
user = form.save(commit=False)
if form.is_valid() and user.social_auth.exists() == True:
user.email_confirmed = True
form.save()
else:
form = form()
return render(request, 'login', {'form':form})
The issue here is that you're setting the email_confirmed = True on user and not form.instance. You could also save the user instance rather than calling form.save().
form = self.form_class()
if request.method == "POST":
form = self.form_class(request.POST)
if form.is_valid() and user.social_auth.exists():
user = form.save(commit=False)
user.email_confirmed = True
user.save()
return render(request, 'login', {'form':form})
Or
form = self.form_class()
if request.method == "POST":
form = self.form_class(request.POST)
if form.is_valid() and user.social_auth.exists():
form.instance.email_confirmed = True
form.save()
return render(request, 'login', {'form':form})
I ended up implementing a solution via models.py instead, basically bypassing the need to save any changes on one of the fields through the views, i.e. this is the logic, which will be triggered every time the client changes something in their profile.
def save(self, *args, **kwargs):
try:
CustomUser.objects.latest('id').id
except:
...
...
if self.is_active == True and self.email_verified == False:
self.email_verified = True
super(CustomUser, self).save(*args, **kwargs)
def __str__(self):
return self.email

MultipleChoiceField form not displaying user's instance of form

I've implemented a MultipleChoiceField form with a CheckboxSelectMultiple. It works perfectly in that the form is displayed and user selected options are saved to the BaseServicesOffered model as desired. The problem is that when the user goes back to the form, the checkboxes that the user had previously selected/submitted are not selected -- they are all unchecked. I'd imagine that it's a problem with my views.py. Here is my code:
models.py
class BaseServicesOffered(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
service = models.CharField(max_length=255, default='', null=True, blank=True)
def __str__(self):
return self.user.username
forms.py
class BaseServicesOfferedForm(forms.ModelForm):
service = forms.MultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user') #this takes in the value of 'user', which is passed from the view function.
super(BaseServicesOfferedForm, self).__init__(*args, **kwargs)
self.fields['service'].choices = [(t.id, t.service) for t in AllServices.objects.filter(industrycode=user.userprofile.industry)]
class Meta:
exclude = ('user',)
model = BaseServicesOffered
views.py
#login_required(login_url="/accounts/login/")
def baseservicesoffered(request):
try:
base_services_offered = BaseServicesOffered.objects.create(user=request.user)
except:
pass
user = request.user
instance = get_object_or_404(BaseServicesOffered, user=user)
form = BaseServicesOfferedForm(request.POST or None, user=request.user, instance=instance)
if request.method == 'POST':
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
service = form.cleaned_data['service']
services = [int(i) for i in service]
instance.service = services
instance.save()
return redirect('/accounts/profile/')
else:
context = {'form': form}
return render(request, 'accounts/setup8.html', context)
context = {'form': form}
return render(request, 'accounts/setup8.html', context)
setup8.html
<form id="post_form" method="post" action="" enctype="multipart/form-data">
{{ form.non_field_errors }}
{% csrf_token %}
{{ form.as_p }}
<div class="submitbutton">
<button type="submit">
SUBMIT
</button>
</div>
</form>
Update:
This is how you store a list of ints in the Charfield:
service = form.cleaned_data['service']
services = [int(i) for i in service] #converts list of strings to list of ints
instance.service = services
I've updated my code above with this.
multiple_choice = forms.MultipleChoiceField(
label=u"Select multiple",
choices=MY_CHOICES,
widget=forms.widgets.CheckboxSelectMultiple,
initial=(c[0] for c in MY_CHOICES)
)
You should set the inital parameter for the options to be checked for a particular user.