ModelForm doesn´t validate - django

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>

Related

DJANGO: forms dont show error to user (def post + ListView)

can you help me?
I can't fix problem: my don't show error validation
when I write not unique slug at form -> no error at form
I think problem at use def post() or return redirect after validations form.
I try many different solutions but nothing helps.
Maybe you should use a non-standard way to report an error?
models.py
class ShortUrl(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='Автор URL', null=True)
url = models.CharField('Ссылка', max_length=200)
slug = models.SlugField('Короткое имя ссылки', unique=True, max_length=20)
def __str__(self):
#return self.slug
return f"Короткая ссылка: {self.user} >> {self.slug}"
​
class Meta:
verbose_name = 'Ссылка'
verbose_name_plural = 'Ссылки
forms.py
class ShortURLForm(forms.ModelForm):
slug = forms.SlugField(
label='Название URL',
required=True,
widget=forms.TextInput(attrs={'placeholder': 'Укажите уникальный URL'})
)
url = forms.CharField(
label='Ссылка',
required=True,
widget=forms.TextInput(attrs={'placeholder': 'Ссылка которую нужно сократить'})​
)
class Meta:
model = ShortUrl
fields = ['user', 'url', 'slug']
widgets = {'user': forms.HiddenInput()}
views.py
class ShortURLPage(LoginRequiredMixin, ListView):
model = ShortUrl
template_name = 'main/shorts.html'
context_object_name = 'shorts'
​
def get_context_data(self, *, object_list=None, **kwargs):
ctx = super(ShortURLPage, self).get_context_data(**kwargs)
ctx['form'] = ShortURLForm()
userurls = ShortUrl.objects.filter(user=self.request.user)
ctx['shorts'] = userurls
ctx['title'] = 'Добавление ссылок'
return ctx
​
def post(self, request, *args, **kwargs):
post = request.POST.copy()
post['user'] = request.user
request.POST = post
form = ShortURLForm(request.POST)
​
if form.is_valid():
slug = form.cleaned_data['slug']
url = form.cleaned_data['url']
form.save()
​
return redirect('shorts')
shorts.html
<form method="post" class="form">
{% csrf_token %}
{{ form }}
<button class="button" type="submit">Создать ссылку</button>
</form>
urls.py
urlpatterns = [
path('', views.homepage, name='home'),
path('about/', views.about, name='about'),
path('shorts/', views.ShortURLPage.as_view(), name='shorts'),
path('shorts/<str:slug>/', views.urlRedirect, name='redirect'),
]
Ok, you're not so far away with accomplishing what you want.
Generally your post method should look like this:
def post(self, request, *args, **kwargs):
post = request.POST.copy()
post['user'] = request.user
request.POST = post
form = ShortURLForm(request.POST)
​
if form.is_valid():
slug = form.cleaned_data['slug']
url = form.cleaned_data['url']
form.save()
else:
context = {
'form': form,
}
return render(
request,
self.template_name,
context,
)
return redirect('shorts')
Then, you should write your shorts.html template like this:
<form method="post" class="form">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.errors }}
{{ form }}
<button class="button" type="submit">Создать ссылку</button>
</form>

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.

How to pass more than one parameters from views.py to forms and get them in forms.py and add to HiddenInput fields in django 2.1.7?s

I am new to Django and I am trying to add comments for a post in a blog in Django 2.1. When adding comments to a post, I need to save the user_id of logged in user and post_id of that comment. I defined my comment model with relations to post and user models as below:
class Comments(models.Model):
comment = models.CharField(max_length=120, null=True, )
name = models.CharField(max_length=120, null=True)
created_by = models.ForeignKey(User, related_name='created_by', null=True, on_delete=models.PROTECT)
post_name = models.ForeignKey(Post, related_name='post_name', null=True, on_delete=models.PROTECT)
def __str__(self):
return self.comment
There is the template code where I use CommentForm and get the data comments from the user:
{% if user.is_authenticated %}
<form method="POST" action=" ">
<div class="form-group"></div>
{% csrf_token %}
{{ form.as_p }}
<input type='submit' class="btn btn-success" />
</form>
{% else %}
Add Comment
{% endif %}
I used __init__() method in CommentForm to get the post_id and user_id. This method works fine for first parameter 'post' and gets post_id and saves it, but not is working for getting user_id 'create'. I want to get the session user_id and pass it from views.py to forms.py and save it with comment creation same as getting post_id. Is it possible to pass more than one parameter from views.py and pass it to form and get them in that form to save to that model?
Here is the code which I wrote for doing above functionality:
veiws.py
My method for creating comments:
def details(request, id):
instance = get_object_or_404(Post, id=id)
if request.method == 'GET':
form = CommentForm(request.POST or None, request.FILES or None)
if request.method == 'POST':
form = CommentForm(request.POST or None, request.FILES or None, post=instance.id) # get and pass post_id of related post
if request.method == 'POST':
if request.user:
user = request.user.id
form = CommentForm(request.POST or None, request.FILES or None, create=user) # Wants to get the session user id and pass it to forms.py
forms.py
In my CommentForm I have following method:
def __init__(self, args, *kwargs):
post = kwargs.pop('post', '')
super(CommentForm, self).__init__(*args, **kwargs)
if post:
self.fields['post_name'] = forms.ModelChoiceField(queryset=Post.objects.filter(pk=post))
create = kwargs.pop('create', '')
print(kwargs)
super(CommentForm, self).__init__(*args, **kwargs)
if create:
self.fields['created_by'] = forms.ModelChoiceField(queryset=Comments.objects.filter(created_by=create))
Above code works for first parameter 'post' but not for second parameter 'create' and give the "name 'create' is not defined" error.

Django forms.ModelMultipleChoiceField not saving anything

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)

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.