I am trying to create a ModelForm that links to an external database, and when you submit the form the external database gets updated. The problem comes when I check the validity of the form, it is invalid.
I have done some researching into this and found the most common problem was that the form is not bound, but when I use print(form.non_field_errors) I get:
<bound method BaseForm.non_field_errors of <EmailForm bound=True, valid=False, fields=(subject;body;name;altsubject;utm_source;utm_content;utm_campaign)>
models.py:
class MarketingEmails(models.Model):
messageid = models.AutoField(db_column='column1', primary_key=True)
subject = models.CharField(db_column='column2', max_length=2000)
body = models.TextField(db_column='column3') #using a text field as there is no maximum length
name = models.CharField(db_column='column4', max_length=25)
altsubject = models.CharField(db_column='column5', max_length=2000)
utm_source = models.CharField(db_column='column6', max_length=25)
utm_content = models.CharField(db_column='column7', max_length=25)
utm_campaign = models.CharField(db_column='column8', max_length=25)
class Meta:
managed = False
db_table = ''
forms.py:
class EmailForm(forms.ModelForm):
class Meta:
model = MarketingEmails
fields = ['messageid','subject','body','name','altsubject','utm_source','utm_content','utm_campaign']
views.py:
def emailinfo(request, pk):
if request.session.has_key('shortname'):
shortname = request.session['shortname']
rows = get_object_or_404(MarketingEmails, pk=pk)
if request.method == 'POST':
form = EmailForm(request.POST)
print(form.errors)
print(form.non_field_errors)
if form.is_valid():
form.save()
print("form is valid")
return redirect('marketingemails:emailinfo', pk = rows.messageid)
return render(request, 'marketingemails/emailinfo.html',{'shortname': shortname, 'rows': rows})
else:
return HttpResponseRedirect(reverse('common:login'))
urls.py:
app_name = 'marketingemails'
urlpatterns = [
url(r'^marketing/emails/(?P<pk>[0-9]+)/$', marketingviews.emailinfo, name='emailinfo'),
]
html:
<form method="POST" class="post-form" action ="">
{% csrf_token %}
<label for="exampleTextarea">Name</label>
<textarea class="form-control" id="exampleTextarea" rows="1">{{ rows.name }}</textarea>
<label for="exampleTextarea">Subject</label>
<textarea class="form-control" id="exampleTextarea" rows="1">{{ rows.subject }}</textarea>
<label for="exampleTextarea">Alternative Subject</label>
<textarea class="form-control" id="exampleTextarea" rows="1">{{ rows.altsubject }}</textarea>
<label for="exampleTextarea">Body</label>
<div class="ibox-content no-padding">
<div class="summernote">
{{ rows.body }}
</div>
</div>
<label for="exampleTextarea">utm_source</label>
<textarea class="form-control" id="exampleTextarea" rows="1">{{ rows.utm_source }}</textarea>
<label for="exampleTextarea">utm_content</label>
<textarea class="form-control" id="exampleTextarea" rows="1">{{ rows.utm_content }}</textarea>
<label for="exampleTextarea">utm_campaign</label>
<textarea class="form-control" id="exampleTextarea" rows="1">{{ rows.utm_campaign }}</textarea>
<button type="submit" class="save btn btn-default">Save</button>
</form>
Your HTML form doesn't name the fields so the form can't get them. You want to use the form for rendering too : https://docs.djangoproject.com/en/1.11/topics/forms/#working-with-form-templates
Related
So i am new to Django, and i want to make some hardware compatibility check feature in my app, i make a form to be filled and check the compatibility from database then show me the result. but the problem is the form seems to just keep saving to database without processing the logical operation first.
this is my view
class CreateSimView(LoginRequiredMixin, CreateView):
login_url = '/login/'
model = Simulation
form_class = SimPostForm
template_name = 'sim_post.html'
redirect_field_name = 'sim/sim.html'
def simpost(request):
mtb = Motherboard.objects.all()
cpu = Cpu.objects.all()
vga = Vga.objects.all()
ram = Ram.objects.all()
storage = Storage.objects.all()
mtbform = request.GET.get('mtb_name')
cpuform = request.GET.get('cpu_name')
vgaform = request.GET.get('vga_name')
ramform = request.GET.get('ram_name')
strform = request.GET.get('str_name')
simcpu = cpu.objects.values_list('socket', flat=True).filter(name__icontains=cpuform)
simcpu1 = mtb.objects.values_list('socket', flat=True).filter(name__icontains=mtbform)
simvga = vga.objects.values_list('vga_interface', flat=True).filter(name__icontains=vgaform)
simvga1 = mtb.objects.values_list('vga_interface', flat=True).filter(name__icontains=mtbform)
simram = ram.objects.values_list('mem_type', flat=True).filter(name__icontains=ramform)
simram1 = mtb.objects.values_list('mem_type', flat=True).filter(name__icontains=mtbform)
simstr = str.objects.values_list('str_interface', flat=True).filter(name__icontains=strform)
simstr1 = mtb.objects.values_list('str_interface', flat=True).filter(name__icontains=mtbform)
if simcpu == simcpu1 :
if simvga == simvga1:
if simram == simram1:
if simstr == simstr1:
form = SimPostForm(request.POST)
if form.is_valid():
form.save()
return render(mtbform,cpuform,vgaform,ramform,strform,"/")
else:
strform = "not compatible"
return render(mtbform,cpuform,vgaform,ramform,strform,"/")
else:
ramform = "not compatible"
return render(mtbform,cpuform,vgaform,ramform,strform,"/")
else:
vgaform = "not compatible"
return render(mtbform,cpuform,vgaform,ramform,strform,"/")
else:
cpuform = "not compatible"
return render(mtbform,cpuform,vgaform,ramform,strform,"/")
my models for the form
class Simulation(models.Model):
build_name = models.CharField(blank=False, max_length=150)
mtb_name = models.CharField(blank=False, max_length=150)
cpu_name = models.CharField(blank=False, max_length=150)
vga_name = models.CharField(blank=False, max_length=150)
ram_name = models.CharField(blank=False, max_length=150)
str_name = models.CharField(blank=False, max_length=150)
def __str__(self):
return self.build_name
def get_absolute_url(self):
return reverse('sim:sim_post')
and my template
{% extends "base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="container">
<div class="jumbotron">
<h1>Pilih Parts</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label for="build">Nama Rakitan</label>
<input class="form-control" type="text" id="build" name="build_name" value="{{ form.build_name.value|default_if_none:"" }}">
</div>
<div class="form-group">
<label for="mtb">Motherboard</label>
<input class="form-control" id="mtb" type="text" name="mtb_name" value="{{ form.mtb_name.value|default_if_none:"" }}">
</div>
<div class="form-group">
<label for="vga">Vga</label>
<input class="form-control" id="vga" type="text" name="vga_name" value="{{ form.vga_name.value|default_if_none:"" }}">
</div>
<div class="form-group">
<label for="ram">Ram</label>
<input class="form-control" type="text" id="ram" name="ram_name" value="{{ form.ram_name.value|default_if_none:"" }}">
</div>
<div class="form-group">
<label for="storage">Storage</label>
<input class="form-control" type="text" id="storage" name="str_name" value="{{ form.storage_name.value|default_if_none:"" }}">
</div>
<div class="form-group">
<label for="cpu">Cpu</label>
<input class="form-control" type="text" id="cpu" name="cpu_name" value="{{ form.cpu_name.value|default_if_none:"" }}">
</div>
<button type="submit" class="btn btn-secondary">Cek Kompatibilitas</button>
</form>
</div>
</div>
{% endblock %}
i also use {{ form.storage_name.value|default_if_none:"" }} in my form hoping it would return the value from before. but it doesn't seem to do that.
It seems to me that you implemented your logic in
def simpost(request)
but you missed to integrate the call of this function in the predefined processing of the CreateView.
So one approach might be to override
def post(self, request, *args, **kwargs)
of the CreateView and put your logic there. If everything is fine, you should call
super().form_valid(your_form_object_here)
to trigger the creation of your data with the predefined actions of the CreateView.
I want to validate my update form using form validation. When i leave a field blank and hit update button I get a blank page showing the error message. I want that error message to be displayed on top of that particular field when I leave it blank and hit update button.
This is my latest update view:
def update(request, id):
item = get_object_or_404(BookEntry, id=id)
if request.method == "POST":
form = UpdateForm(request.POST, instance=item)
# print template
error_messages = {
'error': form.errors,
}
if form.is_valid():
print(form.cleaned_data)
post = form.save(commit=False)
post.save()
return HttpResponseRedirect(reverse('bms:index'), id)
else:
form = UpdateForm()
return HttpResponse(json.dumps(error_messages))
return render(request, 'index.html',{'form':form})
This is my old update view:
def update(request, id):
item = get_object_or_404(BookEntry, id=id)
if request.method=="POST":
form = UpdateForm(request.POST, instance=item)
if form.is_valid():
post=form.save(commit=False)
post.save()
return HttpResponseRedirect(reverse('bms:index'), id)
else:
form=UpdateForm(instance=item)
return HttpResponseRedirect(reverse('bms:index'), id)
return render(request, 'bms.html',{'form':form})
This is my forms.py:
class UpdateForm(forms.ModelForm):
title = forms.CharField(max_length=100)
author = forms.CharField(max_length=100)
edition = forms.CharField(max_length=100)
publisher = forms.CharField(max_length=100)
genre = forms.CharField(max_length=100)
detail = forms.CharField(max_length=100)
language = forms.CharField(max_length=100)
price = forms.IntegerField()
dop = forms.CharField(max_length=100)
cop = forms.CharField(max_length=100)
copyright = forms.CharField(max_length=100)
isbn = forms.IntegerField()
class Meta:
model = BookEntry
fields = '__all__'
This is my html form:
<form action="{% url 'bms:update' book.id %}" id="updateform" name="updateform" method="POST">
{% csrf_token%}
<div class = "form-group">
<label for = "title">
Title:
</label>
<input class = "form-control" id="book_title" type = "text" name="title" value="{{ book.title }}">
</div>
<div class="form-group">
<label for = "author">
Author:
</label>
<input id="book_author" class = 'form-control' type = "text" name="author" value="{{ book.author }}">
</div>
<div class = "form-group">
<label for = "edition">
Edition:
</label>
<input id="book_edition" type = "text" class = 'form-control' name="edition" value="{{ book.edition }}">
</div>
<div class = "form-group">
<label for ="publisher">
Publisher:
</label>
<input id="book_publisher" type = "text" name="publisher" class = 'form-control' value="{{ book.publisher }}"/>
</div>
<div class = "form-group">
<label for ="genre">
Genre:
</label>
<input id="book_genre" type = "text" name="genre" class = 'form-control' value="{{ book.genre }}"/>
</div>
<div class = "form-group">
<label for ="detail">
Detail:
</label>
<input id="book_detail" type = "text" name="detail" class = 'form-control' value="{{ book.detail }}"/>
</div>
<div class = "form-group">
<label for ="language">
Language:
</label>
<input id="book_language" type = "text" name="language" class = 'form-control' value="{{ book.language }}"/>
</div>
<div class = "form-group">
<label for ="price">
Price:
</label>
<input id="book_price" type = "text" name="price" class = 'form-control' value="{{ book.price }}"/>
</div>
<div class = "form-group">
<label for ="dop">
DOP:
</label>
<input id="book_dop" type = "text" name="dop" class = 'form-control' value="{{ book.dop }}"/>
</div>
<div class = "form-group">
<label for ="cop">
COP:
</label>
<input id="book_cop" type = "text" name="cop" class = 'form-control' value="{{ book.cop }}"/>
</div>
<div class = "form-group">
<label for ="cop">
Copyright
</label>
<input id="book_copyright" type = "text" name="copyright" class = 'form-control' value="{{ book.copyright }}"/>
</div>
<div class = "form-group">
<label for ="isbn">
ISBN:
</label>
<input id="book_isbn" type = "text" name="isbn" class = 'form-control' value="{{ book.isbn }}"/>
</div>
<input type = "submit" value="Update" id="update" class = "btn btn-success" style="font-size:18px;" />
</form>
This is the URL it shows when a field is left empty:
http://127.0.0.1:8000/bms/update/2/
And this is the error message:
{"error": {"edition": ["This field is required."]}}
I had left edition field empty to check if validation is working properly or not.
Can anyone suggest me how to do it the right way?
As Daniel pointed out, you need to re-render the form.
The basics of HTTP Form Post are:
If the form is valid: save + do a HttpResponseRedirect
If the form is not valid; re-render the same template with the errors
Change your function to:
def update(request, id):
item = get_object_or_404(BookEntry, id=id)
if request.method == "POST":
form = UpdateForm(request.POST, instance=item)
# print template
error_messages = {
'error': form.errors,
}
if form.is_valid():
print(form.cleaned_data)
post = form.save(commit=False)
post.save()
return HttpResponseRedirect(reverse('bms:index'), id)
return render(request, 'index.html',{'form':form}) # Form invalid case is handled here; the form is reused from above; form.errors is available in the template now
else:
pass # Handle GET requests here
I'm trying to edit the form via my edit button.
When i click update button in my form I'm getting the following error:
FieldError at /studentapp/editrow/72/
Cannot resolve keyword 'rowid' into field Choices are: address, course, id, name, pub_date, roll
My traceback shows the error originating in this line
item = get_object_or_404(Studentapp, rowid=id) this line.
My models.py looks like this:
class Studentapp(models.Model):
name = models.CharField(max_length=100)
roll = models.IntegerField()
course = models.CharField(max_length=100)
address = models.CharField(max_length=100)
pub_date = models.DateTimeField('date published', auto_now=True)
def __str__(self):
return '%s %s %s %s' % (self.name, self.roll, self.course, self.address)
def published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
EDIT
My view:
def editrow(request, id):
item = get_object_or_404(Studentapp, id=id)
if request.method=="POST":
form = EntryForm(request.POST, instance=item)
if form.is_valid():
post=form.save(commit=False)
post.save()
return HttpResponseRedirect(reverse('studentapp:index'), id)
else:
form=EntryForm(instance=item)
return render(request, 'index.html',{'form':form})
else:
form=EntryForm(instance=item)
return render(request, 'index.html',{'form':form})
My urls.py
url(r'^editrow/(?P<rowid>[0-9]+)/$', views.editrow, name='editrow'),
Form that i'm using to update the entry:
{% load staticfiles %}
<form action="{% url 'studentapp:editrow' student_detail.id %}" id="editform" method="POST">
{% csrf_token%}
<div class = "form-group">
<label for = "your_name">
Your name:
</label>
<input class = "form-control" id="new_name" type = "text" name="name" value="{{ student_detail.name }}" placeholder="Enter your name">
</div>
<div class="form-group">
<label for = "course_name">
Course:
</label>
<input id="new_course" class = 'form-control' type = "text" name="course" value="{{ student_detail.course }}" placeholder="Enter your course">
</div>
<div class = "form-group">
<label for = "rollno">
Roll No.:
</label>
<input id="new_rollno" type = "text" class = 'form-control' name="roll" value="{{ student_detail.roll }}" placeholder="Enter your roll number">
</div>
<div class = "form-group">
<label for ="addr">
Address:
</label>
<input id="new_address" type = "text" name="address" class = 'form-control' value="{{ student_detail.address }}" placeholder="Enter your address"/>
</div>
<input type = "submit" value="Update" id="update" class = "btn btn-success" style="font-size:18px;" />
</form>
This line is not right, your model does not have the field rowid
item = get_object_or_404(Studentapp, rowid=id) this line. # WRONG
item = get_object_or_404(Studentapp, id=id) this line. # OK
and your urls should be
url(r'^editrow/(?P<id>[0-9]+)/$', views.editrow, name='editrow'),
# url(r'^editrow/(?P<rowid>[0-9]+)/$', views.editrow, name='editrow'), # WRONG
New to Django and this is my first web application.
I'm having trouble with django's ModelForm feature and I wanted to know:
How do I modify my code so that I can create an instance of ModelForm, and specifically, how can I extract the form data to upload to the backend? I will need to reference this instance at a later time to re-populate the same data in an update_profile view but the updation can only happen once the user is logged in (after signup and profile creation).
For the editing section, do I use pk=some_record.pk? Very confused, any help is appreciated.
The model I'm working with CustomerDetail has a foreign key field customer which references the Customer model:
class CustomerDetail(models.Model):
phone_regex = RegexValidator(regex = r'^\d{10}$', message = "Invalid format! E.g. 4088385778")
date_regex = RegexValidator(regex = r'^(\d{2})[/.-](\d{2})[/.-](\d{2})$', message = "Invalid format! E.g. 05/16/91")
customer = models.OneToOneField(Customer,
on_delete=models.CASCADE,
primary_key=True,)
address = models.CharField(max_length=100)
date_of_birth = models.CharField(validators = [date_regex], max_length = 10, blank = True)
company = models.CharField(max_length=30)
home_phone = models.CharField(validators = [phone_regex], max_length = 10, blank = True)
work_phone = models.CharField(validators = [phone_regex], max_length = 10, blank = True)
def __str__(self):
return str(self.customer)
Here is a snippet of views.py:
def create_profile(request):
if request.POST:
address = request.POST['address']
date_of_birth = request.POST['date_of_birth']
company = request.POST['company']
home_phone = request.POST['home_phone']
work_phone = request.POST['work_phone']
custprofdata = CustomerDetail(address = address, date_of_birth = date_of_birth, company = company, home_phone = home_phone, work_phone = work_phone)
custprofdata.save()
output = {'address': address, 'dateofbirth': date_of_birth, 'company': company, 'homephone': home_phone, 'workphone': work_phone}
return render(request, 'newuser/profile_created.html', output)
else:
return redirect(create_profile)
And here is a snippet of the form part of the respective create_profile.html:
<form action = "{% url 'create_profile' %}" class="create_profile" role="form" method = "post">
{% csrf_token %}
<div class="form-group">
<label for="address" class="col-md-3 control-label">Address</label>
<div class="col-md-9">
<input type="text" class="form-control" name="address" placeholder="777 Park St" />
</div>
</div>
<div class="form-group">
<label for="date-of-birth" class="col-md-3 control-label">Date Of Birth</label>
<div class="col-md-9">
<input type="text" class="form-control" name="date_of_birth" placeholder="09/12/82" />
</div>
</div>
<div class="form-group">
<label for="company" class="col-md-3 control-label">Company</label>
<div class="col-md-9">
<input type="text" class="form-control" name="company" placeholder="Oracle">
</div>
</div>
<div class="form-group">
<label for="home-phone" class="col-md-3 control-label">Home Phone</label>
<div class="col-md-9">
<input type="text" class="form-control" name="home_phone" placeholder="4082992788">
</div>
</div>
<div class="form-group">
<label for="work-phone" class="col-md-3 control-label">Work Phone</label>
<div class="col-md-9">
<input type="text" class="form-control" name="work_phone" placeholder="6690039955">
</div>
</div>
<div class="form-group">
<div class="col-md-offset-3 col-md-9">
<button type = "create" class="btn btn-success" form = "create_profile"> Submit </button>
</div>
</div>
</form>
Implementing a basic ModelForm is just a matter of the following:
from django.forms import ModelForm
from .models import CustomerDetail
class CustomerDetailForm(ModelForm):
class Meta:
model = CustomerDetail
fields = ['address', 'date_of_birth', 'company', 'home_phone', 'work_phone',]
https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/#a-full-example
But I suggest you also switch to using a Class Based View (CBV) - the CreateView will do the same as your existing view with much less code, with an implicit ModelForm (which you can customise by providing your own ModelForm class with form_class = YourFormClass if you want).
https://docs.djangoproject.com/en/1.10/ref/class-based-views/generic-editing/#createview
https://ccbv.co.uk/projects/Django/1.10/django.views.generic.edit/CreateView/
After creating CustomerDetailForm as #John Carter said, you might want to change your view.py to the following
def create_profile(request):
if request.POST:
form = CustomerDetailForm(request.POST)
if form.is_valid():
## save data in database ##
return render(request, 'newuser/profile_created.html', {form:form})
else:
return redirect(create_profile)
I have two forms in a view. One htlm form includes form and formset, other html form includes just single form.
When I am trying to submit singe form (Reminder), I am getting this error:
[u'ManagementForm data is missing or has been tampered with']
View:
form_class = CreateEventForm
second_form_class = modelformset_factory(EventTime, form = EventTimeForm, extra=3, formset=EventTimeFormset)
third_form_class = Reminder
model = Event
template_name = 'eventAdmin.html'
def get_context_data(self, **kwargs):
context = super(EventAdminPage, self).get_context_data(**kwargs)
context['form'] = self.form_class(self.request.POST or None, prefix="form", instance = self.object)
context['formset'] = self.second_form_class(
self.request.POST or None,
prefix="formset",
queryset=context['event_times'])
context['reminder_form'] = Reminder(self.request.POST or None)
return context
def post(self, request, **kwargs):
self.object = self.get_object()
event_times = EventTime.objects.filter(event = self.object).exclude(start_time = None)
if 'update_event_form' in request.POST:
form_class = self.get_form_class()
form = self.form_class(request.POST, prefix="form", instance=self.get_object())
formset = self.second_form_class(request.POST, prefix="formset", queryset=event_times)
if form.is_valid() and formset.is_valid():
event = form.save()
event_times = formset.save()
return HttpResponseRedirect('/event-admin-%s' %self.kwargs['event_id'])
else:
return self.render_to_response(
self.get_context_data(form=form, formset=formset))
if 'reminder_form' in request.POST:
form_class = self.second_form_class
form_name = 'Reminder'
form = form_class(request.POST)
if form.is_valid():
form.save(commit=False)
message = form.cleaned_data.get('text')
return HttpResponseRedirect('/event-admin-%s' %self.kwargs['event_id'])
else:
return self.render_to_response(
self.get_context_data(form=form))
HTML of form that raises error:
<form method='POST' action=''><input type='hidden' name='csrfmiddlewaretoken' value='3uQyEn4m8bHpM7Jy6a8WKQz5A8Uf6RZF' />
<div id="div_id_text" class="form-group"> <label for="id_text" class="control-label requiredField">
Text<span class="asteriskField">*</span> </label> <div class="controls "> <textarea class="textarea form-control" cols="40" id="id_text" maxlength="500" name="text" rows="10">
</textarea> </div> </div>
<input class="btn btn-primary" type="submit" name="reminder_form" value="Send" />
</form>
You need to add this line {{ form.management_form }} after the <form method='POST' action=''>. This is used by Django to manage the forms within the formset.
<form method='POST' action=''>
{{ form.management_form }}
<input type='hidden' name='csrfmiddlewaretoken' value='3uQyEn4m8bHpM7Jy6a8WKQz5A8Uf6RZF' />
<div id="div_id_text" class="form-group">
<label for="id_text" class="control-label requiredField">
Text
<span class="asteriskField">*</span>
</label> <div class="controls ">
<textarea class="textarea form-control" cols="40" id="id_text" maxlength="500" name="text" rows="10">
</textarea>
</div>
</div>
<input class="btn btn-primary" type="submit" name="reminder_form" value="Send" />
</form>