I am trying to build a dynamic form in Django template.
I have two dropdown selections. When the user selects an option in the first one, the available choices in the second dropdown changes dynamically. Here is the jQuery code I use to change the choices in the second dropdown element.
$(document).on("change", "[id^=id_form][id$=brand]", function() {
var $value = $(this).val();
var $id_prefix = $(this).attr('id').slice(0, -5);
$auto_type = $("#" + $id_prefix + "auto_type");
$.ajax({
type: 'GET',
url: '/europarts/filter/auto/type/with/brand/' + String($value),
success: function(data) {
$auto_type.html(data);
}
});
});
You can see that it fetches some HTML from a link which is basically a bunch of options which will replace the current ones. Here is the code:
{% for auto_type in auto_types %}
<option value="{{ auto_type.id }}">{{ auto_type.name }}</option>
{% endfor %}
Here are my forms:
class ListForm(forms.ModelForm):
class Meta:
model = List
fields = ['ref_no']
class ProductCreateForm(forms.Form):
brand = forms.ModelChoiceField(queryset=Brand.objects.all(), initial=Brand.objects.first())
auto_type = forms.ModelChoiceField(queryset=AutoType.objects.filter(brand=Brand.objects.first()), empty_label=None)
part_no = forms.CharField(max_length=50)
description = forms.CharField(max_length=255)
quantity = forms.IntegerField()
unit = forms.CharField(max_length=20, initial='piece(s)')
cost_price = forms.IntegerField(required=False)
selling_price = forms.IntegerField(required=False)
I am using a dynamic formset jquery library to save multiple Products with a List.
This works fine till now!
However after I change the options like this and try to submit the form, the form page gets loaded again with the default options in the second dropdown and not the changed ones along with an error that says:
Select a valid choice. That choice is not one of the available choices.
Here is the request.POST value when the form is submitted.
<QueryDict: {'form-MAX_NUM_FORMS': ['1000'], 'form-1-description': ['lskdjf'], 'form-1-auto_type': ['3'], 'form-MIN_NUM_FORMS': ['0'], 'form-0-quantity': ['1'], 'form-0-brand': ['2'], 'form-0-part_no': ['293847'], 'form-0-auto_type': ['2'], 'form-0-unit': ['piece(s)'], 'form-0-description': ['slkdfj'], 'form-1-part_no': ['928374'], 'form-TOTAL_FORMS': ['2'], 'form-1-quantity': ['1'], 'form-INITIAL_FORMS': ['0'], 'ref_no': ['REF230498203948'], 'csrfmiddlewaretoken': ['WYN08Hi2YhwTSKPS8EnLaz94aOz33RVfFMjwYeHr3rMxBImxn7ggSLYHwguIbuL0'], 'form-1-brand': ['2'], 'form-1-unit': ['pieces']}>
You can see 'form-1-auto_type': ['3'] which was 'form-1-auto_type': ['1'] originally. The form is submitted when the original value is there, but after the dynamic change in value it shows the above error.
This error is shown for the second dropdown. How can I fix this?
When the form is being validated it checks whether the option exists in the choices for the field, in this case:
queryset=AutoType.objects.filter(brand=Brand.objects.first())
It works only when you select an option from the first brand.
To make it work you need to update the choices for the auto_type field before it validates the data or it might be easier not to limit the choices of auto_type to any brand and then update the select element in JavaScript when the page loads to show only the options of the first brand.
Related
I have a client script runs on Suitelet.
On Suitelet we have a sublist which has many select columns.
Let's say we have Select 1 and Select 2.
Select 2 options should be different if you change the option in Select 1.
Could this be done in Client Script? or in that Suitelet?
Any help would be appreciated.
`
var select1= sublist.addField({
id: 'custpage_select1',
label: 'Select1',
type: ui.FieldType.SELECT
});
var select2= sublist.addField({
id: 'custpage_select2',
label: 'Select2',
type: ui.FieldType.SELECT
});
`
In client script when the field is changed, I want to change options of Select 2 depend on Select 1.
function fieldChanged(scriptContext){
if(scriptContext.fieldId=="custpage_select1"){
let page = currentRecord.get();
var select2 = page.getField({fieldID:"custpage_select2"});
select2.removeSelectOption({value:"value1"});
}
}
But this shows me an error that
Cannot read properties of null
Thank you
Yes. You can use the N/currentRecord module in your client script with getField() and removeSelectOption().
let page = currentRecord.get();
const select2 = page.getField({
fieldId: 'custpage_select2'
});
select2.removeSelectOption({
value: 'option1'
});
I have a form with a dependent drop-down. This secondary drop-down is hidden whenever the primary option selected does not have any secondary options, and when the page first loads. Whenever the form is submitted, only the first field gets cleared out, since most of the time the drop-downs remain the same, however, since the script works whenever there is a change in the primary drop-down, since the load upon does not constitute a change, it just keeps the selected/submitted option on the primary drop-down, and will just display an empty secondary drop-down, even when the primary option selected does have secondary options. I got most of the JS from the drop-down from a tutorial, as I am not very familiar with it. For a more visual understanding:
This is the form when the page first loads
When you select an option that has secondary options, the other dropdown appears
After you select a Station and submit, the Employee # clears, but the other two are supposed to remain, however, when the page reloads upon submission, it looks like this, and the station has been cleared according to the debugger since there are none technically. I don't care so much about the station clearing, but more about not having an empty drop-down that should not be empty.
And when I look at the data that remained in the form, only the work area stayed, because the dependent dropdown does not load until you select another option from the drop down, and if you wanted to be able to see the Box Assembly options again, you'd have to click another option and then go back to Box Assembly (for example)
How could I fix this issue? Is there a way to force the javascript to attempt to load first so that it checks if the option that remained does have the secondary options, whether it has been triggered or not?
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'employee_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('employee_number').remote_field, site, attrs={'id':'employee_number_field'}),
}
fields = ('employee_number', 'work_area', 'station_number')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['station_number'].queryset = Station.objects.none()
if 'work_area' in self.data:
try:
work_area_id = int(self.data.get('work_area'))
self.fields['station_number'].queryset = Station.objects.filter(work_area_id=work_area_id).order_by('name')
except (ValueError, TypeError):
pass
elif self.instance.pk:
self.fields['station_number'].queryset = self.instance.work_area.stations.order_by('name')
views.py
def enter_exit_area(request):
enter_without_exit = None
exit_without_enter = None
if request.method == 'POST':
form = WarehouseForm(request.POST)
if form.is_valid():
emp_num = form.cleaned_data['employee_number']
area = form.cleaned_data['work_area']
station = form.cleaned_data['station_number']
# Submission logic
form = WarehouseForm(initial={'employee_number': '', 'work_area': area, 'station_number': station})
else:
form = WarehouseForm()
return render(request, "operations/enter_exit_area.html", {
'form': form,
'enter_without_exit': enter_without_exit,
'exit_without_enter': exit_without_enter,
})
urls.py
urlpatterns = [
url(r'enter-exit-area/$', views.enter_exit_area, name='enter_exit_area'),
path('ajax/load-stations/', views.load_stations, name='ajax_load_stations'),
]
At the end of this html is the script that handles the dependent drop-down
enter_exit_area.html
{% extends "operations/base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" data-stations-url="{% url 'operations:ajax_load_stations' %}" novalidate >
{% csrf_token %}
<div>
<div>
<label>Employee #</label>
{{ form.employee_number }}
</div>
<div>
<label>Work Area</label>
{{ form.work_area }}
</div>
<div class="col-xs-8" id="my-hidden-div">
<label>Station</label>
{{ form.station_number }}
</div>
</div>
</form>
<script>
function loadStations() {
var url = $("#warehouseForm").attr("data-stations-url");
var workAreaId = $(this).val();
var $stationNumberField = $("#{{ form.station_number.id_for_label }}");
$.ajax({
url: url,
data: {
'work_area': workAreaId
},
success: function (data) {
$("#my-hidden-div").show(); // show it
$stationNumberField.html(data);
// Check the length of the options child elements of the select
if ($stationNumberField.find("option").length === 1) {
$stationNumberField.parent().hide(); // Hide parent of the select node
} else {
// If any option, ensure the select is shown
$stationNumberField.parent().show();
}
}
});
}
$("#id_work_area").change(loadStations);
$(document).ready(loadStations);
</script>
{% endblock main %}
station_number_dropdown_options.html
<option value="">---------</option>
{% for station in stations %}
<option value="{{ station.pk }}">{{ station.name }}</option>
{% endfor %}
I see that you have $(document).ready(loadStations);.
But the problem is that in loadStations, you do var workAreaId = $(this).val();.
this will be document, and $(document).val() is an empty string.
Either hardcode the selector in loadStations:
// var workAreaId = $(this).val();
var workAreaId = $("#id_work_area").val();
Or trigger the change from the element instead:
$("#id_work_area").change(loadStations);
// $(document).ready(loadStations);
$("#id_work_area").change();
i have an observable array that get its values from REST
$.getJSON(rootModel.soho()).
then(function (StudentCourseView1) {
$.each(StudentCourseView1.items, function () {
self.data.push({
StudentId: this.StudentId,
CourseId: this.CourseId,
Enrollmentdate :this.Enrollmentdate,
Notes :this.Notes,
Id : this.Id,
CourseName : this.CourseName,
StudentName: this.StudentName
});
});
});
self.dataSource = new oj.ArrayTableDataSource(
self.data,
{idAttribute: 'Id'}
);
and i have created select-one like this
<oj-select-one id="select1" style="max-width:20em"
options={{data}}
value="{{data.Id}}"
options-keys="[[data.StudentName]]" >
</oj-select-one>
the select one show undefined for all it options
Are you sure the ajax call is working, and there are no errors in console? Is the drop-down showing a list of blank values?
If yes, then the only missing element is adding option-keys attribute, to explain what data to show the user, and the key for each piece of data.
<oj-select-one id="select1" style="max-width:20em"
options={{data}}
options-keys="{{optionKeys}}"
value="{{StudentId}}">
JS
//You can change this acc. to your convenience.
self.optionKeys = {value:"Id", label:"StudentName"};
I'm new to coding and django and I'm struggling to find the solution to the following problem having reviewed the answers I've found.
Im creating a search form with multiple fields. When the user selects the first field category (and before hitting search) I would like to dynamically change the queryset for the second field sub_category such that only related values are shown.
I have models.py as follows:
class Product(models.Model):
category = models.ForeignKey("Category")
sub_category = models.ForeignKey("SubCategory")
class Category(models.Model):
name = models.CharField(max_length=256)
class SubCategory(models.Model):
category = models.ForeignKey("Category")
name = models.CharField(max_length=256)
And my forms.py includes:
class BasicSearchForm(forms.Form):
category = forms.ModelChoiceField(
label='Category',
queryset=Category.objects.all(),
to_field_name="name",
empty_label=None,
initial="Red")
sub_category = forms.ModelMultipleChoiceField(
required=False,
label='Type',
queryset= SubCategory.objects.all(),
to_field_name="name",
widget=forms.Select)
And my views.py includes:
def search(request):
if request.method == 'POST':
form = BasicSearchForm(request.POST)
if form.is_valid():
category = form.cleaned_data['category']
sub_category = form.cleaned_data['sub_category']
return render(request, 'myapp/search.html', {'form': form})
else:
form = BasicSearchForm()
return render(request, 'myapp/search.html', {'form': form})
And finally the search.html includes:
<form class="search-form" role="search" action="/search/" method="get">
{{ form }}
<input type="submit" value="Search" />
</form>
I've played around with a few answers but nothing seems to work. I'd really appreciate some help. Thanks in advance!
Update:
Thanks for the feedback. As a result I updated the following:
In my urls.py:
urlpatterns = [
url(r'^ajax/update_subcategories/$', views.update_subcategories, name='update_subcategories'),
And in my views.py:
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(sub_category, safe=False)
And I have this in my myapp/search.html:
{% block javascript %}
<script>
$("#id_category").change(function () {
var category = $(this).val();
$.ajax({
url: '{% url "myapp:update_subcategories" %}',
data: {
'category': category,
},
success: function (response) {
var new_options = response;
alert(new_options[0].name); // works
$('#id_sub_category').empty();
$.each(new_options, function(key, value) {
$('#id_sub_category')
.append($('<option>', { value : key })
.text(value.name));
});
}
});
</script>
{% endblock %}
Update: The sub_category options were showing as [object Object] until I changed value to value.name and it looks like it's working. I'll test it out and close unless there are any comments.
Update: Im still having an issue with the browser back button. When a user clicks back the dropdown values have changed back to the original queryset rather than the updated version.
You can't do this from Django views side, ie, backend. You could try an ajax request for implementing this kind of requests, by sending a GET request to the server for populating the drop-down or whatever you are into.
For a simple example, you could refer
here
How do I POST with jQuery/Ajax in Django?
EDIT
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(dict(sub_category=sub_category))
Then in ajax response you could grab it like response.data.sub_category
Use ajax to send the category and retrieve subcategory elements.
For the category, send it via get request, and using the orm return the subcategories in a json format which you can show using jQuery.
I have a model look like this:
class filled(stateobject):
first = models.IPAddressField(verbose_name=_("First address"))
second = models.CharField(max_length=39, verbose_name=_("Second address")
Embedding these model into Django-forms by following code :
class hostCreateForm(forms.ModelForm):
class Meta:
model = filled
widgets = {
'user': forms.HiddenInput()
}
In views.py:
def address_create(request):
if required.method=='POST':
form = HostCreateForm(request.POST)
if form.is_valid():
host = form.save()
return redirect_to(request, url=host.get_absolute_url())
extra_context = {
'form': HostCreateForm(initial={'user': request.user.pk})
}
return direct_to_template(request, 'networks/Address_form.html', extra_context)
In :Address_form.html
{{ form.as_p }}
When i run above code's it shows me a form containing two fields i.e. first and second. I want to edit this form and add a new char field by named GMT, So that i am able to pass a following javascript variable i.e gmtHours in this new created field
var d = new Date()
var gmtHours = -d.getTimezoneOffset()/60;
Finally when user's click on sumbit button at template it save a form content i.e.
1. first
2. second
3. newly created gmtHours
I am newbie in Django-model. Want your help :)
Add a hidden field to your ModelForm that will hold the date:
class hostCreateForm(forms.ModelForm):
class Meta:
model = filled
widgets = {
'user': forms.HiddenInput()
}
# additional hidden field
datetime = forms.DatetimeField(widget=forms.HiddenInput)
You can then assign it a value in the javascript.