Django - crispy form not showing - django

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">

Related

Customizing inlineformset choice in Django template

I've got some forms I'm trying to customize.
I render the fields manually - and it all works fine until get to a particular field (which is an InlineFormset itself). I'm trying to customize those options but can't seem to figure out how to do so.
my forms.py looks like this:
class SummativeScoreForm(forms.ModelForm):
subdomain_proficiency_level = forms.ModelChoiceField(
empty_label="Undecided",
queryset=SubdomainProficiencyLevel.objects.none(),
widget=forms.RadioSelect,
required=False,
)
def __init__(self, request, *args, **kwargs):
super(SummativeScoreForm, self).__init__(*args, **kwargs)
if self.instance:
if request.user == self.instance.summative.employee:
self.fields["subdomain_proficiency_level"].disabled = True
self.fields[
"subdomain_proficiency_level"
].queryset = SubdomainProficiencyLevel.objects.filter(
subdomain=self.instance.subdomain
)
self.fields[
"subdomain_proficiency_level"
].label = f"""
{self.instance.subdomain.character_code}:
{self.instance.subdomain.short_description}
"""
class Meta:
model = SummativeScore
fields = "__all__"
SummativeScoreInlineFormset = inlineformset_factory(
Summative,
SummativeScore,
fields=("subdomain_proficiency_level",),
can_delete=False,
extra=0,
form=SummativeScoreForm,
)
My template for summative_score_form looks like this:
<form method="post" novalidate>
{% csrf_token %}
{% include "myapp/includes/summative_score_response_formset_snippet.html" with formset=form %}
<button type="submit" class="btn btn-primary"><i class="fal fa-clipboard-check"></i> Submit Updated Scores</button>
</form>
The summative_score_response_formset_snippet looks like this:
{{ formset.management_form }}
{% for formset_form in formset.forms %}
{% if formset_form.non_field_errors %}
<ul>
{% for error in formset_form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{% for hidden_field in formset_form.hidden_fields %}
{% if hidden_field.errors %}
<ul>
{% for error in hidden_field.errors %}
<li>
(Hidden field {{ hidden_field.name }}) {{ error }}
</li>
{% endfor %}
</ul>
{% endif %}
{{ hidden_field }}
{% endfor %}
{% for field in formset_form.visible_fields %}
{% if field.name == 'subdomain_proficiency_level' %}
<label class="form-check-label" for="{{ field.id_for_label }}">
{{ field.label }}
</label>
<ul id="{{ field.auto_id }}" class="form-check mt-2">
{% for choice in formset_form.subdomain_proficiency_level %}
<div class="form-check">
<!--
THIS IS THE PART I WOULD LIKE TO CUSTOMIZE:
Unsatisfactory (name) Lorum Ipsum (description)
Satisfactory (name) Lorum Ipsum (description)
Excellent (name) Lorum Ipsum (description)
CURRENTLY IT ONLY SHOWS THE NAME
-->
{{ choice }}
</div>
{% endfor %}
</ul>
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
{% else %}
{{ field }}
{% endif %}
{% endfor %}
{% endfor %}
My models look like this:
class SubdomainProficiencyLevel(CreateUpdateMixin):
"THIS IS THE 'UNSATISFACTORY' (name) 'LORUM IPSUM' (description)"
name = models.CharField(max_length=75)
description = models.TextField()
sequence = models.IntegerField()
class Meta:
ordering = ["sequence"]
verbose_name = "Subdomain Rank"
verbose_name_plural = "Subdomain Ranks"
def __str__(self):
"""
THIS IS WHAT IS 'CHOICE' IN THE FORM
I'm trying to edit this to add styles to the self.description on the form
"""
return f"{self.name}"
class SummativeScore(CreateUpdateMixin, CreateUpdateUserMixin):
summative = models.ForeignKey(Summative, on_delete=models.PROTECT)
subdomain = models.ForeignKey(Subdomain, on_delete=models.PROTECT)
subdomain_proficiency_level = models.ForeignKey(
SubdomainProficiencyLevel,
on_delete=models.PROTECT,
null=True,
blank=True,
)
class Meta:
ordering = ["subdomain__character_code"]
verbose_name = "SummativeScore"
verbose_name_plural = "SummativeScores"
def __str__(self):
"""Unicode representation of SummativeScore."""
return f"{self.subdomain_proficiency_level}"
The view is a Class Based FormView
class SummativeScoreFormView(
LoginRequiredMixin,
UserIsObserverOrObserveeMixin,
SingleObjectMixin,
FormView,
):
model = Summative
template_name = "myapp/summative_score_form.html"
pk_url_kwarg = "summative_id"
def get(self, request, *args, **kwargs):
summative_id = kwargs.pop("summative_id")
self.object = self.get_object(
queryset=Summative.objects.filter(id=summative_id)
)
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
summative_id = kwargs.pop("summative_id")
self.object = self.get_object(
queryset=Summative.objects.filter(id=summative_id)
)
return super().post(request, *args, **kwargs)
def get_form(self, form_class=None):
formset = SummativeScoreInlineFormset(
**self.get_form_kwargs(), instance=self.object
)
return formset
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["form_kwargs"] = {"request": self.request}
return kwargs
def form_valid(self, form):
form.save()
messages.success(self.request, "Changes were saved!")
return super().form_valid(form)
def form_invalid(self, form):
return super().form_invalid(form)
def get_success_url(self):
user_id = self.kwargs["user_id"]
summative_id = self.kwargs["summative_id"]
return reverse(
"myapp:summative_detail",
kwargs={
"user_id": user_id,
"summative_id": summative_id,
},
)
As you can see in the template - I render the SubdomainProficiencyLevel objects with the template variable {{ choice }}
I have tried doing {{ choice.description }} or {{ choice.name }} <span class="bold">{{ choice.description }}</span> but then nothing displays.
I have also tried adjusting the __str__ method on the model - which changes there work, but do not render as HTML (just as a string as expected).
What is the best way to customize that in the HTML?
I ended up creating a custom radio button class (similar to the documentation)
class CustomRadioSelect(forms.RadioSelect):
def create_option(
self, name, value, label, selected, index, subindex=None, attrs=None
):
option = super().create_option(
name, value, label, selected, index, subindex, attrs
)
if value:
option["attrs"]["description"] = value.instance.description
return option
Using that in the form:
subdomain_proficiency_level = forms.ModelChoiceField(
empty_label="Undecided",
queryset=SubdomainProficiencyLevel.objects.none(),
widget=CustomRadioSelect(),
required=False,
)
Then I could access it like this in the template:
{{ choice.data.attrs.description }}

queryset when building django form

I am trying to get specific querysets based when a customer-specific form loads, showing only that customer's name (embedded as an ID field), its respective locations and users.
The idea is to select one user and any number of locations from a multichoice box.
I've tried to pass the ID as a kwarg but am getting a KeyError. I've tried the kwarg.pop('id') as found on the web and same issue. Any advice?
forms.py
class LocGroupForm(forms.ModelForm):
class Meta:
model = LocationsGroup
fields = ('group_name', 'slug', 'customer', 'location', 'user_id',)
def __init__(self, *args, **kwargs):
qs = kwargs.pop('id')
super(LocGroupForm, self).__init__(*args, **kwargs)
self.fields['customer'].queryset = Customers.objects.get(pk=qs)
self.fields['location'].queryset = CustomerLocations.objects.filter(customer_id=qs)
self.fields['user_id'].queryset = CustomerUsers.objects.filter(customer_id=qs)
here is my view. it's just a generic view
views.py
class LocGroupCreate(LoginRequiredMixin, CreateView):
form_class = LocGroupForm
template_name = 'ohnet/a_gen_form.html'
the template is a dry template I use for all my forms- admittedly something I mostly stole from a tutorial
{% extends "ohnet/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
{% load static %}
<div class="container">
<h1>{{ title }}</h1>
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" name="submit" value="Submit">
</form>
</div>
{% endblock content %}
This is the KeyError from the form load.
You need to pass a value for the id when constructing the LocGroupForm, you can do that by overriding get_form_kwargs:
class LocGroupCreate(LoginRequiredMixin, CreateView):
form_class = LocGroupForm
template_name = 'ohnet/a_gen_form.html'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['id'] = …
return kwargs
You will need to fill in the … that specifies the value passed as id to the form. This might for example be self.request.user.pk, or a URL parameter with self.kwargs['name-of-url-parameter']

Why isn't my dynamic formset displaying any fields

My goal is to define an upload form for cars for users to upload a car with all relevant details, and adding images from the same page.
The Admin form works perfectly, and I just want to emulate it in custom page-- a surprisingly difficult task! I'm using this tutorial, with the fields changed to match my project.
I'm also using the django-dynamic-formset jQuery plugin to provide additional image fields as needed by the user.
I've been stuck on this for a week, and I'm not sure exactly where the issue lays, so I'm going to share lots of code.
class ImagesInline(admin.TabularInline):
model = Image
#admin.register(Car)
class CarAdmin(admin.ModelAdmin):
list_display = ('manufacturer', 'car_model', 'model_year', 'mileage', 'status', 'date_added', 'price', 'seller')
list_filter = ('manufacturer', 'status', 'transmission')
fieldsets = (
('General information', {
'fields': ('manufacturer', 'car_model',
'model_year', 'price', 'vin', 'mileage', 'transmission', 'engine_displacement',
'forced_induction', 'drivetrain', 'description',)
}),
('Availability', {
'fields': ('status', 'seller')
}),
)
inlines = [ImagesInline]
This is the CarCreate view
class CarCreate(generic.CreateView):
model = Car
# fields = '__all__'
success_url = reverse_lazy('car-detail.html')
slug_field = 'id'
slug_url_kwarg = 'car_create'
template_name = 'showroom/car_create.html'
form_class = CarImageForm
success_url = None
def get_context_data(self, **kwargs):
data = super(CarCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['images'] = CarImageFormSet(self.request.POST)
else:
data['images'] = CarImageFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
images = context['images']
with transaction.atomic():
form.instance.created_by = self.request.user
self.object = form.save()
if images.is_valid():
titles.instance = self.object
titles.save()
return super(CarCreate, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('showroom:car_detail', kwargs={'id': self.object.id})
Template car_create.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="card">
<div class="card-header"></div>
<h2>Upload a car</h2>
</div>
<div class="card-body">
{% crispy form %}
</div>
</div>
{% endblock content %}
custom_layout_object.py
from crispy_forms.layout import LayoutObject, TEMPLATE_PACK
from django.shortcuts import render
from django.template.loader import render_to_string
class Formset(LayoutObject):
template = "showroom/formset.html"
def __init__(self, formset_name_in_context, template=None):
self.formset_name_in_context = formset_name_in_context
self.fields = []
if template:
self.template = template
def render(self, form, form_style, context, template_pack=TEMPLATE_PACK):
formset = context[self.formset_name_in_context]
return render_to_string(self.teplate, {'formset': formset})
formset.html
{% load crispy_forms_tags %}
{% extends base.html %}
{{ formset.management_form|crispy }}
{% for form in formset.forms %}
<tr class="{% cycle 'row1' 'row2' %} formset_row-{{ formset.prefix}}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field|as_crispy_field}}
</td>
{% endfor %}
</tr>
{% endfor %}
{% block javascript %}
<script src="{% static '/js/django-dynamic-formset/jquery.formset.js' %}"></script>
<script type="text/javascript">
$('.formset_row-{{ formset.prefix }}').formset({
addtext: 'add another',
deleteText: 'remove',
prefix: '{{ formset.prefix }}',
});
</script>
{% endblock %}
Despite all this, the creation form looks like this:
When it should look like this
There aren't any errors in the console terminal.

Using modelformset_factory in a Django Class Based View

I am building a view that will let me update multiple fields on multiple objects at the same time. I'm doing this using ModelFormSet & modelformset_factory.
The template will be a table of forms with the object name to the left of the fields (see image below).
I found this example, but I am stuck on how to implement the class based view & template.
My Formset
class BaseFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(BaseFormSet, self).__init__(*args, **kwargs)
self.queryset = Reference.objects.filter(
start__isnull=True)
ReferenceFormSet = modelformset_factory(
Reference,
fields=('start', 'end'),
formset=BaseFormSet,
extra=0)
My View
class ReferenceFormSetView(LoginRequiredMixin, SuperuserRequiredMixin, FormView):
model = Reference
form_class = ReferenceFormSet
template_name = "references/references_form.html"
def form_valid(self, form):
for sub_form in form:
if sub_form.has_changed():
sub_form.save()
return super(ReferenceFormSetView, self).form_valid(form)
My Template
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<h1>{{ headline }}</h1>
<div class="row">
<form action="" method="post">
{% crispy form %}
<div class="">
<input type="submit" value="Submit" />
</div>
</form>
</div>
</div>
{% endblock content %}
Questions
The view seems odd with the Formset in the form_class. Is there a better way to handle this?
How can I access the instance name to display in the form?
I found a solution using a package called django-extra-views.
There is a class called ModelFormSetView which does exactly what I wanted. Here is my implementation (simplified) for others to use -
My View
class ReferenceFormSetView(ModelFormSetView):
model = Reference
template_name = "references/references_form.html"
fields = ['start', 'end']
extra = 0
def get_queryset(self):
return self.model.objects.all()
def get_success_url(self):
return reverse('references:formset')
def formset_valid(self, formset):
"""
If the formset is valid redirect to the supplied URL
"""
messages.success(self.request, "Updated")
return HttpResponseRedirect(self.get_success_url())
def formset_invalid(self, formset):
"""
If the formset is invalid, re-render the context data with the
data-filled formset and errors.
"""
messages.error(self.request, "Error dummy")
return self.render_to_response(self.get_context_data(formset=formset))
My Template
<form class="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<div class="">
{% for field in form %}
{{ field }}
{% endfor %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Save</button>
</form>

Django: Using crispy styled formsets in a class based view

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