I want to add help text/tool tip when a user hovers over a form field.
I have a model form based on this model:
class MyModel(TimeStampedModel):
MY_CHOICES = [tuple([x,x]) for x in range(1,8)]
p1 = models.IntegerField("P1", default='1', help_text='text1')
p2 = models.IntegerField("P2", default='1', , help_text='text2')
Parent = models.ForeignKey(ParentModel, on_delete=models.CASCADE)
The form itself looks like:
class MyModelForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'id-CaseForm'
self.helper.form_class = 'blueForms'
self.helper.form_method = 'post'
self.helper.form_tag = False
self.helper.help_text_inline = False
self.helper.form_show_labels = False
self.helper.layout = Layout(
Row(Field(PrependedText('p1', 'field_label1', wrapper_class='col-12 col-lg-6 pe-0 stretchprepend'))),
Row(Field(PrependedText('p2', 'field_label2', wrapper_class='col-12 col-lg-6 pe-0 stretchprepend'))))
CHOICES = [tuple([x,x]) for x in range(1,8)]
p1 = IntegerField( label='field_label1', widget=Select(choices=CHOICES))
p2 = IntegerField( label='field_label2', widget=Select(choices=CHOICES))
class Meta:
model = MyModel
fields = ['p1', 'p2',]
And this is displayed as a crispy form in the template:
{% crispy MyModelForm %}
I want the user to see some help text when they hover over the fields. This help text could be the help_text from the model, or I am happy to put it somewhere else (although it should go in either the model or the form, not in the template). Any help appreciated.
Related
I have a model form that have multiple choice fields. using AJAX to update form choic fields upon changed field.
Model:
class Student(models.Model):
CLASSROOM = 0
GROUPROOM = 1
HOMEROOM = 3
STUDENT_RECORD_TYPES = [
(CLASSROOM,_("Classroom")),
(GROUPROOM,_("Group")),
(HOMEROOM,_("Home Edu")),
]
school = models.ForeignKey(School,on_delete=models.CASCADE,blank=False,related_name='student_records')
grade = models.ForeignKey(Grade,on_delete=models.CASCADE,blank=False,related_name="student_records")
record_type = models.PositiveSmallIntegerField(_("Record Type"),choices=STUDENT_RECORD_TYPES,default=0)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['school','grade', 'record_type'],
name='unique_school_grade_record'
),
]
def __str__(self):
return "Record ID: {}".format(self.pk)
Views.py:
def update_students(request,pk):
updated_table=None
student_record = get_object_or_404(Student,pk=pk)
if request.POST:
form = StudentForm(request.POST or None,instance=student_record)
if form.is_valid():
form.save()
messages.success(request,_("Student record Updated Successfully!"))
#Getting data for view
updated_table = update_students_table(request)
else:
messages.error(request,_("Invalid Input, Please check!"))
else:
form = StudentForm(request.GET or None,instance=student_record)
context = {}
# load form template
context['form'] = form
form_template_path = "components/forms/student_update.html"
html_form = loader.render_to_string(form_template_path, context, request)
context['form'] = html_form
return JsonResponse(context)
Forms.py:
class StudentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'form-control'
self.fields['school'].widget.attrs['class'] += ' select2'
#the issue stands here
#self.data.get('school') returns none even if its exist in form data
if self.data.get('school'):
self.fields['grade'].queryset = Grade.objects.filter(school=self.data.get('school'))
else:
self.fields['grade'].queryset = Grade.objects.none()
class Meta:
model = Student
fields = '__all__'
the strange behavior drives me crazy because when I reselect the school it updates the grade choices normally (with no option selected!), but when I open edit instance form the data is there but grade field have no options in it!
I have a form in django views.py.
diagnosis.patient is a foreignkey of demographics.patient_id.
my_diagnosis form is not valid because patient is empty. Is there a way to fix this?
def input(request):
context = RequestContext(request)
print context
ret = cache.get('input-rendered')
if request.method == 'POST':
my_demographics = DemographicForm(request.POST, prefix="demo")
my_diagnosis = DiagnosisForm(request.POST, prefix='diag')
if (my_demographics.is_valid() ):
print "dem and diag validation"
my_demographics_object = my_demographics.save(commit=False)
my_demographics_object.author = request.user
my_demographics_object.save()
#my_diagnosis = DiagnosisForm(request.POST, prefix='diag', initial={'patient':my_demographics_object.patient_id} )
print "my dem id"
print my_demographics_object.patient_id
if (my_diagnosis.is_valid()):
my_diagnosis_object=my_diagnosis.save(commit=False)
my_diagnosis_object.patient = my_demographics_object.patient_id
my_diagnosis_object.author = request.user
my_diagnosis_object.save()
This is my Diagnosis form in forms.py:
class DiagnosisForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DiagnosisForm, self).__init__(*args, **kwargs)
self.fields['diagnosis_circumstances_date']= forms.DateField(label=('Date'),required=False,
widget=DateTimePicker(options={"format": "YYYY-MM-DD",
"pickTime": False,
"startDate": "1900-01-01"}))
self.helper=FormHelper(form=self)
self.fields['icd_10_desc']= forms.ModelChoiceField(queryset=icd_10.objects.all(),
widget=autocomplete_light.ChoiceWidget("icd_10Autocomplete"))
self.fields['icd_10_desc'].label = "ICD-10 description"
diagnosis_option_value = (
('b-thalassaemia syndromes', 'b-thalassaemia syndromes',),
('a-thalassaemia syndromes', 'a-thalassaemia syndromes'),
('Sickle cell syndromes', 'Sickle cell syndromes'),
('Other haemoglobin variants','Other haemoglobin variants'),
('Red cell membrane disorders','Red cell membrane disorders'),
('Red cell enzyme disorders','Red cell enzyme disorders'),
('Congenital dyserythropoietic anaemias','Congenital dyserythropoietic anaemias')
)
self.fields['diagnosis_option']=forms.MultipleChoiceField(choices=diagnosis_option_value, widget=forms.CheckboxSelectMultiple())
diagnosis_circumstances_value = (
('Antenatal diagnosis','Antenatal diagnosis'),
('Neonatal diagnosis','Neonatal diagnosis'),
('By the presence of affected related','By the presence of affected related'),
('Clinical diagnosis', 'Clinical diagnosis'),
('Other','Other')
)
self.fields['diagnosis_circumstances']=forms.MultipleChoiceField(choices=diagnosis_circumstances_value, widget=forms.CheckboxSelectMultiple())
#self.fields['patient'].queryset = Demographic.objects.filter(patient_id=self.instance.patient)
self.helper.field_class = 'col-md-8'
self.helper.label_class = 'col-md-3'
#self.helper.form_class = 'forms-horizontal'
self.helper.layout = Layout(
Fieldset (
# 'patient',
'<b>Diagnosis information</b>',
Div(
#HTML(u'<div class="col-md-2"></div>'),
Div('age_of_diagnosis',css_class='col-md-6'),
Div('age_at_onset_of_symptoms',css_class="col-md-6"),
css_class='row',
),
'diagnosis_option',
'record_of_genotype',
'icd_10_desc',
'icd_10_code',
'orpha_code',
'comment',
),
FormActions(
Submit('submit', "Save changes"),
Submit('cancel',"Cancel")
),
)
self.helper.form_tag = False
self.helper.form_show_labels = True
class Meta:
model = Diagnosis
exclude = ['patient']
exclude = ('author',)
list_display = ('title', 'pub_date', 'author')
This is the result of my_diagnosis.errors:
<ul class="errorlist"><li>patient<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
If you are setting the patient in the view, then just leave the patient form out of the list of fields for you model form:
DiagnosisForm(forms.ModelForm):
class Meta:
model = Diagnosis
fields = ('myfield1', 'myfield2', ...)
You can use exclude if you prefer:
DiagnosisForm(forms.ModelForm):
class Meta:
model = Diagnosis
exclude = ('author', 'patient',)
The problem in your current form is that you have
exclude = ['patient']
exclude = ('author',)
The second exclude replaces the first. You should have:
exclude = ['author', 'patient']
See the model form docs on selecting the fields to use for more info.
I have a form that works perfectly fine
models.py:
class Location(models.Model):
title = models.CharField(max_length=300)
description = models.TextField(null=True, blank=True)
address = models.TextField(null=True, blank=True)
class Review (models.Model):
location = models.ForeignKey(Location)
description = models.TextField(null=True, blank=True)
views.py:
class Create(CreateView):
model = coremodels.Review
template_name = 'location/test.html'
fields = '__all__'
def form_valid(self, form):
form.save()
return HttpResponseRedirect('')
return super(Create, self).form_valid(form)
html:
<form action="" method="post">{% csrf_token %}
{{ form}}
<input type="submit" value="Create" />
</form>
When I open the site I can select a location and give a review over the create button. However, now I am dependent on the prefilled values in the Location class. What if I want that a user can directly create a description as well as a title of the location (I don't want the title to be in class Review) I already tried looking for this in the docs but couldn't find anything. Somewhere I read that I could create two different forms that handle to different things but I'm not sure how to merge that all in the class Create. Is there something like model = coremodels.Review & coremodels.Location and then in the html I could do
{{form.title}}
{{form.description}}
Anyone any ideas or search terms I could look for?
Thanks !
EDIT
Ok thanks to Ruddra and this post , here is the working solution. I had to edit it a little in order to get it working for me,
class SomeForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SomeForm, self).__init__(*args, **kwargs)
self.fields['title'] = forms.CharField(label='Title', required = False)
self.fields['description'] = forms.CharField(label='Description', required = False)
self.fields['location'] = forms.ModelChoiceField(queryset= Location.objects.all(), required = False) # This line is for making location not required in form field for input
class Meta:
model = Review
fields = '__all__'
def save(self, commit=True):
"""
It will save location from choice field or inputs of title and description
"""
instance = super(SomeForm, self).save(commit=False)
if instance.location_id:
instance.save()
else:
new_location = Location.objects.create(title=self.cleaned_data['title'])
instance.location = new_location
instance.save()
return instance
and the views.py
class Create(CreateView):
model = coremodels.Review
template_name = 'location/test.html'
form_class = SomeForm
Unfortunately you can't use two models like this, you have to write a form and do the stuffs there. For example:
class SomeForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'] = forms.CharField(label='Title', required = False)
self.fields['description'] = forms.CharField(label='Description', required = False)
self.fields['location'] = forms.ModelChoiceField(queryset= Location.objects.all(), required = False) # This line is for making location not required in form field for input
class Meta:
model = Review
fields = '__all__'
def save(self, commit=True):
"""
It will save location from choice field or inputs of title and description
"""
instance = super().save(commit=False)
if instance.location:
instance.save()
else:
new_location = Location.objects.create(title=self.cleaned_data['title'], description = self.cleaned_data['description']])
instance.location = new_location
instance.save()
return instance
Use it in view:
class Create(CreateView):
model = coremodels.Review
template_name = 'location/test.html'
form = SomeForm
And you have make sure that, location is nullable or not required in form (I have added that to the example)
I would like to have the values for latitude and longitude to always display a dot (".") instead of a comma (",") when showing the latitude and longitude form fields.
This seems to be tricky with crispy forms.
In the template which shows the model's fields I just use
{% crispy form %}
But I did not find in the documentation of crispy forms how to do sth. like
{{ value|unlocalize }}
as provided by the Django documentation. Since the crispy forms is supposed to be generic as in the following code example, I don't know where to set the trigger.
extract from forms.py
class CrispyForm(ModelForm):
"""
This form serves as a generic form for creating and updating items.
"""
helper = None
def __init__(self, cancel_button, *args, **kwargs):
form_action = kwargs.pop('form_action', None)
model_name = kwargs.pop('model_name', None)
super(CrispyForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
if form_action is not None:
action = reverse(form_action)
else:
action = ""
# Form attributes
self.helper.form_method = 'post'
self.helper.form_action = action
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-10'
# Save button, having an offset to align with field_class
save_text = _('Save %(model)s') % {'model': model_name}
cancel_text = _('Cancel')
self.helper.layout.append(Submit('save_form', save_text, css_class="btn btn-primary col-sm-offset-2 save_item"))
self.helper.layout.append(Submit('cancel', cancel_text, css_class="btn btn-primary"))
and here is a form which has model fields latitude and longitude
class SomeItemCreateForm(CrispyForm):
def __init__(self, *args, **kwargs):
kwargs['form_action'] = 'create_someitem_url'
kwargs['model_name'] = self._meta.model._meta.verbose_name
super(SomeItemCreateForm, self).__init__(False, *args, **kwargs)
class Meta:
model = SomeItem
fields = '__all__'
The SomeItem model has a longitude and latitude field amongst others.
Check out the Layout Docs
You'll basically want to create a custom template for your field and then use that.
Your code will be a bit like this:
form = SomeItemCreateForm(...)
form.helper.layout = Layout(
Field('latitude', template='custom_field_template.html'),
Field('longitude', template='custom_field_template.html')
)
I hope that helps.
I am having some issues trying to update some records in Django:
When i try to update some record, the app insert a new one, I don't know why i have this behavior.
Model
class DetalleRecepcion(models.Model):
id_proveedor = models.ForeignKey(Proveedor,db_column='id_proveedor',primary_key=True, verbose_name='Proveedor')
anio = models.IntegerField( null=False)
mes = models.IntegerField(verbose_name='Mes')
fecha_recepcion = models.DateField(verbose_name='Fecha Recepcion')
usuario = models.CharField(max_length=15, blank=True)
num_archivos = models.IntegerField(primary_key=True, verbose_name='No de archivos')
class Meta:
managed = False
db_table = 'mpc_detalle_recepcion'
view:
#login_required(login_url='/login/')
def DetRecView(request):
idp = request.GET.get('i')
anio = request.GET.get('a')
mes = request.GET.get('m')
if request.method == 'POST':
r = DetalleRecepcion.objects.get(id_proveedor=idp,anio=anio,mes=mes)
form = DetRecForm(request.POST or None, instance =r)
if form.is_valid():
form.save()
return HttpResponse('<script type="text/javascript">window.close()</script>')
else:
r = DetalleRecepcion.objects.get(id_proveedor=idp,anio=anio,mes=mes)
r.usuario = request.user
form = DetRecForm(instance=r)
return render_to_response('detrec.html',
{'form':form},
context_instance=RequestContext(request))
Form:
class DetRecForm(forms.ModelForm):
fecha_recepcion = forms.DateField(widget=DateInput(),)
def __init__(self,*args,**kwargs):
super(DetRecForm,self).__init__(*args,**kwargs)
self.helper = FormHelper(self)
self.helper.layout = Layout(
Field('id_proveedor',
'anio',
'mes',
'usuario',
readonly = True
),
Fieldset('',
'fecha_recepcion',
'num_archivos',
Submit('save','Grabar'),
HTML('<a class="btn btn-danger" id="cerrar">Cancelar</a>')
)
)
class Meta:
model = DetalleRecepcion
I use the same view and form definition for others models to render edit forms and with this other models works great and the records are updated.
I don't understand what it's happen.
I rewrite the form, view definition for this model and I don't know what it is the problem.
The database is a legacy database and the tables doesn't have any kind of relationship or constraint.
By the way I am using Django crispy form
Thanks in advance
If you using same form for create and update views, then you need provide clean method on your unique field and raise ValidationError when object exists.
But in your case, I assuming you using Composite Primary Key on fields: id_proveedor, num_archivos, you should override clean method of the whole form:
class DetRecForm(forms.ModelForm):
fecha_recepcion = forms.DateField(widget=DateInput())
def __init__(self, *args, **kwargs):
super(DetRecForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.layout = Layout(
Field('id_proveedor',
'anio',
'mes',
'usuario',
readonly=True
),
Fieldset('',
'fecha_recepcion',
'num_archivos',
Submit('save', 'Grabar'),
HTML('<a class="btn btn-danger" id="cerrar">Cancelar</a>')
)
)
def clean(self):
cleaned_data = super(DetRecForm, self).clean()
id_proveedor = self.cleaned_data['id_proveedor']
num_archivos = self.cleaned_data['num_archivos']
qs = self.Meta.model.objects.filter(id_proveedor=id_proveedor, num_archivos=num_archivos)
if self.instance:
qs = qs.exclude(pk=self.instance.id)
if qs.count() > 0:
raise forms.ValidationError(u'Such object exists!')
return cleaned_data
class Meta:
model = DetalleRecepcion
Try to get object by pk for instance
DetalleRecepcion.objects.get(pk=kwargs['pk'])