Add styling to form.as_div in Django - html

I am rendering a model form in Django using {{ form.as_div }}
It is rendering as expected as I want each field wrapped in a div.
I would now like to add a class to this auto generated div.
adding widgets in the forms.py file only adds attribute to the input field itself and not the div around the field.
Can someone point me in the right direction to add attributes to the div instead of the input field?

{{ form.as_div }} is not possible in Django but, you can do like this
====== in views.py =========
def DemoView(request):
form = StudentForm()
context = {'form':form}
return render(request,'demo.html',context)
======= in html file ========
<h1>Student Form</h1>
<form action="" method="get" >
{% for fm in form %}
<div>
<label>{{fm.label}}</label>
<p>{{fm}} </p>
</div>
{% endfor %}
</form>
======= Output =========

since django 4.1 you can use "as_div"!
I myself need this possibility in an earlier Version so i built myself an abstract Formclass containing my version of "as_div" derived from the existing render-functions:
class DivRenderer():
"""
as_div: abstract class
"""
def as_div(self):
"Return this form rendered as HTML <divs>s - according to client-Layout."
return self._html_output(
normal_row='<div class="form-group input" %(html_class_attr)s> %(errors)s%(field)s%(help_text)s %(label)s</div>',
error_row='<div%s</div>',
row_ender='</div>',
help_text_html='<br><span class="helptext">%s</span>',
errors_on_separate_row=False,
)
pass
and than using it simply while defining my forms like:
class someThingForm(forms.ModelForm, DivRenderer):
readonly = …
…
and than just using {{ form.as_div }} in the Templates
However!: The as_div-function is different as the one from 4.1 - so be careful not to expect the same behaviour!
hope it helps
Karl

Related

How to save HTML tags + data into sqlalchemy?

I am creating a personal blog website with Flask and sqlalchemy.
While posting my blogs, I want the blog to be published with well formatted html.
Here is my model for Blogs:
class Blog(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(), index=True)
description = db.Column(db.Text(), index=True)
content = db.Column(db.Text(), index=True)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
likes = db.Column(db.Integer, default=0)
dislikes = db.Column(db.Integer, default=0)
comments = db.relationship('Comment', backref='commented_by', lazy='dynamic')
def __repr__(self):
return 'Title <>'.format(self.title)
And here is my form for adding blog:
{% extends 'base.html' %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12">
<h1 class="code-line text-center" data-line-start="14" data-line-end="15">Add Blog</h1>
<br>
</div>
</div>
<form action="" method="POST" novalidate>
{{ form.hidden_tag() }}
<p>
{{ form.title.label }}<br>
{{ form.title(size=30) }}<br>
</p>
<p>
{{ form.description.label }}<br>
{{ form.description(size=30) }}<br>
</p>
<p>
{{ form.content.label }}<br>
{{ form.content() }}<br>
</p>
<p>
{{ form.submit() }}
</p>
</form>
{{ ckeditor.load() }}
{{ ckeditor.config(name='content') }}
{% endblock %}
This is how I am rendering my blog:
{% extends 'base.html' %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12">
<h1 class="code-line text-center" data-line-start="14" data-line-end="15">{{ blog.title }}</h1>
<br>
{{ blog.content }}
</div>
</div>
</div>
{% endblock %}
While adding blog, I am using a text editor
But once it has been posted and I render it on view blog page, no html content is being rendered not even linebreaks
How can I save html content and tags in my sql database and then render it using jinja template?
first, what is wrong:
the text you get from the text field in the form is not the same thing as HTML that renders it, what you are getting is the text.
in case you want to get the HTML generated inthat form, you should integrate a rich text editor, like quilljs.com, or tiny.cloud in your template, that will have a field that you can use, to grab the HTML it generated, and it will also allow you to create nice looking blog articles.
if you do not want this either, to get html from that form, writing HTML directly in that form will give you what you want.
In the context of markdown, it is actually possible to apply the same format to your database-saved content. You can use a few packages to help you work with HTML in your database.
To begin, let me suggest Stackoverflow QA forms. Notice how it has enabled markdown editing and a nice little preview of the text being written. To enable the preview, you can install the flask-pagedown package in your virtual environment.
(venv)$ pip3 install flask-pagedown
Initialize a pagedown object in your application's instance, say in __init__.py file, or whatever other file you are using.
# __init__.py
from flask import Flask
from flask_pagedown import PageDown
app = Flask(__name__)
pagedown = PageDown(app)
Within your head tags in HTML, add this CDN call whose files you do not need to have in your application.
<!-- base.html -->
{% block head %}
{{ pagedown.html_head() }}
{% endblock %}
Or this:
<head>
{{ pagedown.html_head() }}
</head>
If you prefer to use your own JavaScript source files, you can simply include your Converter and Sanitizer files directly in the HTML page instead of calling pagedown.html_head():
<head>
<script type="text/javascript" src="https://mycdn/path/to/converter.min.js"></script>
<script type="text/javascript" src="https://mycdn/path/to/sanitizer.min.js"></script>
</head>
Now, simply update your forms to use PageDownField:
# forms.py
from flask_pagedown.fields import PageDownField
class Post(FlaskForm):
post = PageDownField('Comment', validators=[DataRequired()])
Or this:
<form method="POST">
{{ form.pagedown(rows=10) }}
</form>
That's it! You should be able to have a client-side post preview right below the form.
Handling Rich Text in the Server
When the post request is made, then only raw markdown will be sent to the database and the preview will be discarded. It is a security risk to send HTML content to your database. An attacker can easily construct HTML sequences which don't match the markdown source and submit them, hence the reason why only markdown text is submitted. Once in the server, that text can be converted back to HTML using a Python markdown-to-html convertor. There are two packages that you can make use of. Install then in your virtual environment as seen below:
(venv)$ pip3 install markdown bleach
bleach is used to sanitize the HTML you want converted to allow for a set of tags.
At this point, the next logical step would be to cache your content field content while in the database. This is done by adding a new field, let us say content_html, in your database specifically for this cached content. It is best to leave your content field as it is in case you would want to use it.
# models.py
class Blog(db.Model):
content = db.Column(db.String(140))
content_html = db.Column(db.String(140))
#staticmethod
def on_changed_body(target, value, oldvalue, initiator):
allowed_tags = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code',
'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul',
'h1', 'h2', 'h3', 'p']
target.content_html = bleach.linkify(bleach.clean(
markdown(value, output_format='html'),
tags=allowed_tags, strip=True))
def __repr__(self):
return f'Title {self.title}'
db.event.listen(Blog.content, 'set', Blog.on_changed_body)
The on_changed_body() function is registered as a listener of SQLAlchemy’s “set” event for body , which means that it will be automatically invoked whenever the body field is set to a new value. The handler function renders the HTML version of the content and stores it in content_html , effectively making the conversion of the Markdown text to HTML fully automatic.
The actual conversion is done in 3 steps:
markdown() function does an initial conversion to HTML. The result is passed to clean() function with a list of approved HTML tags
clean() function removes any tags that are not in the whitelist.
linkify() function from bleach converts any URLs written in plain text into proper <a> links. Automatic link generation is not officially in the Markdown specification, but is a very convenient feature. On the client side, PageDown supports this feature as an optional extension, so linkify() matches that functionality on the server.
In your template, where you want to post your content you can add a condition such as:
{% for blog in blogs %}
{% if blog.content_html %}
{{ blog.content_html | safe }}
{% else %}
{{ blog.content }}
{% endif %}
{% endfor %}
The | safe suffix when rendering the HTML body is there to tell Jinja2 not to escape the HTML elements. Jinja2 escapes all template variables by default as a security measure, but the Markdown-generated HTML was generated by the server, so it is safe to render directly as HTML.

How do I access a single field while using For Loop to iterate through all the fields of a ModelForm in Django Template?

I have a model which has four ForeignKey fields, so they are dropdown fields in the form.
class Package(models.Model):
patient=models.ForeignKey(Patient, on_delete=CASCADE)
diagnosis=models.ForeignKey(Diagnosis, on_delete=CASCADE)
treatment=models.ForeignKey(Treatment, on_delete=CASCADE)
patient_type=models.ForeignKey(PatientType, on_delete=CASCADE)
date_of_admission=models.DateField(default=None)
max_fractions=models.IntegerField(default=None)
total_package=models.DecimalField(max_digits=10, decimal_places=2)
The forms.py:
class PackageForm(ModelForm):
class Meta:
model=Package
fields='__all__'
widgets={
"patient_type" : forms.Select(attrs={"onblur":"mf();"}),
"max_fractions" : forms.NumberInput(attrs={"onfocus":"mf();", "onblur":"tp();"}),
"total_package" : forms.NumberInput(attrs={"onfocus":"tp();", "onblur":"onLoad();"}),
'date_of_admission': DateInput(attrs={'type': 'date'}),
The views.py:
def package_view(request):
if request.method=='POST':
fm_package=PackageForm(request.POST, prefix='package_form')
if fm_package.is_valid():
package=fm_package.save()
IpdReport.objects.create(patient=package.patient, package=package)
fm_package=PackageForm(prefix='package_form')
return render (request, 'account/package.html', {'form5':fm_package})
else:
fm_package=PackageForm(prefix='package_form')
return render (request, 'account/package.html', {'form5':fm_package})
The Template:
<form action="" method="post" novalidate>
{% csrf_token %}
{{form5.non_field_errors}}
{% for fm in form5 %}
<div>
{{fm.label_tag}}
{{fm}}
<span>{{fm.errors|striptags}}</span><br><br>
</div>
{% endfor %}
<button type="submit" id="savebtn">Save</button>
</form>
Now, what I want is to insert an Anchor Tag next to all the foreign_key fields, in the template, to add a new object into the original table. For example, an Add Patient option next to the Patient's dropdown field, when clicked, a new, small window would show up with Patient form. The user enters the new patient's data, saves it and the same name shows up in the dropdown.
But as I am using a For Loop in the template, how would I be able to access those foreign key fields and apply the options? Any suggestions, please?
If it isn't a problem I would move away from rendering all of the fields with the 'forloop'. Instead I would use notation: form.field to render different fields. So it would look like:
{{ form.patient.label_tag }}
{{ form.patient }}
It should be much easier to navigate through fields this way, but of course it will require more typing :)

Django project - crispy forms not rendering in the browser

As part of a Django project, I have created the following in the views.py file
def profile(request):
u_form =UserUpdateForm()
p_form =ProfileUpdateForm()
context={
'u-form': u_form,
'p-form': p_form
}
I am now trying to render these forms on the html page (profile.html) with the following code:
{% extends "socialmedia/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<div class="media">
<img class="rounded-circle account-img" src="{{ user.profile.image.url }}">
<div class="media-body">
<h2 class="account-heading">{{ user.username }}</h2>
<p class="text-secondary">{{ user.email }}</p>
</div>
</div>
<form method="POST" enctype="multipart/form-data>
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Profile Information</legend>
{{u_form|crispy}}
{{p_form|crispy}}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Update....</button>
</div>
</form>
</div>
{% endblock content %}
Everything else is rendering on the page correctly, except for this bit:
{{u_form|crispy}}
{{p_form|crispy}}
There are no errors on running the server, so I am finding it hard to trouble shoot.
The code in the forms.py file is as follows:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserRegisterForm(UserCreationForm): #form that inherits from the usercreationform
email = forms.EmailField()
class Meta:
model = User
#when this form validates it creates a new user
#type the fields to be shown on your form, in that order.
fields = ['username','email','password1','password2']
"""this gives us a nested name space for configurations and
keeps the configs in one place. The model that will be affected is
the user model e.g. when we do a form.save it saves it to the user model.
And the fields we have are the fields we want on the form. It shows order too.
"""
#create a model form...this allows us to create a form that#works with a specific database model#
#we want a form that works with our user model
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username','email']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model= Profile
fields=['image']
My question is:
Could someone tell me why these additional fields (username, email and image update) are not being shown on the profile html above the 'update' button? In what file have I made the mistake. Note: I'd also appreciate an explanation of the rendering of these u-forms, along with the solution(pointing out my error). I understand that u-form is an instance of UserUpdateForm, but not much else.
context={
'u-form': u_form,
'p-form': p_form
}
You just have a typo. Change the - to _

DJANGO template tags in plain text not displaying

I am making an app that displays questions. The question model has a text field and an image field. Each question has a template that is stored in my database in the text field. My problem is when I want to access images from the model, template tags are displayed as text and not rendering. My code:
# question model
class Question(models.Model):
question_text = models.TextField()
question_image = models.FileField(upload_to='static/images/questions', blank=true)
# question view
def question(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'questiontemplate.html', {'question': question})
# template
{% extends 'base.html %}
{% load static %}
{% autoscape off %}
{{ question.question_text }}
{% endautoscape %}
# in my database:
question.question_text = '<p> some html
{{ question.question_image.url }}
some html </p>'
question.question_image = 'image.png'
This works fine and renders the html perfectly except the template tag is not rendered and does not not give the image url
I want this to be the output:
Some html
static/images/questions/image.png
some html
But instead this is the output:
some html
{{ question.question_image.url }}
some html
Any suggestions to how the template tags could be render from the database text would be much appreciated.
Thanks for reading
Django doesn't know that the content in your model field is itself a model. The template can't know that. The only way to make this work is to treat that field itself as a template, and render it manually.
You could do that with a method on the model:
from django.template import Template, Context
class Question(models.Model):
...
def render_question(self):
template = Template(self.question_text)
context = Context({'question': self})
rendered = template.render(context)
return mark_safe(rendered)
Now you can call it in your template:
{{ question.render_question }}

TextAreaField renders with itself (HTML code in readable text format) as its prepopulated text value

I'm rendering a WTForms TextAreaFields with Jinja2 in a Flask application and it has it's own HTML code as its prepopulated text value, although the default property (which should specify the prepopulated value) is set to empty string ''.
Form definition:
channels = TextAreaField('channels', default='')
Jinja2 template HTML file:
{% for c in e.form.conditions %}
{{ c.form.channels }}
{% endfor %}
Result (rendered, visible to end-user, should be empty string ''):
<textarea id="channels" name="channels"></textarea>
... (other iterations)
Result (HTML):
<textarea class="form-control" id="conditions-0-channels" name="conditions-0-channels"><textarea id="channels" name="channels"></textarea></textarea>
... (other iterations)
I double-checked using the Pycharm debugger and the TextAreaField as a whole object shows as the HTML result above, even though none of its properties contain the visible result string (also above), and the default property is equal to '' even though the result doesn't show so.
Bonus hint: for some reason, if the form containing the channels field is not part of a FormField inside a WTForms FieldList, this problem does not occur.
I don't know what on earth is going wrong with this combination of FieldList, FormField and TextAreaField, but if you call {{ c.form.channels.data }} (with extra .data) in your Jinja2 template HTML file instead of {{ c.form.channels }} then everything works fine.
Wow THANK YOU! I'm not sure what's going on either but this solved the issue for me too. I had some similar findings shown below:
Forms.py
class ChannelForm(FlaskForm):
notes = TextAreaField('Notes', render_kw={'class': 'form-control'}, default="")
channels.html
# These:
{{ channels.notes.data }} # Working solution
{{ channels.notes(value="test Value") }}
# Render these:
<textarea class="form-control" id="notes" name="notes"></textarea>
<textarea class="form-control" id="channels-0-notes" name="channels-0-notes" value="Test Value"><textarea class="form-control" id="notes" name="notes">
Test</textarea>