Django: Deal Wizard and Auto ID - django

Say I have field named address_line_1 at form, once I render this it is generated as such:
<input id="id_1-address_line_1" type="text" name="1-address_line_1">
And I use this form at different pages however it doesn't have a consistency, one is generated as id_1-address_line_1 while some other same form at another view as id_1-address_line_1 thus it hurty my javascript side. How can I drop the id_X part so that the rendered id has the exact same name as the field name address_line_1

FormClass(prefix="prefix_to_display")

You can get rid of id_ by using the auto_id parameter. In your view class:
def get_form_kwargs(self, step=None):
kwargs = super().get_form_kwargs(step)
kwargs['auto_id'] = True
return kwargs
In order to get rid of the X- you'll have to render the fields manually. This can be done with custom templates, or in the field definitions:
address_line_1 = forms.CharField(
widget=forms.TextInput(attrs={'id': 'address_line_1'})
)

Related

Multiple choice form within an advanced search form

I am trying to create a multiple choice form where any combination of languages can be chosen. It's within a search form field:
class AdvancedSearchForm(SearchForm):
terms_show_partial_matches = forms.BooleanField(required=False,
label=_("Show partial matches in terms")
)
definitions_show_partial_matches = forms.BooleanField(required=False,
label=_("Show partial matches in definitions")
)
case_sensitive = forms.BooleanField(required=False,
label=_("Case sensitive")
)
...
I would like to implement something like this:
filter_by_part_of_speech = forms.ModelChoiceField(
queryset=PartOfSpeech.objects.all(), required=False,
label=_("Filter by part of speech")
)
However, it needs to be a multiple choice field so that any of the values can be chosen. Ideally though, I'm looking for a form where checkboxes are already checked. So something like this:
LANG_CHOICES = (
("1", "lang1"),
("2", "lang2"),
("3", "lang3"),
("4", "lang4"),
)
filter_by_language = forms.MultipleChoiceField(choices=Language.objects.all().filter(name__in=LANG_CHOICES).values(), required=False, label=_("Filter by language"))
The filter is called from the view with something like this:
tqs = tqs.filter(language=language_filter)
Now although the search works fine, the values are not displayed. On the other hand, they are displayed if I fill up a list and simply write:
choices=list(lang_list)
But then, obviously, the search is not actually performed.
Therefore, my questions are:
Can the constructor be adapted to display the values correctly?
Should I rather implement the filter in the view? If so, how?
Am I using the correct type of form or are there better options, such as providing a list of checkboxes that are checked by default?
I am using Django 2.2 (planning to upgrade soon) for now.
The template file simply refers to the search def in the view, which calls the advanced search form and the others:
{% block breadcrumbs_item %}{% trans "Advanced Search" %}{% endblock %}
Not that relevant I think, but here is the Language model:
class Language(models.Model):
iso_code = models.CharField(primary_key=True, max_length=10, verbose_name=_("ISO code"))
name = models.CharField(max_length=50, verbose_name=_("name"))
description = models.TextField(verbose_name=_("description"), null=True, blank=True)
class Meta:
verbose_name = _("language")
verbose_name_plural = _("languages")
def __str__(self):
return _("%(language_name)s (%(iso_code)s)") % {'language_name': self.name, 'iso_code': self.iso_code}
EDIT: Clarification based on Milo Persic's reply.
The request method in the view for the search functionality (that includes calling AdvancedSearchForm) is GET.
if request.method == 'GET' and 'search_string' in request.GET:
if "advanced" in request.path:
search_form = AdvancedSearchForm(request.GET)
else:
search_form = SearchForm(request.GET)
if search_form.is_valid():
tqs = Translation.objects.all()
dqs = Definition.objects.all()
data = search_form.cleaned_data
...
language_filter = data['filter_by_language']
case_filter = data['case_sensitive']
qs = apply_filters(tqs, dqs, language_filter, case_filter, orig_search_string)
etc.
The search form and results are appended and returned as context to the template:
return render(request, template_name, context)
Within AdvancedSearchForm, variables using ModelChoiceField as well as BooleanField are defined. The ModelChoiceField is the one I'm trying to replace with something more user friendly:
filter_by_language = forms.ModelChoiceField(
queryset=Language.objects.all(), required=False,
label=_("Filter by language")
)
The idea is that the default search result will include all four languages (it won't be more than that) but that any combination of them can be unchecked if so desired. With ModelChoiceField, it seems that only one can be chosen.
I'm still trying to learn if ModelMultipleChoiceField is the correct choice, but this is what I have so far:
filter_by_language = forms.ModelMultipleChoiceField(required=True, queryset=Language.objects.all().filter(name__in=LANG_CHOICES).values(),
widget=forms.CheckboxSelectMultiple)
Nothing next to "filter by language".
Only using the names in LANG_CHOICES instead of the numbers actually result in something but it shows the query set. Narrowing it down using "value_list('name')" shows only the language name but still with the syntax, e.g. "('English',)".
mmcf_qs = Language.objects.all().filter(name__in=LANG_CHOICES).values_list('name')
filter_by_language = forms.ModelMultipleChoiceField(required=True, queryset=mmcf_qs)
I'm trying to figure out how to extract the text but maybe this isn't the right approach.
I would use something like CheckBoxSelectMultiple in the form:
(disclaimer, I do not know if this is supported as written in django 2.2)
filter_by_language = forms.ModelMultipleChoiceField(required=True,
queryset=Language.objects.all().filter(name__in=LANG_CHOICES).values(),
widget=forms.CheckboxSelectMultiple)
And there are plenty of stack posts on how to make the initial values checked, like this one.
Also, if you end up with a long list of check boxes (a likely scenario using this widget), you can style them into columns for better UX, like so:
<style>
.my-language-checkboxes {
column-width: 25%;
column-count: 4;
column-fill: balance;
}
</style>
<div id="my-language-checkboxes">
{{ form.filter_by_language }}
</div>
As for the view, I'll make an assumption that you know how to write a basic view and it's the save method you need. In that case, you'll need something like:
if request.method == 'POST:
if form.is_valid():
form.save_m2m()
This also assumes that you are relating languages back to a parent object via a ManyToManyField, which is a common scenario.

Hov can get the translated values in select?

I get select with values Without and With. How can I get already translated values Без and С in django.po in a select?
models.py
CONFIRMATION_WITHOUT = 'without'
CONFIRMATION_OTHER = 'other'
CONFIRMATION_WITH = 'with'
CONFIRMATION_CHOICES = (
(CONFIRMATION_WITHOUT, _('Without')), #Без
(CONFIRMATION_OTHER, _('Other')), #Другое
(CONFIRMATION_WITH, _('With')), #С
)
income_proof = models.CharField(_('proof'), max_length=255, choices=CONFIRMATION_CHOICES, default=CONFIRMATION_WITHOUT)
#[u'without', u'with']
forms.py
income_proof = forms.ModelChoiceField(queryset=CreditPayment.objects.values_list('income_proof', flat=True).distinct(), widget=forms.Select(attrs={'class': 'selectpicker form-control', 'title':_("Income proof")}))
html
{{ form.income_proof }}
It is possible to make in the form, for example?
<select>
<option value = "CONFIRMATION_WITHOUT">Без</option>
</select>
For the form, you should not use a ModelChoiceField [Django-doc]. Indeed, you here do not select a model object, but a value. You thus should use a ChoiceField [Django-doc] instead.
As for the options, I think you want to use CONFIRMATION_CHOICES, since by using a queryset, you query the database, and you thus are only able to pick income_proofs that are already picked by other records.
from app.models import CONFIRMATION_CHOICES
from django import forms
class MyForm(forms.ModelForm):
income_proof = forms.ChoiceField(
choices=CONFIRMATION_CHOICES,
widget=forms.Select(
attrs={'class': 'selectpicker form-control', 'title':_('Income proof')}
)
)
or if you only want the values that were selected, you can use:
from app.models import CONFIRMATION_CHOICES
from django import forms
class MyForm(forms.ModelForm):
income_proof = forms.ChoiceField(
choices=[],
widget=forms.Select(
attrs={'class': 'selectpicker form-control', 'title':_('Income proof')}
)
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
render = dict(CONFIRMATION_CHOICES)
self.fields['income_proof'].choices = [
(k, render.get(k, k))
for k in CreditPayment.objects.values_list('income_proof', flat=True).distinct()
]
Here the __init__ is called when we construct the form. We first let the super constructor do the work to create the fields, then make a dictionary of the CONFIRMATION_CHOICES.
Next, we perform a query (the same one you used) to get the database values for income_proof, and we use the dictionary to map these to the corresponding translations. We thus yield a list of 2-tuples as choices for that form field.
We here thus use the choices= parameter [Django-doc] which should contain:
choices
Either an iterable of 2-tuples to use as choices for this field, or a
callable that returns such an iterable. This argument accepts the same
formats as the choices argument to a model field. See the model
field reference documentation on choices for more details. If the
argument is a callable, it is evaluated each time the field's form is
initialized. Defaults to an empty list.

Django forms: Is it possible to have multiple drop down menus for different tags within a field?

I have a form in a formset where I would like to display multiple drop down menus under a single field 'tests'. I have achieved this in the form of having a single dropdown menu within 'optgroup' tags (see image below).
I guess this way you can only choose a single value.
However, is it possible to 'nest' these drop downs? I.e have them all under one field 'tests', but be able to have several dropdowns with 'tags' and choose results for each tag? Or do I need a field for each 'tag'?
My forms.py:
class ReportForm(forms.ModelForm):
summary = forms.CharField(
widget=forms.Textarea(attrs={'rows':3, 'cols':70}),
label='',
required=False)
tests = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = ClinicallyReportedSample
fields = ('id', 'summary', 'tests', 'hilis_reported')
def __init__(self, *args, **kwargs):
json_data = kwargs.pop('json_data', None)
super(ReportForm, self).__init__(*args, **kwargs)
crs_obj = self.instance
for j in json_data:
if j['lab_no'] == str(crs_obj):
json = j
summary = json['summary']
self.fields['summary'].initial = summary
self.fields['reported'].label = crs_obj
tests = json.get('tests', None)
if tests:
test_choices = (
('mutated', 'mutated'),
('mutated - see comments', 'mutated - see comments'),
('awaiting confirmation', 'awaiting confirmation'),
)
self.fields['tests'] = forms.ChoiceField(
required=True,
label='Current or repeat samples?',
choices=((k, test_choices) for k in tests),
)
What I get now:
I would instead want a dropdown for each gene, and those choices. Do I need to make a field for each gene? The problem I have with doing this is that each result can have 0-10 genes, and this would be incredibly difficult to render in a HTML table.
Thanks
You probably want to implement something template/client-side to handle that, such as Chosen or Selectize.js (see the option groups examples).
Then on your form class implement a clean and/or clean_[field_name] method if you need to get your selected data in the format you want.

populating prefixed fields - bug or feature?

Having the following models:
#models.py
Column(models.Model):
name = models.CharField()
Input(models.Model):
column = ForeignKey(Column)
text = models.CharField()
And the following form:
class InputForm(Modelform):
class Meta():
model=Input
The following works:
c = Column.objects.get(...)
i=InputForm({'column':c.id})
i.is_valid() #true
In my application I am generating many forms, to avoid clashes I prefix it:
i=InputForm({'column':c.id}, prefix=prfx()) #prfx() is dynamically generated
i.errors # ({'column':['This field is required']})
i.data['column'] is still the right value
I also tried:
i.column = c
i.errors # ({'column':['This field is required']})
How do I populate the column field?
I cannot save the form as long as it does not validate
EDIT What I am trying to achieve:
I am building dynamic forms:
form_list = [InputForm(column, prefix=column.id) for column in col_list]
In the template I iterate over this form list (and I want to set the column field to be invisible.
{% for form in form_list %}
{{form.as_ul}}
{%endfor%}
This form then shall be processed by an AjaxView. The relation between text and column is achieved by the invisible field.
The first parameter to the form is the input data, which is usually request.POST. If you render a Form with a prefix, you will see that all form html elements will have a prefixed name, for example <input name='yourprefix_text' /> etc. If you POST such a form to your Django app, the POSTed data will have the prefix, too.
So, if you are using a prefix, the input data needs to be prefixed, too:
f = InputForm({'yourprefix_column': c.id}, prefix='yourprefix')
Usually, it is a better idea to use the initial parameter to the form for default values, because otherwise the form is always bound, and this has some consequences, for example default/initial values for other fields are will not work.
f = InputForm(prefix='yourprefix', initial={'column': c})
# or ...
form_list = [InputForm(prefix=column.id, initial={'column': column})
for column in col_list]
If you want to always set the column to a programatically determined value, and not allow the user to change it, it is better to not include the field in your form, and set the field manually after saving the object:
f = InputForm(request.POST)
if f.is_valid():
instance = f.save(commit=False)
instance.column = c
instance.save()
To make your field hidden, you can change the widget, as described in the documentation:
class InputForm(ModelForm):
class Meta:
widgets = {
'column': forms.HiddenInput,
}
# ...

Django form cache or reload issue

I have a page to insert value to db.
After inserting am loading another page with a drop down listing the db values
But the inserted value is not listing in dropdown
The problem is not with transaction/commit etc. The query to retrieve the data for the drop down in second form is correct.
Form1(first page)
class Organization(forms.Form):
orgList = getOrgUnitList()
orgUnit = forms.CharField(label=u'Organization Name',
max_length=50,
error_messages={'required':'Organization name is required field.'})
parentOrg= forms.ChoiceField(label=u'Parent Organization',
choices=[(u'Select',u'Select')]+orgList,
error_messages={'required':'Organization name is required field.'})
Form2(Second page)
class User(forms.Form):
orgUnitList = getOrgUnitList()
email = forms.EmailField(label=u'Email',
max_length=50,
error_messages={'required':'Email is required field'})
orgUnit = forms.ChoiceField(label=u'Organizational Unit',
choices=orgUnitList,
error_messages={'required':'Organizational unit is required field'})
Query
def getOrgUnitList():
orgUnitList = list(OrganizationUnit.objects.values_list
('OrgUnitID','OrgUnitName').order_by('OrgUnitName'))
return orgUnitList
but when i tried to bind the choices in view it is working
working code
*view*
def user()
template = get_template('AddUser.html')
form = AddUser()
orgUnitList = getOrgUnitList()
del objAdminUIDA
form.fields['orgUnit'].widget.choices=orgUnitList
variables = RequestContext(request,{'form':form})
output = template.render(variables)
del form
return HttpResponse(output)
But i cant give the dropdown choice in view i want to give choices in form.i need a solution for form2
Firstly, the orgList is evaluated in form definition, that's why choices don't change. You should place getOrgUnitList in form's __init__ (or in some other method).
Secondly, you don't pass any data to the form, may be you want
form = AddUser(request.POST or None)