Flask: render_template with object passing says "method not allowed" - redirect

I have a dynamic table with students based on class block. That is the table has the list of students in each period block. The HTML which dynamically builds this is
html += "<tr>"+
"<td class='align-middle'><input type='checkbox' class='form-check' style='width:50px;height:50px;'></td>"+
"<td class='align-middle'><input type='image' onclick='StudentClicked(\""+item.FirstName+"\",\""+item.LastName+"\",\""+item.Suffix+"\",\""+item.NickName+"\")' src="+path+" onError='StudentImageError("+i+")' id='studentimage"+i+"' height='100'></td>"+
"<td class='align-middle'>"+item.FirstName +" "+item.LastName+"</td>"+
"<td class='align-middle'></td>"+
"<td class='align-middle'>0</td>" +
"<td class='align-middle'>0</td>"+
"</tr>"
Therefore when the user clicks the image, StudentClicked is called.
function StudentClicked(FirstName, LastName, Suffix, NickName){
$.getJSON('/studentSelected',
{firstName: FirstName, lastName: LastName, suffix: Suffix, nickName: NickName},
function(data){})
}
The idea here is that I want to click the image link and the getJSON will look for my app.route("/studentSelected"). I will perform a database call with the variables I have passed to this route and build an object. I want to pass this object to an html page. Here is the python code for the app.route
#app.route('/studentSelected', methods=['GET','POST'])
def studentSelected():
firstName = request.args.get('firstName', 0, type=str)
lastName = request.args.get('lastName', 0, type=str)
suffix = request.args.get('suffix', 0, type=str)
nickName = request.args.get('nickName', 0, type=str)
students = []
con, cur = ConnectToDatabase()
SQL = "select * from students where FirstName = ? and LastName = ? and Suffix = ? and NickName = ?"
entities = (firstName, lastName, suffix, nickName)
student, cur = Execute(SQL, cur, entities)
return render_template('studentcard.html', student=student)
Currently I get "Method Not Allowed. The method is not allowed for the requested URL." which I interpret as I'm not passing the right method to my route. But as you can I do pass both methods to my route. Why else would I be getting this error? studentcard.html exists in the folder templates.
One guess I have is that the return statement returns functionality to the getJSON function.
What is a better strategy for this type of process. The end result is for the clicked image to run app.route, call the database and then redirect the webapp to a new route for studentSelected and load and run this page.
contents of studentcard.html
{% extends 'layout.html' %}
{% block body %}
<div class="container mt-3">
{% from "includes/_formhelpers.html" import render_field %}
</div>
{% endblock %}

Related

Flask-WTF not validating with FieldList subforms

I am trying to get a test form that includes subforms to work but the form does not validate on submission. The form itself is submitting a recipe with ingredients as its subforms using FieldList(). I have also made sure to include hidden_tag() in the HTML.
Forms:
class IngredientsForm(FlaskForm):
ingredient_name = StringField("Ingredient", validators=[DataRequired()])
class RecipeForm(FlaskForm):
recipe_name = StringField("Recipe name", validators=[DataRequired()])
ingredients = FieldList(FormField(IngredientsForm), min_entries=2, max_entries=5)
submit = SubmitField("Submit")
Views:
#app.route("/", methods=["GET", "POST"])
def index():
form = RecipeForm()
return render_template("index.html", form=form)
#app.route("/submit", methods=["POST"])
def submit():
form = RecipeForm()
print(f"ERRORS: {form.errors}")
print(f"DATA: {form.data}")
if form.validate_on_submit():
print("Validated!")
print(form.recipe_name)
for ingredient in form.ingredients.data:
print(ingredient)
return redirect("/")
else:
print("Form not validated")
return render_template("index.html", form=form)
HTML:
<h1>Enter recipe:</h1>
<form action="/submit" method="POST">
{{ form.hidden_tag() }}
<p>
{{ form.recipe_name.label }} <br>
{{ form.recipe_name() }}
</p>
<p>
{{ form.ingredients.label }} <br>
{% for ing in form.ingredients %}
{{ ing.ingredient_name.label }}
{{ ing.ingredient_name() }}
<br>
{% endfor %}
</p>
<p>
{{ form.submit() }}
</p>
</form>
Output:
ERRORS: {}
DATA: {'recipe_name': 'butterbread', 'ingredients': [{'ingredient_name': 'butter', 'csrf_token': None}, {'ingredient_name': 'bread', 'csrf_token': None}], 'submit': True, 'csrf_token': 'Ijg1NmVjZjIwODY3MTJkNDNkMTFiNDQ2YzdiNzYyYzYyNmUzNGUzMWMi.YtaF7g.WRn25PWYMFplr_KV7RoZq1uLgrI'}
Form not validated
127.0.0.1 - - [19/Jul/2022 03:22:44] "POST /submit HTTP/1.1" 200 -
So far, no errors show up but it looks like in the data that since each subform has None as its csrf_token, maybe that's messing up the validation? I've tried getting this to validate for a while but to no avail.
You can disable csrf protection for the FlaskForm by setting the flag to false within the class Meta. CSRF protection is not necessary for nested forms as long as the parent form takes over this task.
class IngredientsForm(FlaskForm):
class Meta:
csrf = False
ingredient_name = StringField("Ingredient", validators=[DataRequired()])
class RecipeForm(FlaskForm):
recipe_name = StringField("Recipe name", validators=[DataRequired()])
ingredients = FieldList(FormField(IngredientsForm), min_entries=2, max_entries=5)
submit = SubmitField("Submit")
An alternative is to inherit from Form.
Figured it out. The problem is that the subform IngredientsForm inherits from FlaskForm which is a subclass that's csrf secure, and was the reason preventing validation. You don't need this as you have already the token in the main form.
Instead, have it inherit from wtforms.Form which doesn't have that. Another way is to disable csrf during init, but the previous method is preferred.

Add custom class in header region on drupal 8

I'd like to add custom class in my drupal template, in header region, but it doesn't works.
<!-- BEGIN OUTPUT from 'themes/marinenat/templates/layout/page.html.twig' -->
<div class="layout-container">
<header role="banner">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'region' -->
<!-- FILE NAME SUGGESTIONS:
x region--header.html.twig
* region.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/marinenat/templates/layout/region--header.html.twig' -->
<div class="nav">
I'd like to add this class after "nav" class di region--header.
Anyone can help me
You already have all you need in the output.
Many roads lead to Rome
If themes/marineat is you base theme, simply copy themes/marinenat/templates/layout/region--header.html.twig into your subtheme's themes/MYSUBTHEME/templates directory and edit this file. Flush cache. Done.
If themes/marinenat is you custom subtheme simply edit the suggested template file /themes/marinenat/templates/layout/region--header.html.twig/ and add your class there. Flush cache. Done.
Last but not least you can also add classes to a region from a preprocess function from your MYSUBTHEME.theme file or any MYMODULE.module file.
/**
* Implements template_preprocess_region().
*/
function MYTHEME/MYMODULE_preprocess_region(&$variables) {
if (isset($variables['region']) && $variables['region'] == 'header') {
$variables['attributes']['class'][] = 'MYCLASS';
}
}
How to pass a string from PHP to Twig
/**
* Implements template_preprocess_region().
*/
function MYTHEME/MYMODULE_preprocess_region(&$variables) {
$variables['foo'] = FALSE;
if (isset($variables['region']) && $variables['region'] == 'header') {
// Get the current node.
$node = \Drupal::routeMatch()->getParameter('node');
if ($node instanceof \Drupal\node\NodeInterface) {
// Get the node type.
$node_type = $node->bundle();
// Do what ever else you need to do to retrieve your class dynamically.
$variables['foo'] = $node_type;
}
}
}
Then in your region--header.html.twig Twig file it's:
{% if foo %}
<div class="nav {{ foo }}">
{{ content }}
</div>
{% endif %}

How to translate form errors with angular i18n?

I am working on i18n for angular and I would like to provide a translation for form errors. But I do not know how to do that. I followed the guide from angular website. And I tried to use the select method but it is not working.
Initially, before trying to translate, I had the following code in my component.ts:
onValueChanged(data?: any) {
if (!this.userForm) { return; }
const form = this.userForm;
for (const field in this.formErrors) {
// clear previous error message (if any)
this.formErrors[field] = '';
const control = form.get(field);
if (control && control.dirty && !control.valid) {
const messages = this.validationMessages[field];
for (const key in control.errors) {
this.formErrors[field] += messages[key] + ' ';
}
}
}
}
formErrors = {
'firstname': ''
};
validationMessages = {
'firstname': {
'required': 'Firstname is required.',
'pattern': 'Only alphabetics caracters are allowed.'
}
};
And the following code in my component.html:
<div *ngIf="formErrors.firstname" class="form-control-feedback alert">
{{ formErrors.firstname }}
</div>
It worked perfectly because there was no translation. Then, I made the following updates:
In the component.ts:
validationMessages = {
'firstname': {
'required': 'required',
'pattern': 'pattern'
}
};
In the component.html:
<div *ngIf="formErrors.firstname" class="form-control-feedback alert">
<ng-container i18n="##userModalFirstnameError">
{formErrors.firstname, select, required {required} pattern {pattern}}
</ng-container>
</div>
In the messages.fr.xlf file, I have this:
<trans-unit id="userModalFirstnameError" datatype="html">
<source>{VAR_SELECT, select, required {required} pattern {pattern} }</source>
<target>
{VAR_SELECT, select, required {Nom utilisateur obligatoire} pattern {pattern}}
</target>
...
</trans-unit>
Unfortunately, this solution does not work.
I finally found where the issue comes from. Actually, there were 2 mistakes.
The first one is related to the 'VAR_SELECT' in the .xlf file. It has been generated by Angular with the build command => "ng-xi18n --i18nFormat=xlf". This 'VAR_SELECT' works well if a "direct" variable is used (for example if I put "{toto, select, required {required} pattern {pattern}}" and toto was equal to "required"). But it seems that it does not work if a variable from table is used (which is my case with the variable "formErrors.firstname"). So I replaced 'VAR_SELECT' in the .xlf file by the name of my variable 'formErrors.firstname'.
The second one is in the "onValueChanged" function:
for (const key in control.errors) {
this.formErrors[field] += messages[key] + ' ';
}
Because of the space character at the end, the variable did not match one of the values defined (for example, it was 'required ' where I was expected 'required'. Note the additional space at the end of the 1st value)

"TypeError: filter_by()" error returned in flask when I am trying to use filter_by()

I am trying to implement filter_by() function of flask. My issue is, when I am trying to use filter for argument, I got the error of:
TypeError: filter_by() takes 1 positional argument but 2 were given
I also attached my code to the question.
Main application source code:
def login():
error = None
form = LoginForm(request.form)
if request.method == 'POST':
user = User.query.filter_by(Email=request.form['email']).first()
password = User.query.filter_by(Password=request.form['password']).first()
group = User.query.filter_by(user).first()
if user is None or password is None :
session['logged_in'] = False
flash('Please write your username password')
else:
session['logged_in'] = True
flash('You were logged in')
if group=="viewer":
return redirect(url_for('viewer'))
elif group=="admin":
return redirect(url_for('admin'))
elif group=="employee":
return redirect(url_for('employee'))
return render_template('login.html', form=form)
The model which I used for my app :
class User(db.Model):
__tablename__ = 'users'
Id = db.Column(db.Integer, primary_key=True)
Name = db.Column(db.String(64), index=True, unique=True)
Email = db.Column(db.String(64), index=True, unique=True)
Password = db.Column(db.String(128), index=True )
Group = db.Column(db.String(30))
Tickets = db.relationship('Request', backref='author', lazy='dynamic')
Login page source code:
<div class="innter-form">
<form class="sa-innate-form" method="post">
{{ form.csrf_token }}
<label>Email Address</label>
<input type="text" name="email" value="{{ request.form.email }}">
<label>Password</label>
<input type="password" name="password" value="{{ request.form.password }}">
<button type="submit" value="submit">Sign In</button>
Forgot Password?
</form>
</div>
filter_by is used for queries on the column names
...
# this returns a user object
user = User.query.filter_by(Email=request.form['email']).first()
# you should query based on the Id of User
group = User.query.filter_by(Id=user.Id).first()
Probably the problem is in the line
group = User.query.filter_by(user).first()
Besides the fact that filter_by accepts only keyword arguments - and you supplied only one positional argument to it, I am guessing you should use something different than User here (maybe Group, not sure since I don't know what models you have defined).
The line I mentioned should look something like
group = Group.query.filter_by(user=user).first()
Thank you all for response:
The eror was in group part as mention in above:
group = User.query.filter_by(user).first()
Instead of this code should be:
group = User.query.filter_by(Email=user).first()

JSP Error Postgres connection

I am trying to establish a connection between JSP and Postgres. Unfortunately I only see the table with its columnms, the columnms are not filled. How can I put data into the columns?
My code:
<%# page contentType="text/html; charset=iso-8859-1" language="java"
import="java.sql.* "%>
<%# page import="java.io.*"%>
<html>
<head>
<title>Films Example: JSP</title>
</head>
<body bgcolor="white">
<div>
<h1>Filmworld</h1>
<table border=1>
<tr>
<td>imdbID</td>
<td>name</td>
<td>year</td>
<td>rating</td>
<td>votes</td>
<td>runtime</td>
<td>actors</td>
<td>genres</td>
<td>directors</td>
</tr>
<%
try {
String driver = "org.postgresql.Driver";
String url = "jdbc:postgresql://localhost:5432/movie";
String username = "postgres";
String password = "cemcan";
String myDataField = null;
String myQuery = "SELECT * FROM movie";
Connection myConnection = null;
PreparedStatement myPreparedStatement = null;
ResultSet myResultSet = null;
Class.forName(driver).newInstance();
myConnection = DriverManager.getConnection(url, username, password);
myPreparedStatement = myConnection.prepareStatement(myQuery);
myResultSet = myPreparedStatement.executeQuery();
out.println("<table border=1>");
while (myResultSet.next()) {
String imdbID = myResultSet.getString("imdbID");
String name = myResultSet.getString("name");
int year = myResultSet.getInt("year");
double rating = myResultSet.getDouble("rating");
int votes = myResultSet.getInt("votes");
int runtime = myResultSet.getInt("runtime");
String directors = myResultSet.getString("directors");
out.println("<tr><td>" + imdbID + "</td><td>" + name + "</td><td>" + year + "</td><td>" + rating
+ "</td><td>" + votes + "</td> <td>" + runtime + "</td> <td>" + directors + "</td> </tr>");
}
out.println("</table>");
myConnection.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException ex) {
out.print("SQLException: " + ex.getMessage());
out.print("SQLState: " + ex.getSQLState());
out.print("VendorError: " + ex.getErrorCode());
}
%>
</table>
</div>
</body>
</html>
your code has at least two problems:
if you don't have the jdbc driver in the classpath the error goes
into the application server output since you have a line with
e.printStackTrace();. you can check if this is the case because in
the html generated by jsp you did not see the html <table
border=1>
the html is not correct you are putting a table element just after
a .. element. the elements taken ffrom the db do not need
an extra tables, thar are already rows. Using a tbody is more
appropriate
I see that this is an example and it's ok for learning, but in "production code" put the login on the server side (servlet, framework controller, etc) and use at least <%=... %> in jsp instead of out, it helps to separate page markup to program variables