Hi Stackoverflow people,
I would like to style a formset with the crispy app, but it causes some grieve.
A very simple model should be presented four times.
class ItemPicture(models.Model):
item = models.ForeignKey('Item')
picture = ThumbnailerImageField(_('Image'),
upload_to='pictures/', null=True, blank=True,)
The form class is also straightforward:
class ItemPictureForm(forms.ModelForm):
class Meta:
model = ItemPicture
fields = ('picture',)
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Fieldset(_('Add pictures'),'picture', ),
ButtonHolder(
Submit('save', _('Add'), css_class='btn btn-primary '),
Reset('reset', _('Cancel'), css_class='btn')))
super(ItemPictureForm, self).__init__(*args, **kwargs)
In my views.py, I generate the formset:
class ItemUploadPictures(FormView):
ItemPictureFormSet = formset_factory(ItemPictureForm, extra=4)
form_class = ItemPictureFormSet
template_name = 'item_upload_pictures.html'
success_url = reverse_lazy('dashboard')
My trouble is that crispy expects {% crispy formset formset.form.helper %} in the template, but it seems that the passed-through variable is form.
{% crispy form %} works, but no helper attributes will be displayed. How can I pass the entire formset information to the template?
Thank you for your suggestions.
Have you tried
{% crispy formset form.form.helper %}
forms.py
class ItemPictureForm(forms.ModelForm):
class Meta:
model = ItemPicture
fields = ('picture',)
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Fieldset(_('Add pictures'),'picture', ),
# These need to be removed because they cant be prevented from duplicating
# ButtonHolder(
# Submit('save', _('Add'), css_class='btn btn-primary '),
# Reset('reset', _('Cancel'), css_class='btn')
))
super(ItemPictureForm, self).__init__(*args, **kwargs)
manage_pictures.html
{% load crispy_forms_tags i18n %}
<form action="" method="post">
{% csrf_token %}
{% crispy formset formset.form.helper %}
<div class="form-actions">
<input type="submit" name="save" value="{% trans "Add" %}" class="btn btn-primary" id="submit-id-save">
<input type="button" name="reset" value="{% trans "Cancel" %}" class="btn" id="button-id-cancel">
</div>
</form>
{% crispy formset formset.form.helper %}
Maybe this library serves your needs: https://github.com/runekaagaard/django-crispy-forms-fancy-formsets
Related
I want to create a charfield input in a django form that has suggestions. Essentially I want a choice field that also allows you to write your own value if needed. In other words a hybrid between a charfield and choice field input. Any suggestions on how to achieve this ?
forms.py
class PDFClassificationForm(forms.ModelForm):
nature = forms.CharField(required=False)
class Meta:
model = Documents
fields = [,
'nature',]
labels = {,,
'nature':'Nature/Concerne:',
}
def __init__(self, uuid_pdf, *args, **kwargs):
super(PDFClassificationForm, self).__init__(*args, **kwargs)
nature_choices= Archivagerecurrencelibelle.objects.filter(Q(id_emetteur=Documents.objects.get(uuid=uuid_pdf).id_emetteur) & Q(source="Nature")).values_list('concerne','concerne')
self.fields['nature'].choices = nature_choices
views.py
class DocumentsArchiveUpdateView(UpdateView):
model = Documents
template_name = 'documents/documents_read_write.html'
form_class = PDFClassificationForm
success_url = lazy(reverse, str)("documents_archive_list")
def get_context_data(self, *args, **kwargs):
# Call the base implementation first to get a context
context = super(DocumentsArchiveUpdateView, self).get_context_data(*args, **kwargs)
id_customer = Documents.objects.get(uuid=self.kwargs['uuid_pdf']).id_customer
context["document_owner"] = Entity.objects.get(id=id_customer)
context["uuid_contrat"] = self.kwargs['uuid_contrat']
context["uuid_group"] = self.kwargs['uuid_group']
context["uuid_pdf"] = self.kwargs['uuid_pdf']
return context
def form_valid(self, form):
obj = form.save(commit=False)
obj.id_document_status = DocumentStatusLst.objects.get(id=3)
obj.save()
return super().form_valid(form)
def get_form_kwargs(self,) :
uuid_pdf = self.kwargs['uuid_pdf']
kwargs = super(DocumentsArchiveUpdateView, self).get_form_kwargs()
kwargs["uuid_pdf"] = uuid_pdf
return kwargs
documents_read_write.html
{% extends 'layout.html' %}
{% load crispy_forms_tags %}
<form action = "{%url 'jnt_customer_create' uuid_contrat uuid_group pdfid%}" method="POST" enctype="multipart/form-data">
<!-- Security token -->
{% csrf_token %}
<!-- Using the formset -->
{{ form |crispy}}
<button type="submit" name="btnform1" class="btn btn-primary">Enregistrer</button>
</form>
{% endblock %}
----------- models.py -------
from django.db import models
class EmployeeModel(models.Model):
name = models.CharField(max_length=255)
age = models.PositiveIntegerField()
city = models.CharField(max_length=255)
def __str__(self):
return self.name
----------- form.py -------
class EmployeeFrom(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EmployeeFrom, self).__init__(*args, **kwargs)
self.fields['city'].widget = forms.Select(choices = CityModel.objects.values_list('name','name'))
class Meta:
model = EmployeeModel
fields = "__all__"
widgets = {
'name':forms.TextInput(attrs={'class':'form-control'}),
'age':forms.NumberInput(attrs={'class':'form-control'}),
}
----------- Html code for form -----------
<div class="modal-body">
<form action="" method="POST">
{% csrf_token %}
<div class="mb-3">
<label class="form-label">{{form.name.label}}</label>
{{form.name}}
</div>
<div class="mb-3">
<label class="form-label">{{form.age.label}}</label>
{{form.age}}
</div>
<div class="mb-3">
<label class="form-label">{{form.city.label}}</label>
<input class="form-select" placeholder="--- Select city ---" name="city" type="text" list="cities">
<datalist id="cities">
{% for i in form.city %}
<option>{{i}}</option>
{% endfor %}
</datalist>
</div>
<button class="btn btn-primary" type="submit">Add Employee</button>
</form>
</div>
========= Ouput ===============
all cities
after entering keyword
==== last output ==============
You might try with this https://github.com/jazzband/django-taggit
I used it in a similar use case, you can pass a whitelist of "tags" as suggestion or populate it with already used tags, while still allowing the creation of new values
I'm trying to render this form:
class LoadForm(forms.Form):
class Meta:
model = Load
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Row(
'whouse',
'supplier',
'company',
'product',
'quantity',
'unit_price',
'load_date',
)
)
with the following view:
def load(request):
form = LoadForm()
context = {
'form': form,
'title': 'Nuovo Carico',
}
return render(request, 'warehouse/load.html', context)
and the following template:
{% extends "masterpage.html" %}
{% load static %}
{% block headTitle %}
<title>{{title}}</title>
{% endblock %}
{% block contentHead %}
{% endblock %}
{% block contentBody %}
{% load document_tags %}
{% load custom_tags %}
{% load crispy_forms_tags %}
<FORM method="POST" autocomplete="off">
{{ form.media }}
{% csrf_token %}
<div class="alert alert-info">
{{ title }}
</div>
{% crispy form %}
<input type="submit" class="btn btn-primary margin-left" value="CARICA">
</FORM>
{% endblock %}
For some strange reason the form will not show up, I just see the title and the input button. I've tried the very simple form.as_p with no crispy, but still nothing...
By looking at the sourse code on the browser I see there is a div with class 'form-row' but not form in it...
looks strange.
Any help?
thank you very much.
Carlo
Your form class is defined as follows: class LoadForm(forms.Form): Note that here this is a Form and not a ModelForm hence using a Meta class and specifying model makes no difference. Instead you want to use a ModelForm [Django docs] and you also need to specify either fields or exclude in the Meta:
class LoadForm(forms.ModelForm): # `ModelForm` here
class Meta:
model = Load
fields = '__all__' # All fields
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Row(
'whouse',
'supplier',
'company',
'product',
'quantity',
'unit_price',
'load_date',
)
)
Call form in template like this. First the name of the form you have in context plus pipe crispy. It works for me.
</div>
{{form|crispy}}
<input type="submit" class="btn btn-primary margin-left" value="CARICA">
I'm creating 2 forms on one template in cookiecutter-django. I have both forms working on a normal django project but when I migrated them to cookiecutter-django, the forms did not work on the user_detail template.
This is the forms.py
class NarrateForm(ModelForm):
class Meta:
model = Narrate
fields = [
'title',
'body',
]
exclude = ('status',)
class TranslateForm(ModelForm):
class Meta:
model = Translate
fields = [
'title',
'body',
]
exclude = ('status',)
These are the views.py of forms that I have:
class TranslateFormView(FormView):
form_class = TranslateForm
template_name = 'user_detail.html'
def post(self, request, *args, **kwargs):
add_translation = self.form_class(request.POST)
add_narration = NarrateForm()
if add_translation.is_valid():
add_translation.save()
return self.render_to_response(
self.get_context_data(
success=True
)
)
else:
return self.render_to_response(
self.get_context_data(
add_translation=add_translation,
)
)
class NarrateFormView(FormView):
form_class = NarrateForm
template_name = 'users/user_detail.html'
def post(self, request, *args, **kwargs):
add_narration = self.form_class(request.POST)
add_translation = TranslateForm()
if add_narration.is_valid():
add_narration.save()
return self.render_to_response(
self.get_context_data(
success=True
)
)
else:
return self.render_to_response(
self.get_context_data(
add_narration=add_narration,
)
)
Now this is the view of the user_details from cookiecutter-django
class UserDetailView(LoginRequiredMixin, DetailView):
model = User
slug_field = "username"
slug_url_kwarg = "username"
user_detail_view = UserDetailView.as_view()
This is the code on the template which works on the old django project
<form method="POST" action="#">
{% csrf_token %}
{{ add_narration }}
<button type="submit" name="submit" value="Send Narration">Submit Narration</button>
</form>
<form method="POST" action="#">
{% csrf_token %}
{{ add_translation }}
<button type="submit" name="submit" value="Send Narration">Submit Narration</button>
</form>
I've been trying to make this work for more than 2 hours already and had no luck.
A possible solution could be creating a basic form (extend django.forms.Form) in the forms.py file, manually creating the fields and the modela later on in the views.py file.
give the submit inputs different names as shown here
<form method="POST" action="#">
{% csrf_token %}
{{ add_narration }}
<button type="submit" name="narrationsubmit" value="Send Narration">Submit Narration</button>
</form>
<form method="POST" action="#">
{% csrf_token %}
{{ add_translation }}
<button type="submit" name="transaltionsubmit" value="Send Narration">Submit Narration</button>
</form>
Then handle them as below in your view.
def handleforms(request):
if request.method =="POST" and "narrationsubmit" in request.post:
//process
elif request.method =="POST" and "transaltionsubmit" in request.post:
//process
NB: this is for a function based view. format it for a CBV
Good Evening,
Im having trouble with a crispy forms inlineformset. I have followed guides as per:
https://github.com/timhughes/django-cbv-inline-formset/blob/master/music/views.py
https://django-crispy-forms.readthedocs.io/en/latest/crispy_tag_formsets.html#formsets
EDIT
I think the issue is something to do with the dual submit buttons. the devicemodel form has a button that when pressed produces this error. but there is also a save button as part of the resource helper, when that's submitted I get an empty model form error.
I've added screenshots of what happens when you action each button
and I must be missing something as am getting the error:
['ManagementForm data is missing or has been tampered with']
here is my update view:
class EditDeviceModel(PermissionRequiredMixin, SuccessMessageMixin, UpdateView):
model = DeviceModel
form_class = DeviceModelForm
template_name = "app_settings/base_formset.html"
permission_required = 'config.change_devicemodel'
success_message = 'Device Type "%(model)s" saved successfully'
def get_success_url(self, **kwargs):
return '{}#device_models'.format(reverse("config:config_settings"))
def get_success_message(self, cleaned_data):
return self.success_message % dict(
cleaned_data,
model=self.object.model,
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title']='Edit Device Model'
if self.request.POST:
context['formset'] = DeviceFormSet(self.request.POST, instance=self.object)
else:
context['formset'] = DeviceFormSet(instance=self.object)
context['helper'] = DeviceFormSetHelper()
return context
def form_valid(self, form):
context = self.get_context_data()
formset = context['formset']
if formset.is_valid():
self.object = form.save()
formset.instance = self.object
formset.save()
return redirect(self.success_url)
else:
return self.render_to_response(self.get_context_data(form=form))
Here are my forms:
class MonitoredResourceForm(forms.ModelForm):
class Meta:
model = MonitoredResource
fields = ['resource','model']
def __init__(self, *args, **kwargs):
self.is_add = kwargs.pop("is_add", False)
super(MonitoredResourceForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'snmp_resource_form'
self.helper.form_method = 'POST'
self.helper.layout = Layout(
Div(
Div(
Field('model'),
Field('resource', placeholder="Resource"),
css_class='col-lg-3'
),
css_class='row'
),
Div(
Div(
HTML("""<input type="submit" name="submit" value="""),
HTML('"Add' if self.is_add else '"Update' ),
HTML(""" monitored resource" class="btn btn-primary"/>"""),
HTML("""Cancel"""),
HTML("""{% if object %}
<a href="{% url 'config:delete_monitoredresource' object.id %}"
class="btn btn-danger">
Delete <i class="fa fa-trash-o" aria-hidden="true"></i></a>
{% endif %}"""),
css_class='col-lg-12'
),
css_class='row'
),
)
class DeviceModelForm(forms.ModelForm):
class Meta:
model = DeviceModel
fields = ['model','vendor','device_type','ports','uplink_speed']
def __init__(self, *args, **kwargs):
self.is_add = kwargs.pop("is_add", False)
super(DeviceModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'device_type_form'
self.helper.form_method = 'POST'
self.helper.layout = Layout(
Div(
Div(
Field('model', placeholder="Model"),
Field('vendor',),
Field('device_type',),
Field('ports', placeholder="Ports"),
Field('uplink_speed', placeholder="Uplink Speed"),
css_class='col-lg-6'
),
css_class='row'
),
Div(
Div(
HTML("""<input type="submit" name="submit" value="""),
HTML('"Add' if self.is_add else '"Update' ),
HTML(""" Device Model" class="btn btn-primary"/>"""),
HTML("""Cancel"""),
HTML("""{% if object %}
<a href="{% url 'config:delete_device_model' object.id %}"
class="btn btn-danger">
Delete <i class="fa fa-trash-o" aria-hidden="true"></i></a>
{% endif %}"""),
css_class='col-lg-12'
),
css_class='row'
),
)
DeviceFormSet = inlineformset_factory(DeviceModel, MonitoredResource, form=MonitoredResourceForm, extra=1)
class DeviceFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(DeviceFormSetHelper, self).__init__(*args, **kwargs)
self.form_method = 'post'
self.render_required_fields = True
self.form_id = 'snmp_resource_form'
self.form_method = 'POST'
self.add_input(Submit("submit", "Save"))
self.layout = Layout(
Div(
Div(
Field('model'),
Field('resource', placeholder="Resource"),
css_class='col-lg-6'
),
css_class='row'
),
)
and in the templates I render:
{% block content %}
{% include "home/form_errors.html" %}
<div class="col-lg-6">
{% crispy form %}
</div>
<div class="col-lg-6">
{% crispy formset helper %}
</div>
<!-- /.row -->
{% endblock %}
is anyone able to see what im missing?
I think you have to render management form in your template, explained here why you need that
Management Form is used by the formset to manage the collection of forms contained in the formset. If you don’t provide this management data, an exception will be raised
add this in view html
{{ DeviceFormSet.management_form }}
You are missing a tag and also {{format.management_form|crispy}}
I guess
Your problem is that each form in a formset has its own management_form. I haven't dealt with this specifically in crispy, but in the general formsets, that was the problem that I had. You have to manually spell out each piece of the formset, either by iteration or hardcoding, and make sure that each has its management_form.
I had the same problem and found the answer in the documentation:
{{ formset.management_form|crispy }}
{% for form in formset %}
{% crispy form %}
{% endfor %}
This is my forms and inlineformset
class EventForm(ModelForm):
def __init__(self, *args, **kwargs):
super(EventForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Field('name'),
Field('description'),
Field('tags'),
)
self.helper.layout.append(Submit('save', 'Save'))
class Meta:
model = Event
fields = ('name','description','tags', )
class GalleryForm(ModelForm):
def __init__(self, *args, **kwargs):
super(GalleryForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_class = 'form-inline'
self.helper.form.method = 'post'
self.helper.form.action = ''
self.helper.layout = Layout(
Div(
Div('title', css_class='col-md-4', ),
Div('image', css_class='col-md-4', ),
css_class='row',
),
FormActions(
Submit('submit', 'Submit'),
),
)
class Meta:
model= Gallery
fields = ('title', 'event', 'image')
GalleryFormSet = inlineformset_factory(Event, Gallery, extra=0, min_num=1, fields=('title', 'image' ))
My views:
class EventCreateView(FormsetMixin, CreateView):
template_name = 'member/event_and_gallery_form.html'
model = Event
form_class = EventForm
formset_class = GalleryFormSet
class EventUpdateView(FormsetMixin, UpdateView):
template_name = 'member/event_and_gallery_form.html'
is_update_view = True
model = Event
form_class = EventForm
formset_class = GalleryFormSet
my form.html:
{% block body %}
<form action="." method="post" enctype="multipart/form-data">
{{ formset.management_form }}
{% csrf_token %}
<legend>Event</legend>
<div class="event">
{{ form|crispy}}
</div>
<legend>
<div class="pull-right"><i class="icon-plus icon-white"></i> Add Photo</div>
Photo Gallery
</legend>
<div class="gallery form-inline">
{% for form in formset %}
{{form|crispy}}
{% endfor %}
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
{% endblock %}
But it didn't give inline formset layout. How do I implement crispy form layout in my form? Help is needed.