Making form with deform and whould like to change the pageShema class depending on the choices made by the user.
Ex. if he selects option 1 from selectwidget, show him one set of fields, if case of other choice - another.
How to do this?
You can use jquery and select with the "name" attribute.
Also, you can use the jquery "parent()" function to get the container of the input you are interested in showing/hiding.
For instance, in your schema do something like:
# This is the "<select>"
choices = (('yes','Yes'),
('no', 'No'))
bar = colander.SchemaNode(colander.String(),
widget=deform.widget.SelectWidget(values=choices))
# This is the input that should appear only when the "yes" value is selected
foo = colander.SchemaNode(colander.String())
Then, in your template, add somethig like:
<script>
$( document ).ready(function() {
// ensure that the "foo" input starts hidden
var target = $("input[name=foo]").parent().parent();
target.hide();
$("select[name=bar]").on('change', function(){
var valueSelected = this.value;
if (valueSelected == "yes") {
target.show();
} else {
target.hide();
}
});
});
</script>
Related
I'm new to Django and any help is appreciated, How can I restrict the choice option in one field based on a previous field. For example, if I select 'dog' for animal, I want to remove 'chocolate' from the FOOD_CHOICE. Thank you!!!
ANIMAL_CHOICE = (
('a','cat'),
('b','dog'),
('c','fish'),
)
FOOD_CHOICE = (
('a', 'chocolate'),
('b', 'kittySnack'),
('c', 'steak'),
)
class Animal(models.Model):
animal = models.CharField(max_length=1, choices= ANIMAL_CHOICE)
food = models.CharField(max_length=1, choices= FOOD_CHOICE)
As explained here, you should do the validation on the model form or in the model clean method and raise a ValidationError there.
Here is an example, in which you could override the clean method of your model's form:
forms.py
class AnimalForm(ModelForm):
class Meta:
model = Animal
fields = "__all__"
def clean(self):
cleaned_data = super(AnimalForm, self).clean()
animal = self.cleaned_data.get("animal")
food = self.cleaned_data.get("food")
if animal == "b" and food == "a": # Might not work, but this is the general idea
raise forms.ValidationError("Chocolate can't be selected with Dogs")
N.B.: At the line where I commented that it might not work, you'll have to debug a bit. I don't remember (and I can't test right now) if the cleaned_data returns a tuple or the actual value, or the humand-readable value.
Now, I guess that you want your select in your HTML to dynamically change. For the frontend, you'll need to do a bit of JavaScript. There are many ways on how to do it with JS, but here is one:
(in your template, between <script> tags)
var selectAnimal = document.getElementById("select-animal");
var selectFood = document.getElementById("select-food");
selectAnimal.addEventListener("change", function() {
if(this.value == "a")
{
// remove from select the "chocolate" option
for (var i=0; i<selectFood.length; i++) {
if (selectFood.options[i].text == 'chocolate')
selectFood.remove(i);
}
}
else {
// checking if "chocolate" is in select or not
let has_chocolate = false;
for (var i=0; i<selectFood.options.length; i++){
if (selectFood.options[i].text == "chocolate"){
has_chocolate = true;
}
}
if (!has_chocolate){ // if "chocolate" is missing
option = document.createElement("option");
option.text = "chocolate";
option.value = "a";
selectFood.add(option);
}
}
});
Im stuck for days, im using this plugin django-datatables-view and I need to render ForeignKey related value in serverside datatables, it's like: {{data.pasien_id.name}} in typical django template. but that way not work with serverside datatables, and there is no documentation anywhere to achieve that. the code shown below.
Models.py
class Pasien(models.Model):
nama = models.CharField(max_length=40, null=True)
class Log_book(models.Model):
pasien = models.ForeignKey(Pasien, on_delete=models.PROTECT, null=True)
Views.py
class logbook_json(BaseDatatableView):
model = Log_book
columns = ['id', 'pasien_id']
order_columns = ['id','pasien_id']
def render_column(self, row, column):
if column == 'id':
return escape('{0}'.format(row.id))
else:
return super(logbook_json, self).render_column(row, column)
def filter_queryset(self, qs):
filter_customer = self.request.GET.get('search[value]', None)
if filter_customer:
customer_parts = filter_customer.split(' ')
qs_params = None
for part in customer_parts:
q = Q(id__icontains=part) | Q(pasien_id__icontains=part)
qs_params = qs_params | q if qs_params else q
qs = qs.filter(qs_params)
return qs
templates.html datatables load,
<script class="init" type="text/javascript">
$(document).ready(function () {
$('#responsive-datatablex').DataTable({
// ...
searching: true,
processing: true,
serverSide: true,
stateSave: true,
"ajax": "{% url 'logbook_json' %}",
});
});
</script>
It's easier if you get your JSON to return in a flat structure (rather than nested):
{
"name": name,
"id": id
}
Then you need to define data for the column to read values from the JSON. This can be quite tricky so it pays to read the documentation carefully.
You must define your datatables column correctly so that it can read this data. Note that there are "orthogonal" data types (ie. for sorting, display etc):
columns: [
{
"title": "Name",
"data": "name",
"render": {
"_": "name",
},
"searchable": true,
"orderable": true,
"defaultContent": "N/A"
}
]
There are other ways of achieving the same, so refer to the docs. The key thing is that the DataTables column definition must match what is returned in the JSON.
Some tips:
Try to get one column working by itself first.
Load the JSON in a separate tab so that you can see that it is being rendered correctly. Note that there are some specific rules about rendering the serverside response, I expect that django-datatables-view honours these but I haven't checked.
In QuickFix, how can I get the name of the tag from the tag number using Python? For example, for OrdStatus, how do I convert tag number 5 to the String "OrdStatus_CANCELED"?
.NET:
If you are using QuickFIX/N, you can achieve this using a DataDictionary instance with whatever data source you want (e.g., FIX42.xml). Note that you can get the DataDictionary instance associated with a given Session or the application itself with Session's properties SessionDataDictionary and ApplicationDataDictionary, respectively.
Consider this trivial C# program:
namespace QuickFixTests
{
using System;
using QuickFix;
using QuickFix.DataDictionary;
using QuickFix.Fields;
class Program
{
static void Main(string[] args)
{
var qfm = new Message();
qfm.SetField(new OrdStatus('4'));
var ordStatus = qfm.GetField(Tags.OrdStatus);
var dd = new DataDictionary("FIX42.xml");
Console.WriteLine(dd.FieldsByTag[39].EnumDict[ordStatus]); // Prints CANCELED
}
}
}
C++/Python:
The C++ DataDictionary class has a method getValueName:
bool getValueName( int field, const std::string& value, std::string& name ) const
{
ValueToName::const_iterator i = m_valueNames.find( std::make_pair(field, value) );
if(i == m_valueNames.end()) return false;
name = i->second;
return true;
}
The following snippets (with comments added) from one of the Python DataDictionary unit tests show how to use getValueName given a DataDictionary instance.
# Create a DataDictionary
def setUp(self):
self.object = fix.DataDictionary()
# Add a dummy value
self.object.addValueName( 23, "BOO", "VALUE_23_BOO" )
# Test that the dummy value's name in the dictionary matches what was set
self.assertEquals( "VALUE_23_BOO", self.object.getValueName(23, "BOO", "")
I have some models which have TextField() with limitation like "only 85 available" and I want to write a js code which counts the remaining free space and shows the user.
but in admin templates, i see only some templates and i dont know which template i should put the code in. and also, i only want to count remaining space on limited fields and not on all fields of model.
i have tried with template admin > edit_line > stacked.html, but nothing shows up in admin
(I created a form field widget to achieve this: https://github.com/timmyomahony/django-charsleft-widget)
You don't override admin templates, instead you create a widget (based on the default TextInput widget) that adds some extra JS to show the remaining characters:
class CharsLeftInput(forms.TextInput):
def render(self, name, value, attrs=None):
if value is None:
value = ''
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
if value != '':
final_attrs['value'] = force_unicode(self._format_value(value))
maxlength = final_attrs.get('maxlength',False)
if not maxlength:
return mark_safe(u'<input%s />'%flatatt(final_attrs))
current = force_unicode(int(maxlength) - len(value))
html = u"""
<span class="charsleft charsleft-input">
<input %(attrs)s />
<span><span class="count">%(current)s</span> characters remaining</span>
<span class="maxlength">%(maxlength)s</span>
</span>
""" % {
'attrs':flatatt(final_attrs),
'current':current,
'maxlength':int(maxlength),
}
return mark_safe(html)
class Media:
css = {'screen':('charsleft-widget/css/charsleft.css',),}
js = ('charsleft-widget/js/charsleft.js',)
and the javascript:
jQuery(function($){
$.fn.charsLeft = function(options){
var defaults = {
'source':'input',
'dest':'.count',
}
var options = $.extend(defaults, options);
var calculate = function(source, dest, maxlength){
var remaining = maxlength - source.val().length;
dest.html(remaining);
/* Over 50%, change colour to orange */
p=(100*remaining)/maxlength;
if(p<25){
dest.addClass('orange');
}else if(p<50){
dest.addClass('red');
}else{
dest.removeClass('orange red');
}
};
this.each(function(i, el) {
var maxlength = $(this).find('.maxlength').html();
var dest = $(this).find(options.dest);
var source = $(this).find(options.source);
source.keyup(function(){
calculate(source, dest, maxlength)
});
source.change(function(){
calculate(source, dest, maxlength)
});
});
};
$(".charsleft-input").charsLeft({
'source':'input',
'dest':".count",
});
});
and you can use the widget on particular CharField fields in your admin model:
from django.contrib import admin
from widgets import CharsLeftInput
class TestForm(forms.Form):
field_one = forms.CharField(widget=CharsLeftInput())
....
class TestAdmin(admin.ModelAdmin):
form = TestForm
you will probably need to play around with it to get exactly what you want, but you can see the code in the repo along with some more examples
I've got this model :
class QuestionInstance(models.Model):
questionsSet = models.ForeignKey(QuestionsSet)
question = models.ForeignKey(Question)
parent = models.ForeignKey('self',null=True,blank=True)
optional = models.BooleanField(default=False)
I'd like to create a dropdown, which user could choose one QuestionInstance.
It has to be filtered with questionsSet.
I've tested using a modelform like this, but it's not working :
(based on this How do I filter values in a Django form using ModelForm?)
class FormQuestionSelect(ModelForm):
instanceList = forms.ChoiceField(choices=[(questionInstance.id, questionInstance.question) for questionInstance in QuestionInstance.objects.all()])
class Meta:
model = QuestionInstance
fields = ('instanceList', )
widgets = {
'instanceList': Select(attrs={'class': 'select'}),
}
def __init__(self, questionsSet=None, **kwargs):
super(FormQuestionSelect, self).__init__(**kwargs)
if questionsSet:
#Tested many code here to filter, None of them worked :(
#Is that possible to create instanceList there ?
I'm not sure using a modelform is a good idea for this kind of purpose.
A modelform is great when create or update a model instance.
When using specific forms, like in this case, I'm using a custom form in template :
View
questionInstanceList = QuestionInstance.objects.filter(questionsSet=questionsSet)
Template
<select name="questionInstanceSelect">
{% for instance in questionInstanceList %}
<option value="{{ instance.id }}">{{ instance.question.text }}</option>
{% endfor %}
</select>
and process them this way :
instanceList = request.POST.get('questionInstanceSelect')
I'm quite sure there's a proper way.
You can change queryset of ModelChoiceField after form instantiation either in form __init__ or in view. but this wouldn't solve issue on client side. When someone change QuestionSet Question selectbox will remain the same
To update queryset just update form field's one
form.fields['parent'].queryset = (QuestionInstance.objects
.filter(questionsSet=questionsSet))
or if You change form __init__
self.fields['parent'].queryset = (QuestionInstance.objects
.filter(questionsSet=questionsSet))
But One should remember that if questionsSet is changed on client side parent list will remain the same.
Would You consider to add client side code updating parent's choice list
Let me explain a bit.
You have model
class QuestionInstance(models.Model):
questionsSet = models.ForeignKey(QuestionsSet)
question = models.ForeignKey(Question)
parent = models.ForeignKey('self',null=True,blank=True)
optional = models.BooleanField(default=False)
Here parent field link to self(the same model).
Let us use `Model form for this model
class FormQuestionSelect(ModelForm):
class Meta:
model = QuestionInstance
ModelForm will create fields for each model field with the same name
then after Its creation we update ModelChoiceField (created for ForeignKey) queryset
If you want your field to be dynamic, you need to use jQuery and ajax for this functionality. I have given code for use in django admin. You can tweak it a bit, if you want to use it in custom pages. But the concept remains same for both.
question_set_change.js
(function($){
$(function(){
$(document).ready(function() {
$('#id_questionsSet').bind('change', question_set_change);
$('#id_question > option').show();
if ($('#id_questionsSet').val() != '') {
var question_set_id = $('#id_questionsSet').val();
$.ajax({
"type" : "GET",
"url" : "/product_change/?question_set_id="+question_set_id,
"dataType" : "json",
"cache" : false,
"success" : function(json) {
$('#id_question >option').remove();
for(var j = 0; j < json.length; j++){
$('#id_question').append($('<option></option>').val(json[j][0]).html(json[j][1]));
}
}
});
}
});
});
})(django.jQuery);
// based on the questionsSet, questions will be loaded
var $ = django.jQuery.noConflict();
function question_set_change()
{
var question_set_id = $('#id_questionsSet').val();
$.ajax({
"type" : "GET",
"url" : "/product_change/?question_set_id="+question_set_id,
"dataType" : "json",
"cache" : false,
"success" : function(json) {
$('#id_question > option').remove();
for(var j = 0; j < json.length; j++){
$('#id_question').append($('<option></option>').val(json[j][0]).html(json[j][1]));
}
}
})(jQuery);
}
Include the following in views.py:
import simplejson
from django.shortcuts import HttpResponse
from app.models import Question
def question_choices(request):
question_list = []
question_set_id = request.GET.get('question_set_id')
questions = Question.objects.filter(question_set = question_set_id)
[question_list.append((each_question.pk,each_question.name)) for each_question in questions]
json = simplejson.dumps(question_list)
return HttpResponse(json, mimetype='application/javascript')
In urls.py:
from app.views import question_choices
urlpatterns = patterns(
(r'^question_set_change/', question_choices),
)
In admin.py where you want to load question based on question_set:
class Media:
js = ['/path/to/question_set_change.js',]