How to treat CMS fields that support HTML [Django] - html

I have a Django site with a Post object like so:
class Post(models.Model):
title = models.CharField(max_length=100,blank=True,null=True)
body = models.TextField(blank=True,null=True)
author = models.ForeignKey(User,blank=True,null=True)
date_created = models.DateTimeField(default=timezone.now)
date_updated = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to=post_dir, blank=True, null=True)
def __unicode__(self):
return unicode(self.date_created.strftime('%Y-%m-%d %H:%M') + ' ' + self.title)
which outputs body TextField like so in order to support HTML:
{% if post.body %}
<p>
{{ post.body | safe }}
</p>
{% endif %}
My question is, since the admins can input HTML which could potentially malform the html (such as post.body = '</div></div>'), what is the best way to format and sanitize this textfield while still allowing users to input html?

I got the desired functionality with some extras using django-ckeditor

Related

How do I use a for loop to reuse a django form in a template

After struggling with this issue for a while, I am hoping someone here can point me in a more productive direction.
I am trying to take an indeterminate number of variables in a database (obtained through a different template) and render them on a webpage, each variable with a simple data entry form, to be saved back to the database. Basically, it's a tracker for analysis. Say I want to track my daily sleep, running time, and calorie intake (the variables). I have those saved in a database as variables and want to call upon those variables and show them on a webpage with a daily entry form. I am using a "for" loop right now and it renders the way I want it to, with the variable name and the form, but it is only saving the last item in the variable list. How do I amend the code below such that when I hit the save button for each form rendeded, it saves the information for that variable (not just the last one rendered).
Below is the code. Any and all help would be infinitely appreciated.
Models...
class Variable(models.Model):
date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
ENTRY_TYPE_CHOICES = [
('numeric', 'enter a daily number'),
('scale', 'rate daily on a scale of 1-10'),
('binary', "enter daily, 'yes' or 'no' "),
]
variable = models.CharField(max_length=50, default='')
entry_type = models.CharField(max_length=50, choices=ENTRY_TYPE_CHOICES, default="numeric")
def __str__(self):
return self.variable
class DailyEntry(models.Model):
date = models.DateField(default=datetime.date.today)
# date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
variable_name = models.CharField(max_length=50, default='')
variable_id = models.SmallIntegerField(default=0000)
entry = models.FloatField(default=9999)
class Meta:
verbose_name_plural = 'Daily Entries'
def __str__(self):
return self.variable
Form...
class VariablesForm(forms.ModelForm):
class Meta:
model = Variable
fields = ['variable', 'entry_type' ]
labels = {'variable':'Dependent variable to track', 'entry_type': 'Type of measure'}
class DailyEntryForm(forms.ModelForm):
class Meta:
model = DailyEntry
fields = ['variable_name', 'variable_id', 'entry', 'date']
labels = {'entry': 'Daily entry', 'date': 'Date'}
widgets = {'variable_name': forms.HiddenInput(), 'variable_id': forms.HiddenInput()}
Views...
def daily_entry(request):
''' page to make daily entries '''
vars = Variable.objects.filter(id__gt = 0 )
if request.method != 'POST':
# No data submitted. GET submitted. Create a blank form
form = DailyEntryForm()
else:
#POST data submitted. Process data
form = DailyEntryForm(data=request.POST)
if form.is_valid():
data = form.save(commit=False)
data.created_by = request.user
data.save()
return HttpResponseRedirect(reverse('entry_new'))
context = {'form': form, 'vars': vars}
return render(request, 'entry_new.html', context)
and HTML...
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% for var in vars %}
<div>
<ul>
<h3>{{ var.variable }}</h3>
<form class="" action="" method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="hidden" name="variable_id" value="{{ var.id }}" >
<input type="hidden" name="variable_name" value="{{ var.variable }}">
<input type="submit" name="" value="Save" />
</ul>
</div>
{% endfor %}
{% endblock content %}
Any help, well, helps...
Thanks!

Django many to many field data rendering

I wanted to create a blog site where an author and editor can both have the edit option and the editors will be assigned by author. Now my model field looks like this :
class Editor(models.Model):
name = models.OneToOneField(Author, on_delete=models.CASCADE, null=True, blank=True)
class Blog(models.Model):
title = models.CharField(max_length=150, null=True, blank=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE, null=True, blank=True)
editor = models.ManyToManyField(Editor, blank=True)
date_created = models.DateTimeField(auto_now_add=True)
article = models.TextField(blank=True, null=True)
genre = models.ManyToManyField(Genre, blank=True)
def __str__(self):
return self.title
and views.py :
def blog(request, pk):
if request.user.is_authenticated:
blogs = Blog.objects.get(id=pk) //for dynamic url
editors = Editor.objects.all()
context = {'blogs':blogs,'editors':editors}
return render(request, 'blog/blog.html', context)
then I wanted to check if the person who is accessing if author or editor or not, so I written an if condition :
{% if request.user.author == blogs.author or blogs.editor %}
Edit
{% endif %}
but unfortunately the edit button can be accessed by everyone. what should I do?
First of all.
{% if request.user.author == blogs.author or blogs.editor %}
Edit
{% endif %}
In this piece of code, the condition is not very good defined. My suggestion is that you make this in this way:
{% if request.user.author == blogs.author or request.user.author == blogs.editor %}
Edit
{% endif %}
This is one suggestion.
Now another posibility is to use JavaScript.
In order to do that, I give you some code example of my own.
<script type="text/javascript">
var editor = '{{user.author.editor}}'
if (editor == 'False'){
document.getElementById('edit').innerHTML = ''
}
</script>
In this code, I'm defining that if the user is not a editor, I will not hide the element (In html) with id 'edit'. So in your template, set an id to your element.
<a id="edit" href="#" class="btn btn-warning">Edit</a>
For this, you will have to set a boolean field in your model.
You can do it like this:
class Editor(models.Model):
name = models.OneToOneField(Author, on_delete=models.CASCADE, null=True, blank=True)
editor = models.Boolean()

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

Django - Filter Model Objects (Posts) Using Form

So I have a model called Blog
class Blog(models.Model):
tagoptions = (
(u'Project', u'Project'),
(u'IT', u'IT'),
(u'Robotics', u'Robotics'),
. . .
(u'-', u'-'),
)
user = models.TextField(blank=True, null=True)
title = models.CharField(max_length=50, default='')
context = models.TextField(max_length=5000, default='')
ireceived = models.IntegerField(default=0, blank=True, null=True)
personnelneeded = models.IntegerField(default=1)
datewritten = models.DateField(default=datetime.date.today, blank=True, null=True)
tags = models.CharField(max_length=100, choices=tagoptions, default='-')
def __str__(self):
return str(self.user) + ": id" + str(self.id) + " - title: " + str(self.title)
and a form for filtering the objects using "tags"
class FilterOption(forms.Form):
tagoptions = (
(u'showall', u'Show All'),
(u'Project', u'Project'),
(u'IT', u'IT'),
. . .
(u'-', u'-'),
)
tags = forms.ChoiceField(choices=tagoptions, initial="showall")
tags.widget.attrs.update({'style' : 'color: black;'})
This is the part of the form for filtering in my template.
<form method="post" action="{% url 'posts' %}">
{% csrf_token %}
<p>{{ form.tags }} <input type="submit" name="filter" value="Filter"/></p>
</form>
Here is what I use in my template to display the top 10 most recent posts.
{% for p in postshow|slice:":10" %}
<br><br>
<div class="postdisplay">
<h1><a class="stuffurl" href="{% url 'posted' postid=p.id %}"><u>{{ p.title }}</u></a></h1>
<h4>Post by <a class="stuffurl" href="{% url 'user' user_login_name=p.user %}"><u>{{ p.user }}</u></a>, {{ p.datewritten}}</h4>
<h4>Tag: {{ p.tags }}
</div>
<br><br>
{% endfor %}
Then finally in my views I have
def posts(request):
postshow = Blog.objects.order_by('-id')
if request.method == 'POST':
form = FilterOption()
if form.is_valid():
filteropt = form.cleaned_data['tags']
if filteropt != showall:
postshow = Blog.objects.order_by('-id')
else:
postshow = Blog.objects.filter(tags=filteropt).order_by('-id')
else:
form = FilterOption()
context = {'postshow' : postshow, 'form' : form}
return render(request, 'home/posts.html', context)
When ever I submit the form, it's supposed to check if the input says show all or not. If it does then it does not filter anything and if not it filters the objects so that only the objects with the tag of the input gets shown. However, the code seems to be stuck at form.is_valid (I checked using print). Another possible problem is that even if it worked pass form.is_valid, the filter won't work since the input is refreshed whenever you submit the form.
It will be greatly appreciated if someone can help me fix my code so that I can select a tag and the post will be filtered and if I select "show all", it shows all posts without filtering them.
Before checking if the form is valid, try passing the request.POST data into it:
form = FilterOption(request.POST)

Flask-SQLAlchemy queries

I am having issues with a seemingly simple sqlalchemy query using Flask.
I have a table called Links and within that table there are columns called 'id', 'author_id', 'link', and 'group'. My models.py looks like this:
class Links(db.Model):
__tablename__='links'
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
link = db.Column(db.String, unique=False, nullable=True)
group = db.Column(db.String, unique=False, nullable=False)
def __init__(self, author_id=None, link=None, group=None):
self.author_id = author_id
self.link = link
self.group = group
def __repr__(self):
return'<Link %r>' %(self.link)
I would like to return the values of all groups associated with the user that is logged into the application. Here is my views.py file:
#app.route('/members/', methods=['GET','POST'])
#login_required
def members():
error=None
form = PostLink(request.form, csrf_enabled = False)
uid = session['user_id']
link = "NULL"
groups = Links.query.filter_by(author_id=uid).all()
if request.method=='POST':
if form.validate_on_submit():
new_group = Links(
uid,
form.group.data,
link,
)
try:
db.session.add(new_group)
db.session.commit()
flash("You have created a group!")
except IntegrityError:
error = 'That group did not work, maybe it already exists?'
else:
flash_errors(form)
return render_template('members.html', form=form, error=error, link = link, groups=groups)
And my 'members.html':
{% extends "base.html" %}
{% block content %}
<p>Add New Group: {{ form.group }}</p>
<input id="link" type="hidden" name="link" value= {{ link }}/>
<p><input type="submit" value="Request"></p>
</form>
<br/>
{% for group in groups %}
<li><p>
{{ group }}
</p></li>
{% endfor %}
{% endblock %}
Currently this is just returning a list of links and groups in an odd format:
<Link u'link2'>
<Link u'linky'>
<Link u'linkymaybe'>
<Link u'madeit'>
<Link u'BLAH'>
So the core of my question is how do I build a query using SQLAlchemy to display all groups associated with the logged in user (uid = session['user_id']) I am pretty new to Flask and this problem is becoming an issue as I have tried a number of filter_by and filter statements with no luck at all.
Thank you in advance!
It is displaying correctly the object "Link" returned by the query.
You need to format it in the template.
Thisi is link {{ group.link }} from author #{{ group.author_id }} in group named {{ group.group }}
Maybe you've chosen a bad name "group" when cycling on results in the template. It should be called link.
In the template, you can show the group name using {{ link.group }} instead of {{ link }}. The query is returning the whole object.