Remove Label from django crispy form layout - django-forms

I am using djano Crispy form and twitter bootstrap, i have a form.
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_method = 'POST'
self.helper.form_class = 'form-horizontal'
self.helper.layout = Layout(
Div(
Div(HTML("""<label> Trust Admin Name</label>"""), css_class='span3'),
Div('first_name', css_class='span3'),
Div('middle_name', css_class='span3'),
Div('last_name', css_class='span3'),
),
)
How to remove the label first_name, middle_name and last_name from layout.

http://django-crispy-forms.readthedocs.org/en/latest/form_helper.html#helper-attributes-you-can-set
Given that this is a bootstrap view, you can do:
self.helper.form_show_labels = False

Related

Django crispy forms field type date

I am trying to get date picker functionality like HTML <input type="date"> in django crispy form layout field below.
Field('invoice_date', type='date'),
However, its not working. Full snippet below:
def __init__(self, *args, **kwargs):
super(InvoiceForm, 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(
Field('customer'),
Field('invoice_num'),
Field('invoice_date', type='date'),
Fieldset('Add detail', Formset('details')),
HTML("<br>"),
ButtonHolder(Submit('submit', 'save')),
)
)
I've managed this by making simple widget for a form field.
You have to set input type "date" and provide possible default date value in ISO format:
widgets.py
from django.forms import DateInput
class DatePicker(DateInput):
input_type = "date"
def format_value(self, value):
return value.isoformat() if value is not None and hasattr(value, "isoformat") else ""
In your forms.py change widget for field 'invoice_date' to DatePicker and you will enable picker functionality:
forms.py
from django import forms
from .widgets.py import DatePicker
class YourForm(forms.Form):
...
invoice_date = forms.DateField(widget=DatePicker())
...

Select a valid choice. <some choice> is not one of the available choices

I am trying to build a Form which will show a choice field with options. Now these options changes for different kind of user. I am able to set the values in the choice field from view. The problem is when I click on "Submit" all the fields become empty and get the error like "Select a valid choice. 2 is not one of the available choices."
Logic to find the correct list of companies to show in the choice field.
def get_my_companies(user):
if user.profile.company.is_customer:
return (user.profile_set.company.id,user.profile_set.company.name)
elif user.is_superuser:
companies = Company.objects.filter(is_customer=True)
list_of_companies = [(c.id, c.name) for c in companies]
return list_of_companies
else:
list_of_groups = list(user.groups.values_list('name',flat = True))
list_of_companies = []
# ... some more logic to find appropriate list of companies for each such user
return list_of_companies
The logic is working correctly and the values are getting populated on the page load. Only problem occurs when I submit the form.
views.py
def home(request):
my_companies = get_my_companies(request.user)
myForm = device_readings_form()
myForm.fields['company'].choices = my_companies
if request.method == 'POST':
myForm = device_readings_form(request.POST)
is_historical = True
if myForm.is_valid():
cd = myForm.cleaned_data
company = cd.get('company')
#device = cd.get('device')
date_from = cd.get('date_from')
date_to = cd.get('date_to')
print(date_from)
print(date_to)
# .... some more stuff to work. Then I return the render using template
context = {
'readings': data,
'columns' : df.columns,
'range' : {
'from' : datetime.datetime.now().strftime('%Y-%m-%d')+ ' 00:00:00',
'to': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
},
'is_historical' : is_historical,
'show_chart' : show_chart,
'form' : myForm
}
return render(request,'portal/home.html',context)
forms.py
class device_readings_form(forms.Form):
initial_from_date = datetime.date.today()
initial_to_date = initial_from_date + datetime.timedelta(days=1)
date_from = forms.DateField(initial=initial_from_date)
date_to = forms.DateField(initial=initial_to_date)
company = forms.ChoiceField()
portal/home.html
{% block sidebar %}
<div class="col-md-4">
<div class="content-section">
<p class='text-muted'> <h5>Get Historical Readings</h5>
<form method="POST" action="{% url 'portal-home' %}">
{% csrf_token %}
{{form|crispy}}
<button type="submit">Submit</button>
</form>
</p>
</div>
</div>
{% endblock sidebar %}
The problem is faced only in case of ChoiceField. If I remove the ChoiceField and keep only remaining two DateFields then form gets properly submitted and I am able to read the form values in the view.
Can you guys please point me in right direction what I am doing wrong?
I would like to get the form submitted properly and get the form values in the view to process.
SOLVED!!!
The solution is:
One need to set the choices in the form instance.
Modified the code like below:
forms.py
class device_readings_form(forms.Form):
company = forms.ChoiceField()
devices = forms.ChoiceField()
initial_from_date = datetime.date.today()
initial_to_date = initial_from_date + datetime.timedelta(days=1)
date_from = forms.DateField(initial=initial_from_date)
date_to = forms.DateField(initial=initial_to_date)
def __init__(self, companies, devices, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['company'].choices = companies
self.fields['devices'].choices = devices
views.py
def home(request):
my_companies = get_my_companies(request.user)
my_devices, total_list = get_available_devices(my_companies)
is_historical = False
show_chart = False
myForm = device_readings_form(my_companies, total_list)
#myForm.fields['company'].choices = my_companies
if request.method == 'POST':
myForm = device_readings_form(my_companies, total_list, request.POST or None)
is_historical = True
if myForm.is_valid():
cd = myForm.cleaned_data
company = cd.get('company')
device = cd.get('devices')
date_from = cd.get('date_from')
date_to = cd.get('date_to')
print(company)
print(device)
print(date_from)
print(date_to)

django checkboxSelectMultiple

Something very strange is happening, I've built a MultipleChoiceField in forms.py that is rendering as a normal list. I am unable to have the checkboxes display. I'm hoping someone can spot where I might have gone wrong.
forms.py
from django import forms
from . import models
from behaviour.models import Interventions
class IncidentForm(forms.Form):
def __init__(self,*args,**kwargs):
self.request = kwargs.pop('request')
super(IncidentForm,self).__init__(*args, **kwargs)
intervention_set = Interventions.objects.filter(schoolid_id = self.request)
intervention_choice = []
for intervention in intervention_set:
intervention_choice.append((intervention.pk, intervention.name))
self.fields['intervention'].choices = intervention_choice
intervention = forms.MultipleChoiceField(label='Intervention', choices=(), widget=forms.CheckboxSelectMultiple(), required=True,)
incident.html
<div>
<label class="control-label">{% trans 'Intervention' %}</label><br />
{{ form.intervention }}
<small class="form-control-feedback"> {{ form.intervention.errors }} </small>
</div>
HTML output
<div>
<label class="control-label">Intervention</label><br>
<ul id="id_intervention">
<li><label for="id_intervention_0"><input type="checkbox" name="intervention" value="3" id="id_intervention_0">
Communicate verbally with Parent</label>
</li>
<li><label for="id_intervention_1"><input type="checkbox" name="intervention" value="2" id="id_intervention_1">
Non-verbal signal</label>
</li>
<li><label for="id_intervention_2"><input type="checkbox" name="intervention" value="1" id="id_intervention_2">
Spoke with Student</label>
</li>
</ul>
<small class="form-control-feedback"> </small>
</div>
Screenshot of output
If you are using Django admin and you parent model is not using any relationship with child model but has charfield to store ids of selected items of child model that will be added to ModelAdmin as custom field.
Follow steps:
STEP 1: model.py
class yourparentmodel(models.Model):
...
prior_learning_checks = models.CharField(max_length=120, null = True, blank=True)
...
class childmodel(models.Model):
rpl_id = models.CharField(max_length=4, null = True, blank=True)
rpl_desc = models.CharField(max_length=120, null = True, blank=True)
Here in this example parent is CohortDetails as inline to Cohort, or can limit to Cohort only if don't need inlines.
and child model is StudentRPL here in this example.
STEP 2: admin.py
Add CheckboxSelectMultiple to list your table data that id and description in this example.
Then use init attach your custom field for child model with parent form.
class StudentRPLForm(forms.ModelForm):
student_prior_learning = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=StudentRpl.objects.all())
class Meta:
model = StudentRpl
fields = [
'rpl_indicator',
'rpl_description',
]
def __init__(self, *args, **kwargs):
super(StudentRPLForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk:
self.fields['student_prior_learning']=forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=StudentRpl.objects.all())
rpl_list = []
ids = self.instance.prior_learning_checks
if ',' in ids:
ids = ids[0:(len(ids) - 1)]
rpl_list = ids.rsplit(',')
self.fields['student_prior_learning'].initial = StudentRpl.objects.filter(rpl_indicator__in=rpl_list)
Now it is time to save data by overriding save(self, commit).
Make sure use the cleaned_data. this is magic to collect only checked rows data and save to charfield of your parentmodel to read it again when to load the change_form. Because you are saving as comma separared ids, you have chance to load this string into list and filter your custom child model.
Other magic to view last saved ids in commas by assigning:
self.fields['student_prior_learning'].initial value to your filtered data in init function above (as i did)!!!
def save(self, commit=True, *args, **kwargs):
m = super(StudentRPLForm, self).save(commit=False, *args, **kwargs)
selected_rpl_list = ''
if self is not 'CohortDetailsForm':
for cr in self.cleaned_data['student_prior_learning']:
selected_rpl_list += cr.rpl_indicator + ','
m.prior_learning_checks = selected_rpl_list
if commit:
m.save()
STEP 3: admin.py
Assign your form to Inline or directly to modeladmin class if you don't need inlines.
class CohortDetailInline(admin.StackedInline):
model = CohortDetails
form = StudentRPLForm
fieldsets = ['fields':, ('student_prior_learning')]
.....
#admin.register(Cohort)
class CohortAdmin(admin.ModelAdmin):
inlines = CohortDetailInline
....
DONE !!!!

How to get a Modelform to show fields in a detail page in Django?

What I would like to accomplish is that I get the form from songcreate to in same url as the Detail.html so a person can add more songs without having to leave and comeback multiple times.Eventually I hope to do this process with AJAX but haven't advance to that stage yet since i can't get the form to start. I think it may just be something simple but have been unable to pin point what it is.
Views.py
class DetailView(generic.DetailView):
# form_class = SongCreate
model = Album
template_name = 'post/detail.html'
def get_form_kwargs(self):
kwargs = super(SongCreate, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def get_object(self, *args, **kwargs):
pk =self.kwargs.get('pk')
obj = get_object_or_404(Album, pk=pk)
return obj
def get_context_data( self, *args, **kwargs ):
context =super(DetailView, self).get_context_data(*args, **kwargs)
# context['create_form'] = SongCreate()
# context['create_url'] = reverse_lazy("post:index")
query = self.request.GET.get("q")
if query:
qs = Album.objects.search(query)
return context
This is the Song create view when using its on url. it works just fine but I want to be able to show this form in the detail page or list page if thats not possible.
class SongCreate(CreateView):
model = Song
success_url = reverse_lazy('post:index')
form_class = SongCreate
def form_valid(self, form):
if form.is_valid():
----------(Here is validation which ill skip for brevity)--------
return super(SongCreate, self).form_valid(form)
def get_form_kwargs(self):
kwargs = super(SongCreate, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
So my main problem lies here form some reason I am not able to get the fields to show up in the detail page correctly even though it shows up perfectly in its actual page. When I set it up it only shows me the the outline of the form and the submit button.
Details.html
{% block body %}
**<!--Form--> HERE IS WHERE I WANT THE FORM-->**
<div class='col-sm-9 '>
{% if not request.GET.q %}
<div class=''>
{% include "post/song_form.html" with form=create_form action_url=create_url btn_title='submit' form_class='SongCreate' %}
</div>
<hr/>
{% endif %}
<div clas="col-sm-8 col-sm-offset-2" >
{% for Song in Album.song_set.all %}
<div class="media">
<div class="media-left">
<a href="#">
{% if object.image %}
<img class="media-object" src="..." alt="...">
{% endif %}
</a>
</div>
<div class="media-body">
{{ song.song_name }}<br/>
{{ song.description}}|
via {{ Album.timestamp }}
</br/>
<br>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
Here is my form i used a modelform and it requires you to select the the album and it shows the albums the person has created to you can append it that album then make a description about it.
Forms.py
from django.views.generic import DetailView
from .models import Song
from .models import Album
from django import forms
class SongCreate(forms.ModelForm):
class Meta:
model = Song
fields = [
'album',
'song_name',
'description'
]
def __init__(self, user=None, *args, **kwargs):
self.user = user
super(SongCreate, self).__init__(*args, **kwargs)
self.fields['album'].queryset = Album.objects.filter(owner=user)
Try include "/post/song_form.html". Without the preceding slash it might be looking in the local folder of your details.html.
Instead of doing two separate views for your detail view page and your form, you could include the form in the detail view page view. You could do this by adding a "mixin" FormMixin. So you would list your validate and create form logic in your detail view.
Link to docs here
from django.views.generic.edit import FormMixin
class DetailView(generic.DetailView,FormMixin):
model = Album
template_name = 'post/detail.html'
form_class = SongCreate
def get_object(self, *args, **kwargs):
pk =self.kwargs.get('pk')
obj = get_object_or_404(Roote, pk=pk)
return obj
def get_context_data( self, *args, **kwargs ):
context =super(DetailView, self).get_context_data(*args, **kwargs)
# context['create_form'] = SongCreate()
# context['create_url'] = reverse_lazy("post:index")
query = self.request.GET.get("q")
if query:
qs = Album.objects.search(query)
return context
**def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)

Only Last Parameter being passed into Object

My app is supposed to create an image object for each image selected in the input field of my form. It–the create action– iterates over the :picture param and for each entry, it is supposed to create a new image object. However, it seems to only create an image object for the last image selected in the input box. Why is this not functioning properly?
Controller
class Admin::ImagesController < ApplicationController
respond_to :html, :json
#before_filter :split_hash, :only => [ :create, :update ]
def index
#album = Album.find(params[:album_id])
#images = #album.images.all
end
def new
#album = Album.find(params[:album_id])
#image = #album.images.new
end
def create
params[:image][:picture].each do |image|
#album = Album.find(params[:album_id])
#params = {}
#params['picture'] = image
#image = #album.images.build(#params)
end
if #image.save
flash[:notice] = "Successfully added image!"
redirect_to [:admin, #album, :images]
else
render "new"
flash[:notice] = "Did not successfully add image :("
end
end
def show
#album = Album.find(params[:album_id])
#image = #album.images.find(params[:id])
end
def edit
#album = Album.find(params[:album_id])
#image = #album.images.find(params[:id])
end
def update
#album = Album.find(params[:album_id])
#image = #album.images.find(params[:id])
if #image.update_attributes(params[:image])
flash[:notice] = "Successfully updated Image"
redirect_to [:admin, #album, :images]
else
render "edit"
end
end
def destroy
#album = Album.find(params[:album_id])
#image = #album.images.find(params[:id])
#image.destroy
#albumid = #album.id
#id = #image.id
FileUtils.remove_dir("#{Rails.root}/public/uploads/image/picture/#{#albumid}/#{#id}", :force => true)
redirect_to admin_album_images_path(#album)
end
# def split_hash
# #album = Album.find(params[:album_id])
# #image = #album.images
# array_of_pictures = params[:image][:picture]
# array_of_pictures.each do |pic|
# size = array_of_pictures.size.to_i
# size.times {#image.build(params[:image], :picture => pic)}
# #image.save
# end
# end
end
View Form
<%= form_for([:admin, :album, #image], :html => {:multipart => true}) do |f| %>
<%= f.hidden_field :album_id, :value => #album.id %>
<%= f.file_field :picture, :multiple => true %>
<%= f.submit "Submit" %>
<%end%>
Request Params on Submit
{"image"=>{"picture"=>[#<ActionDispatch::Http::UploadedFile:0x10c986d88 #tempfile=#<File:/var/folders/bx/6z1z5yks56j40v15n43tjh1c0000gn/T/RackMultipart20130404-53101-3c2whv-0>,
#headers="Content-Disposition: form-data; name=\"image[picture][]\"; filename=\"background-pic.jpg\"\r\nContent-Type: image/jpeg\r\n",
#content_type="image/jpeg",
#original_filename="background-pic.jpg">,
#<ActionDispatch::Http::UploadedFile:0x10c986d60 #tempfile=#<File:/var/folders/bx/6z1z5yks56j40v15n43tjh1c0000gn/T/RackMultipart20130404-53101-bvdysw-0>,
#headers="Content-Disposition: form-data; name=\"image[picture][]\"; filename=\"bible-banner.png\"\r\nContent-Type: image/png\r\n",
#content_type="image/png",
#original_filename="bible-banner.png">],
"album_id"=>"10"},
"authenticity_token"=>"dr8GMCZOQo4dQKgkM4On2uMs8iORQ68vokjW0e4VvLY=",
"commit"=>"Submit",
"utf8"=>"✓",
"album_id"=>"10"}
I would greatly appreciate any help you can share!
#album.images.build will only create the object, it will not save it to the database. When you exit your each loop, #image is just the last image that was built (which would be the last image in the params).
Using #album.images.create will actually save the image to database so that it is persisted like you expect; however, your logic after the each block won't be valid anymore, since you'll only be verifying that the last image was saved. You should check if each image is saved inside of the each block, and somehow record which ones were unsuccessful if that is what you want to do.

Resources