Django dropdown form submission invalid - django

Hey i am trying to use modelchoicefield to get a dropdown list in html. But the submission of form yields a invalid form. My code is given below.
views.py
class SubjectSelectFormView(View):
form_class = SubjectSelectForm
template_name = 'study/select_subject.html'
def get(self, request):
form = self.form_class(user=request.user)
return render(request, self.template_name, {'form':form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
subject = models.Subject.objects.get(name=form['name'])
return HttpResponseRedirect('study:quiz', subject.subject_id)
else:
return HttpResponse('<h1>Failed</h1>')
forms.py
class SubjectSelectForm(forms.Form):
name = forms.ModelChoiceField(queryset=Subject.objects.all().order_by('name'), widget=forms.Select())
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(SubjectSelectForm,self).__init__(*args, **kwargs)
self.fields['name'].queryset = Subject.objects.filter(user_id=user)
html
{% extends 'basic_home_app/base.html' %}
{% block content %}
<br>
<form class="form-horizontal" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Start">
</form>
{% endblock %}

First you should always render the same template with the bound form when a posted form is found to not be valid, this way you can display errors to the user:
def post(self, request):
form = ...
if form.is_valid():
...
else:
return render(request, self.template_name, {'form':form})
Inside your template, you can display errors using either:
{{ form.errors }} # all form errors
{{ form.non_field_errors }} # form errors that aren't for one specific field, use this if you're displaying the field errors separately
or
{{ form.name.errors }} # just the errors for one specific field
Second, I assume you want to initialise your form the same way when it's posted as when it's first displayed (empty) to the user via the get() request:
def post(self, request):
form = self.form_class(request.POST, user=request.user) # note the user
Otherwise your form.__init__() method will set as queryset only Subject objects where user_id is None.

Related

how to add form as data in class-base views?

I used to send form as content in function-base-view and I could use for loop to simply write down fields and values like:
{% for x in field %}
<p>{{ x.label_tag }} : {{ x.value }} </p>
I don't remember whole the way so maybe I wrote it wrong but is there anyway to do this with class-based-views, because when I have many fields its really hard to write them 1by1
Not entirely sure if this is what you need. But still I will try to answer. I took an example with class AuthorDetail(FormMixin, DetailView) as a basis. In get_context_data saved the form itself. In the template, first I displayed the form, then the value from the bbs model and requested
form.message.label_tag. To get the tag, I looked at this documentation.
In the class, replace the Rubric model with your own. In the template path: bboard/templ.html replace bboard with the name of your application where your templates are located.
views.py
class Dw(FormMixin, DetailView):
model = Rubric
template_name = 'bboard/templ.html'
form_class = TestForm
context_object_name = 'bbs'
def get_context_data(self, **kwargs):
context = super(Dw, self).get_context_data(**kwargs)
context['form'] = self.get_form()
print(77777, context['form']['message'].label_tag())
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
forms.py
class TestForm(forms.Form):
message = forms.CharField()
templates
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="adding">
</form>
<h2>{{ bbs }}</h2>
<h2>{{ form.message.label_tag }}</h2>
urls.py
urlpatterns = [
path('<int:pk>/', Dw.as_view(), name='aaa'),
]

How to render a bound Django model form as HTML-safe?

I am trying to return a bound form that has been modified and has some arbitrary text and HTML inserted into it. I have done some research and have been able to successfully insert some arbitrary text into a bound form but I haven't found any way to render the injected HTML as HTML. It renders as plain text. How can I achieve my goal?
Here is the code:
# views.py
def multi_text(request):
if request.method == 'POST':
data = request.POST.copy()
form = MultilineForm(data=data)
if form.is_valid():
cd = form.cleaned_data
form.data['text'] = '<i>Hello hello</i>'
return render(request, 'multi_text.html', {'form': form})
else:
form = MultilineForm()
return render(request, 'multi_text.html', {'form': form})
# forms.py
class MultilineForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['text'].widget.attrs.update({'class': 'form-control'}, verbose_name='Text', placeholder='Type your text here...')
self.data['text'] = '...'
class Meta:
model = Multiline
fields = ['text']
widgets = {
'text': Textarea(attrs={}),
}
# template.html
<form method="post" action="" class="form">
{% csrf_token %}
{{ form.text.as_widget }}
<span class="input-group-btn">
<input type="submit" value="Check" class="form-control btn btn-primary">
</span>
</form>

Django inline-formset with an image field not updating

I have a listing model and a photo model:
class Listing(models.Model):
title = models.CharField(max_length=255)
<more fields>...
class Photo(models.Model):
image = models.ImageField(upload_to=create_file_path)
listing = models.ForeignKey(Listing, related_name='photos')
I am using a CBV, UpdateView, to edit a listing. I am using this form:
class ListingDetailForm(forms.ModelForm):
class Meta:
model = Listing
exclude = []
and the inline formset in forms.py to make deleting/changing the image possible:
PhotoFormset = inlineformset_factory(Listing, Photo, fields='__all__', extra=1)
here is my view:
class ListingDetailView(UpdateView):
model = Listing
template_name = 'listing/listing_detail.html'
form_class = ListingDetailForm
success_url = '/store/'
def get_context_data(self, **kwargs):
self.object = self.get_object()
context = super(ListingDetailView, self).get_context_data(**kwargs)
if self.request.POST:
context['form'] = ListingDetailForm(self.request.POST, instance=self.object)
context['photo_form'] = PhotoFormset(self.request.POST, self.request.FILES, instance=self.object)
else:
context['form'] = ListingDetailForm(instance=self.object)
context['photo_form'] = PhotoFormset(instance=self.object)
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
photo_form = PhotoFormset(self.request.POST)
print photo_form.is_valid()
if form.is_valid() and photo_form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
print 'in form valid for update'
context = self.get_context_data()
base_form = context['form']
photo_form = context['photo_form']
# print base_form
# print photo_form
if base_form.is_valid() and photo_form.is_valid():
print 'forms are valid for update'
base_form.save()
photo_form.save()
return super(ListingDetailView, self).form_valid(form)
else:
return self.render_to_response(self)
and the relevant template section:
{% block body %}
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}<br><br>
{% endfor %}
{% for field in photo_form %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}<br><br>
{% endfor %}
{{ photo_form.management_form }}
<input type="submit" value="Update" />
</form>
{% endblock %}
The issues I am having are:
1) If there is a photo attached to the listing, through the admin, the photo form does not pass validation if I do nothing with the photo form, e.g. change only fields from the listing model. The photo form displays no errors when the page reloads after invalid.
2) selecting a new photo does not change the current photo, the photo form does not validate and displays no errors.
3) if there is currently no photo related to the listing trying to add one validates through the form but does not actually save a photo related to that listing.
Deleting an image, if there is one attached to the listing, works just fine. Deleting the image and updating some other field from the listing works. If there is no image updating only a listing field works. Adding a second photo to the listing through the form does not work and displays no form errors.
There are a few issues I noticed with your form.
You need to include enctype="multipart/form-data" on your form attributes or else you won't be able to post file data to the server
I would use the Django methods for rendering the form (form.as_p, form.as_table or form.as_ul) if you absolutely need to use manual rendering then follow the official guide: model formsets
On the post method your formset is missing FILES and instance
Once you implement these changes your formset should work just fine.

Django Template Form Render

My problem is not to show django form fields on template.It's silly but I just haven't found any solution.
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['name', 'email', 'text']
def __init__(self, content_type, id, *args, **kwargs):
super(CommentForm, self).__init__(*args, **kwargs)
self.content_type = content_type
self.id = id
def save(self, commit=True):
post_type = ContentType.objects.get_for_model(Post)
comment_type = ContentType.objects.get_for_model(Comment)
comment = super(CommentForm, self).save(commit=False)
if self.content_type == 'post':
comment.content_type = post_type
comment.post = self.id
else:
parent = Comment.objects.get(id=self.id)
comment.content_type = comment_type
comment.post = parent.post
comment.object_id = self.id
if commit:
comment.save()
return comment
my view:
def add_comment(request, content_type, id):
if request.method == 'POST':
data = request.POST.copy()
form = CommentForm(content_type, id, data)
if form.is_valid():
form.save()
return redirect(reverse('index'))
my add_comment template:
<form method="post" action="{% url 'add_comment' 'post' post.id %}">
{% csrf_token %}
{% if not user.is_authenticated %}
{{ form.name.label_tag }}
{{ form.name }}
{{ form.email.label_tag }}
{{ form.email }}
{% endif %}
{{ form.text.label_tag }}
{{ form.text }}<br>
<input type="submit" value="Comment" />
</form>
and I included like:
<button id="button" type="button">Add Comment</button>
<div id="post_comment_form">{% include 'articles/add_comment.html' %}</div>
</article> <!-- .post.hentry -->
why not django rendered form fields,despite of showing buttons?
EDIT:
I'm rendering form in post view.
def post(request, slug):
post = get_object_or_404(Post, slug=slug)
comments = Comment.objects.filter(post=post.id)
return render(request,
'articles/post.html',
{'post': post,
'form': CommentForm,
'comments': comments,
# 'child_comments': child_comments
}
)
You forgot to instantiate the form, change this line:
'form': CommentForm,
to this
'form': CommentForm(),
In your view, you're not sending any context variables to the template, so your 'form' object isn't available for your template to process.
For example, the following return statement will render your .html and pass along all local variables, this isn't necessarily the best option (how much do you want your template to have access to), but is simple:
from django.shortcuts import render
...
return render(request, "template.html", locals())
you can also pass a dictionary instead of all local variables. Here's the documentation for render

Is it possible to have a form in a ListView template?

I built a listview which works fine and gives me exactly what I want.
In the template of this ListView I declared a form that points to a CreateView.
The form is like so,
{% if user.is_authenticated %}
<form action="{% url 'post_wall' %}" method="POST">
{% csrf_token %}
<input type='text' name='body' />
<input type='hidden' name='from_user' value='{{ user.id }}' />
<input type='hidden' name='to_user' value='{{ to_user }}' />
<input type='submit' value='POST'/>
</form>
{% endif %}
the post_wall url corresponds to
url(r'accounts/post_wall', WallCreate.as_view(), name='post_wall'),
The url which contains the form is
url(r'accounts/wall/(?P<slug>\w+)/$', WallList.as_view(), name='wall'),
This calls the CreateView,
class WallCreate(CreateView):
model = WallPost
def get_success_url(self):
url = reverse('wall', kwargs={'slug': request.POST.to_user})
return HttpResponseRedirect(url)
This gives me a
TemplateDoesNotExist at /accounts/post_wall
users/wallpost_form.html
Shouldn't this be working properly as a post is sent to a CreateView? Or have I misunderstood something about CBVs?
Yes, but all the form process will have to be made by the ListView itself. That is simple, considering you can inherit the behaviour from ModelFormMixin. You will only need one url (to the list view). The template will look like:
{% if user.is_authenticated %}
<form action="" method="POST">
{% csrf_token %}
{{ form }}
<input type='submit' value='POST'/>
</form>
{% endif %}
And your view:
from django.views.generic.list import ListView
from django.views.generic.edit import ModelFormMixin
class ListWithForm(ListView, ModelFormMixin):
model = MyModel
form_class = MyModelForm
def get(self, request, *args, **kwargs):
self.object = None
self.form = self.get_form(self.form_class)
# Explicitly states what get to call:
return ListView.get(self, request, *args, **kwargs)
def post(self, request, *args, **kwargs):
# When the form is submitted, it will enter here
self.object = None
self.form = self.get_form(self.form_class)
if self.form.is_valid():
self.object = self.form.save()
# Here ou may consider creating a new instance of form_class(),
# so that the form will come clean.
# Whether the form validates or not, the view will be rendered by get()
return self.get(request, *args, **kwargs)
def get_context_data(self, *args, **kwargs):
# Just include the form
context = super(ListWithForm, self).get_context_data(*args, **kwargs)
context['form'] = self.form
return context