I'm using a modelform to display only a subset of the fields in the model. When the form is submitted it fails form.is_valid() but form.errors is empty. I'd rather not display all my code here, but below is a sample:
Model and Form
class Videofiles(models.Model):
active = models.CharField(max_length=9)
filenamebase = models.CharField(max_length=180, primary_key=True, db_column='FilenameBase')
program = models.CharField(max_length=60, db_column='Program')
displayname = models.CharField(max_length=150, db_column='DisplayName')
description = models.TextField(db_column='Description', blank=True)
tagskeywords = models.TextField(db_column='TagsKeywords', blank=True)
class Meta:
db_table = u'legacyTable'
class VideoFilesForm(ModelForm):
filenamebase = forms.CharField(max_length=30)
displayname = forms.CharField(max_length=30)
description = forms.CharField(max_length=30, required=False)
tagskeywords = forms.CharField(max_length=60, required=False)
class Meta:
model=Videofiles
fields=['filenamebase','displayname','description','tagskeywords']
View
def editClip(request, clipId):
clip = Videofiles.objects.get(filenamebase=clipId)
form = VideoFilesForm(instance=clip)
if request.method == 'POST':
if 'save' in request.POST:
if form.is_valid():
form.save()
else:
print form.errors
return render_to_response('legacyDB/edit_clip.html',locals())
Your form is unbound, because you are not passing any data to it. Calling is_valid on an unbound form will always return False, with empty errors (see the form api docs).
Your view should be something like the following:
def editClip(request, clipId):
clip = Videofiles.objects.get(filenamebase=clipId)
if request.method == 'POST':
# create bound form with input from request.POST
form = VideoFilesForm(request.POST, instance=clip)
if 'save' in request.POST:
if form.is_valid():
form.save()
else:
print form.errors
else:
# create unbound form to display in template
form = VideoFilesForm(instance=clip)
return render_to_response('legacyDB/edit_clip.html',locals())
Related
I am using the same form for profile_edit and create_profile functionality. It is updating the multi-choice values in the profile_edit page but does not create in create_profile.
Below is the form code in forms.py
class ProfileForm(ModelForm):
full_name = forms.CharField(required=True)
current_position = forms.CharField(required=True)
about_me = forms.Textarea(attrs={'required':True})
topic_name = forms.ModelMultipleChoiceField(Topic.objects.all())
class Meta:
model = Profile
fields =(
"full_name",
"current_position",
"about_me",
"topic_name",
)
Below is the views.py for profile creation
def create_profile(request, user_id):
if request.method == "POST":
form = ProfileForm(request.POST)
if form.is_valid():
form = form.save(commit=False)
user = get_object_or_404(User, id=user_id)
form.user = user
print(form.topic_name.all()) # Prints empty queryset
form.save()
return redirect("profile_view", user_id=user_id)
else:
context = {"form": form}
return render(request, "profile/create_profile.html", context)
else:
form = ProfileForm()
context = {
"form": form
}
return render(request, "profile/create_profile.html", context)
Below is Model.py
class Topic(models.Model):
topic = models.CharField(max_length=12)
def __str__(self):
return self.topic
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True,)
full_name = models.CharField(max_length=60, null=True)
current_position = models.CharField(max_length=64, null=True)
about_me = models.TextField(max_length=255, null=True)
topic_name = models.ManyToManyField(Topic)
def __str__(self):
return self.full_name
Both create_profile and edit_profile templates are exactly the same.
It saves everything except Multichoice field.
When you do save(commit=False),
you need to use mymodelform.save_m2m() below save(commit=True) on your ModelForm,
because many to many relationships cannot be saved without an ID.
see this docs
so in your views.py
if form.is_valid():
profile = form.save(commit=False)
user = get_object_or_404(User, id=user_id)
profile.user = user
profile.save()
form.save_m2m()
return redirect("profile_view", user_id=user_id)
I have a form without an associated model, just a contact form for sending a message.
I have some experience with django forms by now, so I thought I had done everything correctly, but nothing ends up rendering when the page is viewed in a browser at all, nor are there any errors to troubleshoot.
My forms.py:
from django import forms
class ContactForm(forms.Form):
class Meta:
fields = ['full_name', 'phone', 'email', 'message']
full_name = forms.CharField(max_length=20)
phone = forms.CharField(max_length=20)
email = forms.CharField(max_length=30)
message = forms.CharField(max_length=400)
And my view that turns the form into something useful:
def contact_view(request):
full_name = request.POST.get('full_name', False)
phone = request.POST.get('phone', False)
email = request.POST.get('email', False)
message = request.POST.get('message', False)
form = ContactForm()
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# send_emails(first_name, last_name, email)
template = loader.get_template('/myapp/mysite/main_page/templates/main_page/thankyoumsg.html')
return HttpResponse(template.render({}, request))
template = loader.get_template('/myapp/mysite/main_page/templates/main_page/contact.html')
return HttpResponse(template.render({}, request))
And my template:
<form class="leave-comment" action="." method="post">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Submit</button>
</form>
But nothing is displaying, and I am unsure why. How can I troubleshoot this?
You're not including the form in the response at the last line. This should (probably) do the trick:
def contact_view(request):
...
return HttpResponse(template.render({'form': form}, request))
I also believe you need to add the fields directly to the form class, not in the Meta-class.
from django import forms
class ContactForm(forms.Form):
# Move out the fields here instead
full_name = forms.CharField(max_length=20)
phone = forms.CharField(max_length=20)
email = forms.CharField(max_length=30)
message = forms.CharField(max_length=400)
class Meta:
# This may still be there but may also be a bit redundant since
# you're choosing to show all applied fields.
fields = ['full_name', 'phone', 'email', 'message']
class Meta is only used when you have a model. If you only need to render a form without a specific model use it this way. for more information please visit official documentation:
https://docs.djangoproject.com/en/2.1/topics/forms/
forms.py
class ContactForm(forms.Form):
full_name = forms.CharField(max_length=20)
phone = forms.CharField(max_length=20)
email = forms.CharField(max_length=30)
message = forms.CharField(max_length=400)
views.py
def contact_view(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
full_name = form.cleaned_data['full_name']
phone = form.cleaned_data['phone']
email = form.cleaned_data['email']
message = form.cleaned_data['message']
template = loader.get_template('/myapp/mysite/main_page/templates/main_page/thankyoumsg.html')
return HttpResponse(template.render({'form': form}, request))
template = loader.get_template('/myapp/mysite/main_page/templates/main_page/thankyoumsg.html')
return HttpResponse(template.render({'form': form}, request))
form = ContactForm()
template = loader.get_template('/myapp/mysite/main_page/templates/main_page/contact.html')
return HttpResponse(template.render({'form': form}, request))
This question already has answers here:
Saving Many To Many data via a modelform in Django
(2 answers)
Closed 4 years ago.
There's form with many fields (Date, Char, Text, Image, URL...) and they works fine. I mean values are submitted to DB as they must. But when I added ManyToManyField, it didn't save the value of this MultipleChoice form to DB. Any ideas why?
models.py:
class EventTag(models.Model):
tags = models.CharField(max_length=300)
def __str__(self):
return self.tags
class Article(models.Model):
source = models.CharField(max_length=100)
source_img = models.ImageField(default='default.png', blank=True)
#other fields
event_tags = models.ManyToManyField(EventTag, blank=True)
forms.py:
class CreateArticle(forms.ModelForm):
class Meta:
model = models.Article
fields = ['source', 'source_img', 'event_tags', ]
views.py:
def article_create(request):
if request.method == 'POST':
form = forms.CreateArticle(request.POST, request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.author = request.user
instance.save()
return redirect('articles:list')
else:
form = forms.CreateArticle()
return render(request, 'articles/article_create.html', { 'form': form })
after save your form you must call form.save_m2m(). your view must be like this:
def article_create(request):
if request.method == 'POST':
form = forms.CreateArticle(request.POST, request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.author = request.user
instance.save()
form.save_m2m()
return redirect('articles:list')
else:
form = forms.CreateArticle()
return render(request, 'articles/article_create.html', { 'form': form })
I have a fairly standard model and form. I have some mandatory fields with an ImageField. When the user choose an image and doesn't field the mandatory fields, the image isn't saved and the user needs to 're-upload' it again.
As the row in the database already exists (because this form is part of a 'wizard'), I don't mind saving the picture in the database even if all the form isn't valid with all the mandatory data.
This is what I have right now in my view, which works when you fill all the mandatory fields:
def my_view(request):
instance = MyModel.objects.get(user=request.user)
form = MyForm(instance=instance)
if request.POST:
form = MyForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
new_instance = form.save(commit=False)
if request.FILES and request.FILES['photo']:
uploaded_photo = request.FILES['photo']
new_instance.photo = uploaded_photo
new_instance.save()
return HttpResponseRedirect(reverse('new_url'))
return render_to_response('current_template.html', locals(), context_instance=RequestContext(request))
Here's what I tried to save the picture in DB even if the other fields aren't filled, but I get the error Django Upload Error - Upload a valid image (either not an image or corrupted):
def my_view(request):
instance = MyModel.objects.get(user=request.user)
form = MyForm(instance=instance)
if request.POST:
form = MyForm(request.POST, request.FILES, instance=instance)
if request.FILES and request.FILES['photo']:
uploaded_photo = request.FILES['photo']
instance.photo = uploaded_photo
instance.save()
if form.is_valid():
new_instance = form.save()
return HttpResponseRedirect(reverse('new_url'))
return render_to_response('current_template.html', locals(), context_instance=RequestContext(request))
Here's my form (fairly simple):
class MyForm(ModelForm):
first_name = forms.CharField(label='First Name', max_length=50, required=True)
last_name = forms.CharField(label='Last Name', max_length=50, required=True)
photo = forms.ImageField(required=False)
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
class Meta:
model = MyModel
fields = ('first_name','last_name', 'photo')
Here's my model (again very simple):
class MyModel(models.Model):
first_name = models.TextField(max_length=50)
last_name = models.TextField(max_length=50)
photo = ImageField(upload_to=get_photo_path,null=True,blank=True)
This is how I made it work. Notice that there's no 'request.FILES' as a parameter for the constructor of the form in the 'else' when the form is not valid. This is what made Django display the error message.
if form.is_valid():
instance = form.save(commit=False)
if request.FILES and request.FILES['photo']:
instance = save_photo(instance, request.FILES['photo'])
instance.save()
return HttpResponseRedirect(reverse('url'))
else:
if request.FILES and request.FILES['photo']:
instance = save_photo(instance, request.FILES['photo'])
form = InstanceForm(request.POST, instance=instance)
How to validate the unique_together error and how to return the custom error message to the html through the view.py. Please help.
model.py:
class Pcq(models.Model):
product = models.ForeignKey(Product, null=False)
component = models.ForeignKey(Component, null=False)
quantity = models.IntegerField('Quantity', null=False)
class Meta:
unique_together = ("product", "component")
def __unicode__(self):
return self.product.productname
Form.py
class PcqForm(ModelForm):
class Meta:
model = Pcq
fields = ['component', 'quantity']
exclude = ['product']
Views.py
def pcq_add(request, product_id):
if request.method == 'POST':
form = PcqForm(request.POST or None)
if form.is_valid():
pcq = form.save(commit=False)
pcq.product_id = product_id
pcq.save()
form = PcqForm()
successmsg = "Component added successfully! Add another Component..."
return render(request, 'maps/pcq/pcq_add.html', {'form': form, 'product_id': product_id, 'successmsg': successmsg})
form = PcqForm()
return render(request, 'maps/pcq/pcq_add.html', {'form': form, 'product_id': product_id})
You need a custom clean function for the form
class PcqForm(ModelForm):
class Meta:
model = Pcq
fields = ['component', 'quantity']
exclude = ['product']
def clean(self):
cleaned_data = super(PcqForm, self).clean()
component = cleaned_data.get('component')
quantity = cleaned_data.get('quantity')
if component and quantity:
try:
Pcq.objects.get(
component=component,
quantity=quantity,
)
except Pcq.DoesNotExist:
# Yay
pass
else
raise forms.ValidationError(_("Error message goes here"))
UPDATE
Same concept as above but in the view.
def pcq_add(request, product_id):
if request.method == 'POST':
form = PcqForm(request.POST or None)
data = {
'form': form,
'product_id': product_id
}
if form.is_valid():
pcq = form.save(commit=False)
pcq.product_id = product_id
try:
pcq.save()
except IntegrityError:
# You'll need to check the exception that is raised
# Handle failed unique_together
pass
else:
form = PcqForm()
data['successmsg'] = (
"Component added successfully! Add another Component.")
else:
form = PcqForm()
data = {'form': form, 'product_id': product_id}
return render(request, 'maps/pcq/pcq_add.html', data)
Alternatively:
remove the exclude = ['product']
on GET pass product_id to the form form = PcqForm(initial_data={'product': product_id})
Use the form to validate unique_together (Not sure you even need a custom clean then)