Crispy form layout hide files - django-crispy-forms

crispy_forms Crispy_Forms 1.14.0
crispy_forms_foundation Crispy_Forms_Foundation 0.8.0
Django 3.2.11
I'm trying to migrate from django 2 to django 3, but stuck with empty forms.
class Myform(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset( 'This is shown:' ),
Column('myfield', css_class='large-4'), #hidden
Column('myfield2', css_class='large-4'), #hidden
)
Input fields are not rendered in template (you can see only the Fieldset header text 'This is shown:').
I've discovered that deleting the self.helper.layout = Layout() in the init method, make the fields rendered correctly in the template.
Any idea on how to fix this?

It seems that Column doesn't exist anymore, replace with:
Field('myfield', css_class="black-fields")

Related

Grails render-plugin does not render images

I have now setup the render-plugin and I get nice PDF-files but I also need to put an image into a few columns in my table. These images are conditionally selected by a data field of the instance.
I have the image in the assets/images folder as I think is the correct place.
I use the following GSP-line in the template that will be used by the renderer to create the PDF.
<td><g:if test="${od?.priceFSC > 0.1}"><asset:image src="checkOut16x16.png" width="16" height="16"/></g:if></td>
As a HTML-view the images prints perfect but when render the PDF they are missing.
I checked the documentation and tried the code from the example:
The controller:
def createPDF() {
def file = new File("asets/CheckOut16x16.png")
def OfferHeader offerHeader = OfferHeader.get(params.id)
[offerHeader: offerHeader])
renderPdf(template: "/stocknote/Stocknote", model: [offerHeader: offerHeader,imageBytes: file.bytes], filename: "Stocknote-"+params.id+".pdf")
}
The view:
<rendering:inlinePng bytes="${imageBytes}" class="some-class" />
I didn't care of the condition here I just wanted to see if it would be printed but it's not because the view crasched:
URI
/stocknote/editStocknote/32
Class
org.grails.taglib.GrailsTagException
Message
Request processing failed; nested exception is org.grails.gsp.GroovyPagesException: Error processing GroovyPageView: [views/stocknote/editStocknote.gsp:32] [views/stocknote/_StocknoteDetail.gsp:3] 'bytes' is required
Caused by
[views/stocknote/editStocknote.gsp:32] [views/stocknote/_StocknoteDetail.gsp:3] 'bytes' is required
I don't know what I've done wrong but error message seems confusing, "Bytes is required" but I have bytes="${imageBytes}".
Hope someone could give me some help or explanation.
Sounds like the path to your file is incorrect, try:
Controller:
def assetResourceLocator
def createPDF() {
def file = assetResourceLocator.findAssetForURI( 'CheckOut16x16.png' )
def OfferHeader offerHeader = OfferHeader.get(params.id)
[offerHeader: offerHeader])
renderPdf(template: "/stocknote/Stocknote", model: [offerHeader: offerHeader,imageBytes: file.getByteArray()], filename: "Stocknote-"+params.id+".pdf")
}
View should be fine as is.
Made a big mistake >_<, The template used to create the PDF is also used by the view from where you order the PDF and at that time the image has not been created by the controller.
So to come through, I had to check the value of the image before rendering.
<g:if test="${imageBytes!= null}"> <rendering:inlinePng bytes="${imageBytes}" /></g:if>
That what was needed.

django-crispy-forms: Align button with fields vertically

I've been struggling to get my form (including the button) in just one line using django-crispy-forms.
I eventually found a solution, but I decided posting the question together with the answer, in case somebody else faces the same problem.
Code in forms.py was as follows:
class SearchForm(forms.Form):
[...]
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-inline'
self.helper.layout = Layout(
Field('From', placeholder='From'),
Field('To', placeholder='To'),
Field('Date', placeholder='Date'),
ButtonHolder(Submit('submit', 'Search', css_class='btn btn-primary'))
)
But the button was appearing in a second line.
I tried as alternative
self.helper.form_class = 'form-horizontal'
but no difference.
I found a solution based on this post in github.
That is to adapt the Layout by using a FormActions object:
self.helper.layout = Layout(
Field('From', placeholder='From'),
Field('To', placeholder='To'),
Field('Date', placeholder='Date'),
FormActions(ButtonHolder(Submit('submit', 'Search', css_class='btn btn-primary')))
)
And all fields and the button are perfectly aligned:

Render PDF on custom controller action using wicked_pdf

This may end up being very simple but I have been trying to figure this out to no avail. I am using wicked_pdf to render pdfs on html pages. When I do this the restful way by adding the respond_to block to a controllers show action everything works fine when going to controller/id.pdf.
What I am trying to do however, is use the respond_to block on a custom controller action. I have a reports controller which has many reports and custom actions. One of these reports has a form which is submitted to another custom controller action to render the report. This all works fine with the html render however, when I added the respond_to block to this custom action it simply tells me it cannot find the show action for that controller. Below is the related code:
routes.rb
resources :reports do
collection do
get 'customer_summary'
post 'summary_report'
end
end
reports_controller.rb
def summary_report
#tickets = tickets
respond_to do |format|
format.html
format.pdf do
render pdf: "summary_report", header: {spacing: 10, html: {template: 'shared/header'}}, footer: {html: {template: 'shared/footer'}}, margin: { top: 20, bottom: 20 }
end
end
The templates exsist as well as the summary_report.pdf.haml file. What am I doing wrong? Thank you in advance for your help!

How can extra context be passed to django-crispy-forms field templates?

Inspecting the field.html template in the bootstrap3 template pack of django-crispyforms, I noticed an additional context variable, "tag", referenced. You can see this on line 12 and line 41 of the template. How can I specify a value for "tag" in the context used to render the field.html template for a particular form field?
I used palestamp's response as a guide to build a more generic CustomCrispyField. You can pass extra_context as a kwarg to CustomCrispyField. extra_context is just a dictionary that I access in my custom template that I copied from crispy_forms.
from crispy_forms.layout import Field
from crispy_forms.utils import TEMPLATE_PACK
class CustomCrispyField(Field):
extra_context = {}
def __init__(self, *args, **kwargs):
self.extra_context = kwargs.pop('extra_context', self.extra_context)
super(CustomCrispyField, self).__init__(*args, **kwargs)
def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, extra_context=None, **kwargs):
if self.extra_context:
extra_context = extra_context.update(self.extra_context) if extra_context else self.extra_context
return super(CustomCrispyField, self).render(form, form_style, context, template_pack, extra_context, **kwargs)
And I would use it like so in my form:
self.helper.layout=Div(CustomCrispyField('my_model_field', css_class="col-xs-3", template='general/custom.html', extra_context={'css_class_extra': 'value1', 'caption': 'value2'})
And my template would have code similar to the following:
{% crispy_field field %}
<button class="btn {{ css_class_extra }}">{{ caption }}</button>
You can just override standard crispy Field like this:
class LinkField(Field):
def __init__(self, *args, **kwargs):
self.view_name = kwargs.pop('view_name')
super(LinkField, self).__init__(*args, **kwargs)
def render(self, form, form_style, context, template_pack=CRISPY_TEMPLATE_PACK):
if hasattr(self, 'wrapper_class'):
context['wrapper_class'] = self.wrapper_class
if hasattr(self, 'view_name'):
context['view_name'] = self.view_name
html = ''
for field in self.fields:
html += render_field(field, form, form_style, context, template=self.template, attrs=self.attrs, template_pack=template_pack)
return html
Then just pass additional variable ('view_name') in overridden template.
In Layout it will look like this:
Layout(
LinkField('field_name', template='path_to_overridden_template',
view_name='variable_to_pass')
)
The tag context variable is set in the template, not the view. If you're using the built-in bootstrap3 template pack it's defined in the template that is including field.html. If the including template does not define tag then it defaults to div.
Ex: table_inline_formset.html line 41.
following bobort's answer, I would like to highlight that you therefor can use any queryset as arguments in the extra_content, which allows you to inject via the custom template an html rendering of anything you want, this becomes very interesting concept on how to transform the crispy forms into super agile. I am currently using forms to render my templates, instead of rendering forms in templates ^^

how to see form validation error in django view (not template)?

I am using ajax and need to pull my errors from form validation via ajax.
I try the following, but it prints blank:
if request.method == 'POST':
print form.errors
if form.is_valid():
try:
form.save()
except:
pass
What do I need to do to extract validation errors so that I can pass it to view via ajax, or even start with a print in the view?
Since form.errors is printed inside form.is_valid condition, it will never show error.
You can pull the errors in the views.py file using something like this:
if form.is_valid():
response = form.save()
return HttpResponse('ok inserted')
else:
if form['fieldname'].errors:
... your code here ...
return render(request, 'survey.html', context)

Resources