How to implement an object specific modal in Django? - html

I'm new to Django and I have a problem moving my stuff to the front end of my website.
I want to do fantasy-style website. My main page will display a list of NHL players. These players will be picked by the user and added to their team in order .
My problem is this: I want a popup-style modal to open when a user clicks a player on the main page. This modal would contain a confirmation before this specific player is added to the user's team.
Here is my player model:
class Player(models.Model):
name = models.CharField(max_length=30)
position = models.CharField(max_length=5)
Here is my main page view:
def main_page(request):
players = Player.objects.all()
my_dict = {"players":players}
return render(request, 'my_app/main_page.html', context=my_dict)
Here is my template for the main page:
{% extends "my_app/base.html" %}
{% block body_block %}
<button onclick="document.getElementById({{player.name}}).style.display='block'" class="w3-button w3-black">Open Modal</button>
<div class="w3-modal-content">
<div class="w3-container">
<span onclick="document.getElementById({{player.name}}).style.display='none'" class="w3-button w3-display-topright">×</span>
<p>{{player.position}}</p>
</div>
</div>
{% for player in players %}
<tr>
<th>{{player.name}}</th>
<th onclick="document.getElementById({{player.name}}).style.display='block'" class="w3-button w3-black" width="100%">Open Modal</th>
</tr>
{% endif %}
{% endblock %}
As you can see, I would like the modal popup to display the player's position.
Unfortunately, not only is the modal not working, I have no clue how to make it display attributes of my Player model.
Would anybody know how to modify my html to fix the modal?
Thank you very much for your help!
EDIT: found a very interesting answer on How do I integrate Ajax with Django applications?

Related

Form not submitting to correct url in django

Im making a web app in django and im having a problem with a form which isnt submitting to the correct place
searcc.html
<form method='POST', action='/saveApply'">
{% csrf_token %}
<div class="JobSub">
<input value="{{user.id}}" name="usrid">
<input value="{{comp.id}}" name="compid">
<button type="submit">Submit</button>
</div>
</form>
views.py
def saveApply(request):
current_user = request.user
if request.method == 'POST': # Foreign key to User is just the username
savedApply.objects.create(
user = request.POST.get('usrid'),
company = request.POST.get('company')
)
return render('main/profile.html') # Change later
return redirect('login')
The confusing thing is, when I press on the submit button Im not even getting sent to the login view. I am getting sent to the home page. I think the problem has something to do with the fact that this html page is getting included in another.
main.html
{% include 'main/search.html' %}
{% endblock %}
Main.html is also inheriting from another file
Urls.py
path('saveApply/', views.saveApply, name="saveApply"),
path('feed/', views.feed, name='feed'),

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.

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 _

Add form choices as options in a select tag html

I'm in my final project and I'm a little bit lost on what I'm doing now.
I'm developping a website and I need to insert some choices into a html select tag, I mean, that choices must be options of this select tag. I'm trying it, but the dropdown doesn't appear as it should, and, when I try to submit my form, it gives me an error. I wish you could help me, I'm becoming crazy. Here I leave my code, feel free to ask anything you want
forms.py
class FormCancion(forms.ModelForm):
NOTAS_CHOICES=(('35','Acoustic Bass Drum'),('36','Bass Drum 1'),('37','Side Stick'),('38','Acoustic Snare'),('39','Hand Clap'),
('40','Electric Snare'),('41','Low Floor Tom'),('42','Closed Hi Hat'),('43','High Floor Tom'),('44','Pedal Hi-Hat'),
('45','Low Tom'),('46','Open Hi-Hat'),('47','Low-Mid Tom'),('48','Hi-Mid Tom'),('49','Crash Cymbal 1'),('50','High Tom'),
('51','Ride Cymbal 1'),('52','Chinese Cymbal'),('53','Ride Bell'),('54','Tambourine'),('55','Splash Cymbal'),
('56','Cowbell'),('57','Crash Cymbal 2'),('58','Vibraslap'),('59','Ride Cymbal 2'),('60','Hi Bongo'),('61','Low Bongo'),
('62','Mute Hi Conga'),('63','Open Hi Conga'),('64','Low Conga'),('65','High Timbale'),('66','Low Timbale'),('67','High Agogo'),
('68','Low Agogo'),('69','Cabasa'),('70','Maracas'),('71','Short Whistle'),('72','Long Whistle'),('73','Short Guiro'),
('74','Long Guiro'),('75','Claves'),('76','Hi Wood Block'),('77','Low Wood Block'),('78','Mute Cuica'),('79','Open Cuica'),
('80','Mute Triangle'),('81','Open Triangle'))
nota_pad_gris = forms.ChoiceField(choices=NOTAS_CHOICES, widget=forms.Select())
views.py:
def crearCancion(request):
cancion=Cancion()
if request.method=="POST":
formulario=FormCancion(request.POST,request.FILES,instance=cancion)
if formulario.is_valid():
formulario.save()
return HttpResponseRedirect('/ListadoCanciones/')
else:
formulario=FormCancion()
context={'formulario':formulario}
return render(request,"nuevaCancion.html",context)
.html:
<br><br><br>
<div class="container">
<form id='formulario' method='post' {% if formulario.is_multipart %} enctype="multipart/form-data" {% endif %} action=''>
{% csrf_token %}
<center>
<label for="nota_pad_gris">Nota del pad Gris:</label>
<select id="nota_pad_gris" onchange="validar();">
{% for value in formulario.nota_pad_gris %}
<option value="hola">{{ value }}</option>
{% endfor %}
</select>
<br><br>
<p><input type='submit' class="btn btn-success btn-lg" value='Añadir'/>
Cancelar</p>
</center>
</form>
<br>
</div>
Edit:
Look at Nota del pad gris. This is what the list shows. It shows the choices, but also shows a blank space between the choices.
[
This is the error that the web gives me when I try to submit the form

Populating multiple plotly offline plots<div> of HTML page using flask

I am trying to embed multiple plotly offline plots into an HTML page at different locations on the page. I am using flask at the back-end. Currently I am unable to change the properties of the returned by plotly offline plot to change the location, width, height on the HTML page. I am new to the front-end development concept so pardon my ignorance.
I am using the below test code to create div plot.
from plotly.offline import plot
import plotly.graph_objs as go
def plot_test():
months = ['Jan', 'Feb', 'Mar']
user = [10,50,30]
t20h = [17, 46, 39]
trace1 = go.Scatter(x= months, y=user, mode='lines+markers', name="OHR")
trace2 = go.Scatter(x= months, y=t20h, mode='lines+markers', name="T20H")
data = [trace1, trace2]
layout = go.Layout(title='Test', xaxis=dict(title='Months'), yaxis=dict(title='Test'))
fig = go.Figure(data=data, layout=layout)
div_output = plot(fig, output_type='div', include_plotlyjs=False)
return div
And below code to render the results in my HTML
from plot_test import plot_test
#app.route('/')
def home():
plotly_graph = plot_test()
return render_template("index.html", plotly_graph=Markup(plotly_graph))
And below is my HTML. Currently the plot gets crated but at the top of everything else. All my other buttons.
<!DOCTYPE html>
<html>
<head>
<title>Intro</title>
<link href="static/index_style.css" rel="stylesheet">
</head>
<body>
<div id="home">
<form action="/" method="GET">
<input type="submit" class="btn btn-default>" value="HOME" id="home-btn">
</form>
</div>
<div id="logout">
<form action="/logout" method="GET">
<input type="submit" class="btn btn-default>" value="LOGOUT" id="logout-btn">
</form>
</div>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
{{ plotly_graph }}
<!-- errors -->
<!-- {% if error %}
<p class="error"><strong>Error:</strong> {{ error }}</p>
{% endif %}
-->
<!-- messages -->
<!-- {% for message in get_flashed_messages() %}
{{ message }}
{% endfor %} -->
</body>
</html>
I used a slightly different approach :
(simplified)
In my python code, I returned fig instead of the plot itself
In the HTML, I created a div with id( 'div_name'
In Javascript, through the API call received the data to recreate the plot
and then I only had to add Javascript: Plotly.plot("div_name",fig[("data],fig["layout"].
This way you have more flexibility trying out different sizing methods for either your div or the width-height arguments of the layout itself.