Django and jQuery: jQuery not working, but receiving no errors - javascript

I cannot figure out why my jQuery script isn't working. I've read through a bunch of stackOverflows but haven't been able to fix this issue.
I have a form, and when a checkbox is checked, I want to enable a form field with the id div_id_newLockName. If the checkbox is unchecked, I want the form field to be disabled and greyed out. The checkbox has id div_id_newNameBool I have written code that I think should work, but it doesn't. I'm not receiving any errors though, which makes troubleshooting difficult. I'm hoping a second set of eyes can help. A picture of what the form looks like can be found here: In this picture, the "change name?" checkbox corresponds with div_id_newNameBool, and the "Lock Name" char field is what I want to toggle enable or disable.
base.html (contains jQuery script)
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<style>
<!-- unimportant css stuff -->
</style>
<body class="w3-content" style="max-width:1200px">
<!-- !PAGE CONTENT! -->
{% block content %}{% endblock %}
<!-- End page content -->
</div>
<script>
$(document).ready(function(){
if ($("#div_id_newNameBool").is(':checked')){
$("#div_id_newLockName").prop("disabled", false);
}
else {
$("#div_id_newLockName").prop("disabled", true);
alert("DISABLED");
}
});
</script>
</body>
</html>
HTML page for form
{% extends "homepg/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<!-- Top header -->
<header>
</header>
<form method="POST">
<div class="w3-display-container w3-container">
{% csrf_token %}
<fieldset class="form-group" style="font-size:20px">
<legend class="border-bottom mb-4" style="font-size:20px, p">Edit Lock Information </legend>
{{ form|crispy }}
</fieldset>
<button type="submit" style="width: 200px; padding: 10px; left: 50%; margin:10px;">Change Lock</button>
</div>
</form>
{% endblock content %}
models.py
class Lock(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
lockName = models.CharField(max_length=20)
roomLoc = models.ForeignKey(Room, on_delete=models.CASCADE, null=True, blank=True)
state = models.BooleanField(default=0)
code1 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
code2 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
code3 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
code4 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
# create a dunder method to control how we want this to printed out
def __str__(self):
return self.lockName
forms.py
class changeLockForm(forms.Form):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(changeLockForm, self).__init__(*args, **kwargs)
self.fields['roomLoc'] = forms.ModelChoiceField(queryset=Models.Room.objects.filter(owner=None) | Models.Room.objects.filter(owner=user), label='Room Location')
self.fields['lockName'] = forms.ModelChoiceField(queryset=Models.Lock.objects.filter(owner=None) | Models.Lock.objects.filter(owner=user), label="Current Lock Name")
newNameBool = forms.BooleanField(label='Change Name? Y/N')
newLockName = forms.CharField(min_length=2, max_length=20, label='Lock Name', disabled=True)
#roomLoc = forms.ModelChoiceField(queryset=Models.Room.objects.filter(user=request.user), label='Room Location')
state = forms.BooleanField(required=False, label='LOCKED/UNLOCKED')
code1 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='First Code')
code2 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='Second Code')
code3 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='Third Code')
code4 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='Fourth Code')
class Meta:
model = Models.Lock
fields = ('lockName','newNameBool','newLockName','roomLoc', 'state', 'code1', 'code2', 'code3', 'code4')
field_order = ['lockName', 'newNameBool', 'newLockName', 'roomLoc', 'state', 'code1', 'code2', 'code3', 'code4']
views.py
def changeLock(request):
if request.method == "POST":
# if get post request will create form that has request.POST data!
form = changeLockForm(request.POST, user=request.user)
if form.is_valid():
locksObj = Model.Lock()
# will tell us if form valid when submitted
# form.cleaned_data is a DICTIONARY
tempBool = form.cleaned_data['newNameBool']
if tempBool:
locksObj.lockName = form.cleaned_data['newLockName']
elif not tempBool:
locksObj.lockName = form.cleaned_data['lockName']
else:
print("WHOOPSIE! clearly I coded something wrong wtf")
locksObj.state = form.cleaned_data['state']
locksObj.roomLoc = form.cleaned_data['roomLoc']
locksObj.code1 = form.cleaned_data['code1']
locksObj.code2 = form.cleaned_data['code2']
locksObj.code3 = form.cleaned_data['code3']
locksObj.code4 = form.cleaned_data['code4']
locksObj.owner = request.user
# get the old alarm name
allCodes = Model.Lock.objects.filter(owner_id=request.user)
# old Alarm Name
currentName = allCodes.values_list('lockName', flat=True)[0]
# if the old alarm name and new alarm name aren't equal...
# delete the old alarm entry
entry = Model.Lock.objects.get(lockName=str(currentName))
entry.delete()
# replace it with the new data
locksObj.save()
messages.success(request, 'Lock ' + str(locksObj.lockName) + ' edited!')
return redirect("status-page")
else:
# if not POST request, will create a blank form!
form = changeLockForm(user=request.user)
return render(request, 'users/changeLockForm.html', {'form': form})

Related

Django Dynamic Inline Formset without any fields in the parent model not working

i am new to Django and i have been trying to create a dynamic inline form but ran into an issue. I am trying to create an inline form with user-customisable number of inline forms (models: DataSheetProperties) with foreign key to models: ProvisionalData. However, in the parent model (ProvisionalData), I do not want the user to fill in any fields, but for me to do the filling of the fields in the backend. Meaning, the user should only see the child model (DataSheetProperties) and be able to add or remove instances of that inline form as they see fit. The rendering of the form is okay, but when i press the 'save' button, I can't seem to save it and it does not even run through the form_valid() method in the views.py. I figured it was because there was no fields in the form of the parent model for the user to fill in, hence django does not recognise that the form was being submitted. Hence, I tried to add in a field into the forms.py (ProvisionalDataCreateForm) under the Div() section of the __init_ method. After adding the field, I was able to save the inline form successfully. However, I would hope that the user would not be able to see and fill in any of the fields in the parent model and form as I would want that form to be automatically filled up in the backend. Hope that you guys can help to give some suggestions, all answers are welcome. Thanks in advance!
I downloaded the django-dynamic-formset plugin from this link to use in my dynamic inline formset.
https://github.com/elo80ka/django-dynamic-formset
Also, the code for my dynamic inline formset can from this website.
https://dev.to/zxenia/django-inline-formsets-with-class-based-views-and-crispy-forms-14o6
forms.py
class ProvisionalDataForm(forms.ModelForm):
class Meta:
model = DataSheetProperties
fields = ['properties', 'name','method','value','units']
exclude = []
ProvisionalDataFormSet = inlineformset_factory(ProvisionalData, DataSheetProperties,
form=ProvisionalDataForm, extra=1,
can_delete=True)
class ProvisionalDataCreateForm(forms.ModelForm):
class Meta:
model = ProvisionalData
exclude = []
def __init__(self, *args, **kwargs):
super(ProvisionalDataCreateForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = True
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-3 create-label'
self.helper.field_class = 'col-md-9'
self.helper.layout = Layout(
Div(
Fieldset('Add properties',
Formset('data')),
HTML("<br>"),
ButtonHolder(Submit('submit', 'save')),
)
)
models.py
class ProvisionalData(models.Model):
status = (
('active', 'Active'),
('inactive', 'Inactive'),
)
provisionaldata_id = models.AutoField(primary_key=True)
rndProject = models.ForeignKey('RndProject', on_delete=models.CASCADE, null=True, blank=True)
status = models.CharField(max_length = 20, choices=status, default='active')
justification = models.TextField(max_length=200, null=True, blank=True)
class DataSheetProperties(models.Model):
options = (
('physical','Physical'),
('mechanical','Mechanical'),
('thermal','Thermal'),
)
status = (
('active', 'Active'),
('inactive', 'Inactive'),
)
name = models.CharField(max_length=50, default='Property Name')
method = models.CharField(max_length=50, default='Test Method')
units = models.CharField(max_length=50, default='Unit of Measurement')
value = models.CharField(max_length=50, default='Typical Value')
properties = models.CharField(max_length=50, choices=options)
data = models.ForeignKey('ProvisionalData', on_delete=models.CASCADE)
status = models.CharField(max_length = 20, choices=status, default='active')
views.py
class ProvisionalDataSheetView(CreateView):
model = ProvisionalData
form_class = ProvisionalDataCreateForm
template_name = 'rnd/provisional_datasheet.html'
def get_context_data(self, **kwargs):
context = super(ProvisionalDataSheetView, self).get_context_data(**kwargs)
if self.request.POST:
context['data'] = ProvisionalDataFormSet(self.request.POST)
else:
context['data'] = ProvisionalDataFormSet()
return context
def form_valid(self, form):
form.instance.rndProject = RndProject.objects.filter(rnd_project_id = self.kwargs['pk'])[0]
context = self.get_context_data()
data = context['data']
with transaction.atomic():
self.object = form.save()
if data.is_valid():
data.instance = self.object
data.save()
return super(ProvisionalDataSheetView, self).form_valid(form)
def get_success_url(self):
return reverse('rnd-project-details', kwargs={'pk': self.object.rndProject.rnd_project_id})
formset.html
{% load static %}
{% load crispy_forms_tags %}
<table>
{{ formset.management_form|crispy }}
{% for form in formset.forms %}
<tr class="{% cycle 'row1' 'row2' %} formset_row-{{ formset.prefix }}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field|as_crispy_field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<br>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
</script>
<script src="{% static 'rnd/jquery.formset.js' %}" type="text/javascript"> </script>
<script type="text/javascript">
$('.formset_row-{{ formset.prefix }}').formset({
addText: 'add another',
deleteText: 'remove',
prefix: '{{ formset.prefix }}',
});
</script>
provisional_datasheet.html
{% extends 'rnd/base.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="card">
<div class="card-header">
Create provisional datasheet
</div>
<div class="card-body">
{% crispy form %}
</div>
</div>
</div>
{% endblock content %}

Django model form mutiple selector can not save inti DB

I have a question as below. I stuck in this problem for a while.
I build up an order in django model form which provide customer using shopping cart to fill their transaction info.
Below is my code structure :
## models.py
from django.db import models
class Order(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField()
address = models.CharField(max_length=250)
postal_code = models.CharField(max_length=20)
city = models.CharField(max_length=10)
## forms.py
from django import forms
from .models import Order
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout,Div,Field
class OrderCreateForm(forms.ModelForm):
class Meta:
model = Order
fields = ['first_name', 'last_name', 'email', 'address', 'postal_code','city']
def __init__(self,*args,**kwargs):
super(OrderCreateForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper = Layout(
Div(Field('city') , id="twzipcode_ADV"),
)
## views.py
from .forms import OrderCreateForm
from .models import OrderItem
def order_create(request):
cart = Cart(request)
if request.method == 'POST':
form = OrderCreateForm(request.POST)
if form.is_valid():
order = form.save()
## .html
<form action="." method="post">
{% csrf_token %}
{% for field in form %}
{% if field is form.city or field is form.county%}
<div id="twzipcode_ADV"></div>
{% else %}
{{field.name}}
{{field}}
{% endif %}
{% endfor %}
{% buttons %}
<button type="submit" class="btn btn-success btn-product">
<span class="glyphicon glyphicon-shopping-cart"></span> Place order
</button>
{% endbuttons %}
</form>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/jquery-twzipcode#1.7.14/jquery.twzipcode.min.js'></script>
<script src="{%static "js/orders/create.js" %}"></script>
## create.js
$("#twzipcode_ADV").twzipcode({
zipcodeIntoDistrict: true,
css: ["city form-control", "town form-control"],
countyName: "city",
districtName: "town"
});
And the website can be shown as figure below .
As I show in the red rectangular area. My question is when I press down the placeholder button,the second selector will not be saved into city field I have already defined in models.py . However it only store the first selector. How can I solve this issue ?
If you need to add extra field value which comes dynamically as a form value then you may add it in views.py:
from .forms import OrderCreateForm
from .models import OrderItem
def order_create(request):
cart = Cart(request)
if request.method == 'POST':
form = OrderCreateForm(request.POST)
if form.is_valid():
country = form.cleaned_data.get("country")
order = form.save(commit=False)
order.country = country
order.save()
Your model must have this field in the field list. And need to make the migration. Otherwise field value will not be saved rather it will triggered the error.

Dynamic content on page is not showing

I am trying to generate dynamic content on page with the onclick event of js but there is error in console "Uncaught SyntaxError: Unexpected identifier"
html
{% load static %}
<head>
<script type="text/javascript">
function myFunction() {
// document.getElementById("demo").innerHTML = "Paragraph changed.";
document.getElementById("demo").innerHTML = "{{ Malegender }}";
}
</script>
</head>
<body>
<div class="container mt-5">
<h1>Gender</h1>
<hr>
<h5>{{ mygender.as_p }}</h5>
<h1 id="demo">Hello</h1>
<div class="container">
<button type="submit" name="gender_submit" class="btn btn-success" onclick="myFunction()">Confirm</button>
</div>
</div>
</body>
views.py
def Gender(request):
gender_selection = GenderForm()
male_detail = MaleForm()
Male = False
print("Value of male",Male)
if 'gender_submit' in request.POST:
male_detail = MaleForm(data=request.POST)
Male = True
print("Value of male d",Male)
print("Value of male a",Male)
return render(request, 'Gender.html',{"mygender":gender_selection,"Malegender":male_detail})
forms.py
class MaleForm(forms.ModelForm):
class Meta:
model = GenderModel
fields = ('genderMale','genderMale1','genderMale2','genderMale3','genderMale4')
models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
class GenderModel(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
gender_choice=(('Male','Male'),('Female','Female'),)
gender = models.CharField(max_length=6,choices=gender_choice,null=True,)
genderMale = models.BooleanField(default=False)
genderMale1 = models.BooleanField(default=False)
genderMale2 = models.BooleanField(default=False)
genderMale3 = models.BooleanField(default=False)
genderMale4 = models.BooleanField(default=False)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
GenderModel.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.gendermodel.save()
when I click on button the error on the console is showing. I am trying to display that content when the user clicks on the button
this question suggests never using the onclick attribute. Instead you can try using an event listener like this:
<button type="submit" name="gender_submit" class="btn btn-success"
id="gender_submit_button">Confirm</button>
<script type="text/javascript">
function myFunction() {
document.getElementById("demo").innerHTML = "{{ Malegender }}";
}
document.getElementById('gender_submit_button').addEventListener('click', myFunction);
</script>
Also, make sure that your script is placed after the button and demo.

Handle form submission in bootstrap modal with ajax and class based views

i'm quite new using django and i've been stuck in this problem for several days.
I have a form.Form in a bootstrap modal on my template with only 1 field (email_field) and basically i need to submit that form via ajax, check if that email address is registered in the database, then send an invitation to that email and close the modal. if the email is not registered show the form errors without closing the modal. I've tried with different examples but can find the solution either because the examples don't handle errors or the form is not inside a modal or not using class based views
.
I'm having 2 issues with my code:
Not sure what to return in my view if the form is valid or invalid and how to handle errors in my js code to show them on the modal.(return tha form to render the errors or a JSON response??).
After the first success submission is made the form cannot be used again.(The size of the submit button changes and if you click it return a error : CSRF token missing or incorrect)
Form.py
class CollaboratorForm(forms.Form):
email_address = forms.EmailField(required=True,widget=forms.TextInput(attrs={'class': 'form-control focus-text-box', 'type': 'email',
'placeholder': 'Enter email'}))
def clean_email_address(self):
email = self.cleaned_data['email_address']
if not User.objects.filter(email=email):
raise forms.ValidationError('This user is not registered')
return email
def sendEmail(self, datas):
message = "Hello, " + datas['user_name']+" "+ datas['email_from'] + " invited you to collaborate in an existing project. Follow this link if you are interested " + datas['invitation_link']
msg = EmailMessage('Invitation from ' + datas['user_name'],
message, to=[datas['email_to']])
msg.send()
Template.html (project_detail.html)
<script src="{% static '/experiments/js/invite_collaborator.js' %}"></script>
<div class="bootstrap-modal modal fade in" id="collaboratorModal" style="display: none;">
<div class="modal-body">
<form action="{% url 'experiments:invite-collaborator' project_id=project.id %}" method="post" id=collaborator-form >
{% csrf_token %}
<div class="form-group">
{% if collaborator_form.errors %}
<ol>
{% for error in collaborator_form.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
<label class="control-label">Invite someone by email</label>
<div class="input-group mt10">
{{ collaborator_form }}
<span class="input-group-btn">
<input name="collaborator-commit" onClick="invite({{project.id}});" class="btn btn-primary" data-disable-with="Send Invitation" id="invite-button" type="submit">
</span>
</div>
</div>
</form>
</div>
</div>
Url.py
urlpatterns = [
url(r'^(?P<project_id>[0-9]+)/invite_collaborator$', views.InviteCollaborator.as_view(), name='invite-collaborator'),
]
View.py
class ProjectDetail(DetailView):
model = Project
template_name = 'experiments/project_detail.html'
pk_url_kwarg = 'project_id'
def get_context_data(self, **kwargs):
context = super(ProjectDetail, self).get_context_data()
project = get_object_or_404(Project,pk=self.kwargs["project_id"])
context["project"] = project
context["collaborator_form"] = CollaboratorForm()
return context
class InviteCollaborator(FormView):
form_class = CollaboratorForm
template_name = 'experiments/project_detail.html'
def post(self, request, *args, **kwargs):
collaborator_form = CollaboratorForm(data=request.POST)
project_id = request.POST['project_id']
current_project = Project.objects.get(id=project_id)
datas={}
if collaborator_form.is_valid():
cleaned_data = collaborator_form.cleaned_data
email_address = cleaned_data.get('email_address')
user = User.objects.get(pk=request.user.id)
invitation_link = "http://exp.innovationhackinglab.com/projects/"+ str(current_project.id) + "/join/" + current_project.invitation_key
datas['user_name'] = user.first_name + ' ' + user.last_name
datas['email_from'] = user.email
datas['email_to'] = email_address
datas['invitation_link'] = invitation_link
collaborator_form.sendEmail(datas)
data = simplejson.dumps("Success")
return HttpResponse(data, content_type='application/json')
else:
return super(InviteCollaborator, self).form_invalid(collaborator_form)
invite_collaborator.js
function invite(project_id) {
$('#collaborator-form').submit(function(e) {
e.preventDefault();
$.ajax({
data: $(this).serialize()+'&'+$.param({ 'project_id': project_id }),
type: $(this).attr('method'),
url: $(this).attr('action'),
});
$('#collaboratorModal').modal('toggle');
$('#collaboratorModal').on('hidden.bs.modal', function () {
$(this).find("input,textarea,select").val('').end();
});
});
};
I've read about using success: & error: on the js file but don't know how to use it without the appropriate "return" in the view
You need to have two ajax methods, one to get the form (as raw html) and one to post the form. You will have a corresponding get and post method in your view too.
get function of your view class:
def get(self, request, *args, **kwargs):
form = CollaboratorForm()
return render(request,'template.html',{'form':form})
def post(self, request, *args, **kwargs):
form = CollaboratorForm(request.POST)
if form.is_valid():
//save form
//return whatever you want to show on successful form submission
else:
//return bound form as html with errors
return render(request,'template.html',{'form':form})
js functions
have two seperate ajax function one for get (showing form) one for post(submitting form)
If you want to use templates on server's side, with FormView and ajax, I would suggest splitting templates into two parts - wrapper and form, load only wrapper via TemplateView, then fetch form with ajax. That allows you to send form with ajax and put responses (like form with errors) in wrapper.
Change your HTML template - take modal body's to another file, ex.:
project_detail.html
<script src="{% static '/experiments/js/invite_collaborator.js' %}"></script>
<div class="bootstrap-modal modal fade in" id="collaboratorModal" style="display: none;">
<div class="modal-body" id="collaboratorModalContent">
</div>
</div>
project_detail_content.html
<form action="{% url 'experiments:invite-collaborator' project_id=project.id %}" method="post" id=collaborator-form >
{% csrf_token %}
<div class="form-group">
{% if collaborator_form.errors %}
<ol>
{% for error in collaborator_form.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
<label class="control-label">Invite someone by email</label>
<div class="input-group mt10">
{{ collaborator_form }}
<span class="input-group-btn">
<input name="collaborator-commit" onClick="invite({{project.id}});" class="btn btn-primary" data-disable-with="Send Invitation" id="invite-button" type="submit">
</span>
</div>
</div>
</form>
FormView should handle GET and POST - first one to get the form in project_detail_content.html into modal, second for sending email. Fortunately, FormView can do all that for us! (I don't know from where you get that project variable though)
View.py
class InviteCollaborator(FormView):
form_class = CollaboratorForm
template_name = 'experiments/project_detail_content.html'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
project_id = self.request.POST['project_id']
current_project = Project.objects.get(id=project_id)
datas={}
cleaned_data = form.cleaned_data
email_address = cleaned_data.get('email_address')
user = User.objects.get(pk=request.user.id)
invitation_link = "http://exp.innovationhackinglab.com/projects/"+ str(current_project.id) + "/join/" + current_project.invitation_key
datas['user_name'] = user.first_name + ' ' + user.last_name
datas['email_from'] = user.email
datas['email_to'] = email_address
datas['invitation_link'] = invitation_link
form.sendEmail(datas)
data = simplejson.dumps("Success")
return HttpResponse(data, content_type='application/json')
Note few things - we use FormView, so for GET request it will return content of project_detail_content.html with CollaboratorForm, and on POST, same template with form and errors if form is invalid, or JSON with Success message otherwise.
What happened to project_detail.html? We will use TemplateView to create thw wrapper:
Url.py
urlpatterns = [
url(r'^invite_collaborator$', TemplateView.as_view(template_name="project_detail.html")),
url(r'^(?P<project_id>[0-9]+)/invite_collaborator/form$', views.InviteCollaborator.as_view(), name='invite-collaborator'),
]
Finally, JS
invite_collaborator.js
// In JS you need to make sure you fetch form from /project_id/invite_collaborator/form each time you show modal
$(document).ready(function(e) {
$('#collaboratorModalContent').load('invite_collaborator');
});
// Then, on submit we simply send data and handle response with success and error.
// With our current View, invalid form will generate successful response with form and error, so we need to check
function invite(project_id) {
$('#collaborator-form').submit(function(e) {
e.preventDefault();
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: $(this).serialize()+'&'+$.param({ 'project_id': project_id }),
success: function ( response, status, xhr, dataType ) {
if( dataType === 'json' ){
//Make sure response is 'Success' and close modal
$('#collaboratorModal').modal('toggle');
$('#collaboratorModal').on('hidden.bs.modal', function () {
$(this).find("input,textarea,select").val('').end();
});
});
};
}
else {
// It's not JSON, it must be HttpResposne with forms and errors, so it goes into modal's body
$('#collaboratorModalContent').html(response)
}
}
});
I still don't know where and how you get/set you project variable, so maybe TemplateView is bad choice...

Django formsets no data on post

I have a form that uses multiple formsets. The formset forms are added dynamically via JS. I have been looking at a few different places to help myself along.
Add a dynamic form to a django formset using javascript in a right way
And
A nice post by Kevin Dias - Django class-based views with multiple inline formsets
The problem I am having is that when I post my data the outer form has data, but non of my formsets actually have any data in the cleaned_data dictionary when I start looping through them. Any thoughts on what I may be missing? The second formset is added with a very similar JS method.
Forms
class ShippingForm(Form):
is_partial = BooleanField(label='Partial?')
class ShippingActualProduct(Form):
box_no = CharField(label='Box Number', max_length=3)
upc = CharField(
widget=forms.TextInput(attrs={'class':'upcAjax'}),
)
serial_no = CharField(
label='Serial Number',
widget=forms.TextInput(attrs={'class':'serial'}),
)
sku = CharField(
widget=forms.TextInput(attrs={'class':'skuAjax'}),
)
description=CharField(label='Description')
on_hand=CharField(label='On Hand')
def __init__(self, *args, **kwargs):
super(ShippingActualProduct,self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = True
self.helper.form_class = 'form-inline'
class ShippingNonInventoryProduct(Form):
non_box_no = CharField(label='Box Number', max_length=3)
quantity = IntegerField()
description = CharField()
serial_no = CharField()
def __init__(self, *args, **kwargs):
super(ShippingNonInventoryProduct,self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = True
self.helper.form_class = 'form-inline'
ActualProductFormSet = formset_factory(ShippingActualProduct, extra=1, can_delete=True)
NonInventoryProductFormSet = formset_factory(ShippingNonInventoryProduct, extra=1, can_delete=True)
Views
class ShippingCreate(FormView):
template_name = 'jinja2/Shipping/shipping_create.html'
form_class = ShippingForm
success_url = reverse_lazy('shipping_create')
def get_context_data(self, **kwargs):
context = super(ShippingCreate, self).get_context_data(**kwargs)
input_invoice_no = self.request.GET['invoice_no']
try:
self.object = Invoice.objects.get(invoice_no = input_invoice_no)
context['invoice'] = self.object
except Invoice.DoesNotExist:
messages.error(self.request, 'We were unable to find invoice number %s, please try again' % input_invoice_no)
try:
context['voucher'] = Voucher.objects.get(voucher_no = self.object.voucher_no)
except Voucher.DoesNotExist:
messages.error(self.request, 'We were unable to find an installation voucher for claim number %s' % self.object.voucher_no)
context['actual_items_forms'] = ActualProductFormSet(prefix='actual')
context['non_inventory_items_forms'] = NonInventoryProductFormSet(prefix='non')
context['form'] = ShippingForm()
return context
def get(self, request, *args, **kwargs):
self.object = None
context = self.get_context_data()
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
actual_product = ActualProductFormSet(self.request.POST, prefix='actual')
non_inv_product = NonInventoryProductFormSet(self.request.POST, prefix='non')
if actual_product.is_valid():
for product in actual_product:
data = product.cleaned_data
sku = data.get('sku')
context={}
return render(request, self.template_name, context)
Template
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load static from staticfiles %}
{% load socialaccount %}
{% load sitetree %}
{% block headcss %}
{{ block.super }}
<link rel="stylesheet" href="{% static "css/shipping.css" %}">
{% endblock headcss %}
{% block headjs %}
{{ block.super }}
<script src="{% static "js/shipping.js" %}"></script>
{% endblock headjs %}
{% block content %}
{% block messages %}
{{ block.super }}
{% endblock messages %}
<div class="container-fluid">
<form action="." method="post">
<div>
<h3>Item information:</h3>
{% csrf_token %}
{{ actual_items_forms.management_form }}
<div id="actual-items-form-container">
</div>
Add Item
</div>
<div>
<h3>Non Inventory Items Shipped:</h3>
{{ non_inventory_items_forms.management_form }}
<div id="non-inv-items-form-container">
</div>
Add Item
</div>
{{ form.as_p }}
<input type="submit" value="Complete" class="submit btn btn-success" />
</form>
</div>
{% include "jinja2/hub/loading_modal.html" %}
{% endblock content %}
</html>
The JavaScript
function getNewActualItemForm() {
// unbind this ajax call from the overlay displayed when looking data up.
$(document).unbind(".items");
var count = $('#actual-items-form-container').children().length;
$.get("/forms/actualitemform",function(data, status){
var form = data.replace(/__prefix__/g, count);
// Get the html contents of the form, all together to iterate over.
var htmlForm = $('<form>').html(form).contents();
// Just grab the children of that form
var rows = htmlForm.children();
// loop through the inputs locating the DELETE input and label.
$(rows).each( function(index, value) {
var row = $(this);
var del = $(row).find('input:checkbox[id $= "-DELETE"]');
// Only move forward if we have found the DELETE input
if (del.length){
//Write the form ot the Dom so the search for the label will succeed.
$('div#actual-items-form-container').append(form);
var label ='label[for="id_form-' + count + '-DELETE"]';
$(label).hide();
}
});
// update form count
$('#id_actual-TOTAL_FORMS').attr('value', count+1);
// some animate to scroll to view our new form
$('html, body').animate({
scrollTop: $('#actual-item-btn').position().top-200
}, 800);
// get the max box number.
var maxBoxNo = getBoxNo();
// Set focus to the next UPC
var boxID = '#id_form-var-box_no';
var upcID = '#id_form-var-upc';
var nextBox = boxID.replace('var',count);
var nextUpc = upcID.replace('var',count);
// set the box number for the new line.
$(nextBox).val(maxBoxNo);
$(nextUpc).focus();
});
return count;
}
There were two issues that were causing the troubles here.
I had the crispy forms helper rendering the form tags for each formset. This is a bad idea. Setting form_tag = False fixed that.
I had forgotten to set the prefix argument on my formsets in the views I created to grab the next form via JavaScript.
Once both of these were implemented the data was now available from the forms on submission.

Categories