Cannot write data to db in Flask - sqlite

I am working on a small reading tracking app in Flask. I cannot seem to write data from one of my routes, below:
#app.route('/add_book', methods=['GET', 'POST'])
#login_required
def add_book():
form=BookForm()
if request.method=='POST':
if form.validate_on_submit():
book=Book(
title=form.title.data,
author=form.author.data,
# category=form.category.data,
# added_on=form.added_on.data,
# done=form.done.data,
user_id=current_user
)
db.session.add(book)
db.session.commit()
flash('Book added')
return redirect(url_for('books'))
else:
flash('ERROR. The book not added.')
return render_template('add_book.html', form=form)
This is the corresponding HTML:
{% extends "layout.html" %}
{% block content %}
{% if form %}
<form action="{{ url_for('add_book') }}" method="post">
{{ form.hidden_tag() }}
{{ form.title.label }}<br>
{{ form.title() }}<br>
{{ form.author.label }}<br>
{{ form.author(cols=32, rows=4) }}<br>
<!-- {{ form.category.label }}<br>
{{ form.category() }}<br> -->
{{ form.submit() }}
</form>
{% endif %}
{% endblock %}
When the page renders, the label and forms for the book and author appear, however on clicking Submit, the data does not get saved.
The code section is similar to that for registering a user and I am stuck on what to do because I cannot see any errors. I am using SQLite as a database.
Here is the book model:
class Book(db.Model):
__tablename__='books'
id=db.Column(db.Integer, primary_key=True)
title=db.Column(db.String(100))
author=db.Column(db.String(50))
# category=db.Column(db.String(50))
added_on=db.Column(db.DateTime, index=True, default=datetime.utcnow)
done=db.Column(db.Boolean, default=False)
user_id=db.Column(db.Integer, db.ForeignKey('users.id'))
def __init__(self, title, author, added_on, done, user_id):
self.title=title
self.author=author
self.added_on=added_on
self.done=done
self.user_id=user_id
def __repr__(self):
# return '<Book: Title - {0}, Author - {1}, Category - {2}>'.format(self.title, self.author, self.category)
return '<Book: Title - {0}, Author - {1}>'.format(self.title, self.author)

The current_user is a user proxy giving access to all user attributes or use something like this How to track the current user in flask-login?
a quick solution would be to change this line
user_id = current_user
into this
user_id = current_user.id
Update:
ok, I got it working. You need the following fixes, each of them leads to trouble with form validation and or committing to database:
- use current_user.id in your book object as I said earlier.
- removed the init method in books model. I'm not sure what the added value is at the moment, I'm getting error messages about fields added on and done which are not on the form. I haven't taken the time to look into it further.
- just go for if request.method=='POST' as you don't need both. The form will be checked for validation anyways.
tip: don't forget to create a requirements file (pip freeze --> requirements.txt), that makes it a lot easier to reinstall in a new virtual environment.

Related

Flask-WTForms .validate_on_submit() never executes

So basically I want to have a Updateform() form that allows users to update their account details. All my other forms (register, etc.) work perfectly fine but this specific form never validates on submit. Hence when I press the submit button the page just refreshes but the .validate_on_submit() code doesn't execute.
I've looked through the forums and a common issue I found is the .CSRF token missing, but I'm using form.hidden_tag() which I read should work perfectly fine. So it seems that my issue is unique.
I've been looking on the forums for hours but haven't found a solution.
Here is my form code:
class Updateform(FlaskForm):
email = StringField('Email:', validators=[DataRequired(), Email()])
picture = FileField('Update Profile Picture:', validators=[FileAllowed(['jpg', 'png'])])
submit = SubmitField("Update")
def validate_email(self, email):
if email.data != User.email:
if User.query.filter_by(email=email.data).first():
raise ValidationError('Email has already been registered')
Here is the route:
#users.route('/account', methods=['GET', 'POST'])
#login_required
def account():
form = Updateform()
print("hello")
if form.validate_on_submit():
print(form)
print("YES!!!")
name = current_user.name
pic = add_profile_pic(form.picture.data, name)
current_user.profile_image = pic
current_user.email = form.email.data
db.session.commit()
flash("Account Updated")
# elif request.method == "GET":
# form.email = current_user.email
profile_image = url_for('static', filename='profile_pics/'+current_user.profile_image)
return render_template('account.html', profile_image=profile_image, form=form)
And here is the html code:
{% extends "base.html" %}
{% block content %}
<div align="center">
Hi {{ current_user.name }}<br>
<img align="center" src="{{ url_for('static', filename='profile_pics/'+current_user.profile_image) }}">
</div>
<div class="container">
<form method="post">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.email.label(class='form-group') }}
{{ form.email(class='form-control') }}
</div>
<div class="form-group">
{{ form.picture.label(class='form-group') }}
{{ form.picture(class='form-control') }}
</div>
<div class="form-group">
{{ form.submit() }}
</div>
</form>
</div>
{% endblock %}
The extra classes you see are from the bootstrap library incase anyone is wondering.
Hello you could find out what the problem is by adding a else for you if form.validate_on_submit(): and have it do this
for error in form.email.errors:
print(error)
for error in form.picture.errors:
print(error)
this should tell you what is not working hope this helps and I have not tested this so it could have typos

Display dynamically informations in a Symfony layout

I have a layout included in another one that display my menu. The labels of my menu items need to be dynamic (like the unread messages number of a mailbox). Then I did this :
My orders
(
{{ render(controller('MyController', {'etat':2})) }}
<span style="color:red">with {{ render(controller('MyController', {'etat':2})) }} in late</span>
)
I would like to display labels according to the number that return my controller. I don't know how to get it in a variable.
When rendering your template in your controller
return $this->render('twig_template_name.html.twig', array('variable_name' => $variable);
you pass a variable to the twig template in the array of options as I showed. Your code
{{ path('mypath',{'etat': '2' }) }}
prints a path defined in the routing.yml under the 'mypath' section and ends up adding a GET request variable to the link ('?etat=2'), if 'mypath' showed an absolute route 'www.website.com/yourpath',
{{ path('mypath',{'etat': '2' }) }} would produce 'www.website.com/yourpath?etat=2', which would send your controller for a route /yourpath/{etat} a variable etat with a value of 2 so all you need to do now is change 2 with an actual dynamic value which you receive from another controller.
I am not sure what etat is but lets say it's an article and it has it's id, you have a blog page with lots of articles and the controller that prints them all out sends an array of articles to the twig template, on your twig template you do something like:
{% foreach article in articles %}
{{ article.title }}
{{ article.story }}
read more
{% endforeach %}
And you end up something like:
Catchy Title
Awesome story about code without bugs and where deadlines depend on how creative and well designed and implemented the solutions are
[read more]
and you ofcourse click on "read more" and end up on the url ~/article/2 because the article had an id of 2, your controller for that url receives a variable id in the request, you do a $id = $_GET['id']; and grab the article from the repository and send it to the template.
Hopefully this answered your question, I am very tiered so forgive me if i was confusing which I surely was.

How can I print Google Books api description?

Hie I am trying to get the synopsis and other items like author and published date printed. But I am Able to achieve this only with certain search terms, an error occurs with other words or terms
Key "description" for array with keys "title, subtitle, authors, publishedDate, industryIdentifiers, readingModes, pageCount, printType, categories, maturityRating, allowAnonLogging, contentVersion, imageLinks, language, previewLink, infoLink, canonicalVolumeLink" does not exist.
I am using symfony and twig. this is what the twig file looks like :
{% for item in items %}
<article>
<img src="{{ item.volumeInfo.imageLinks.thumbnail}}"/>
<h4>{{ item.volumeInfo.title}}</h4>
{{ item.volumeInfo.description }}
<strong> {{ item.volumeInfo.publishedDate }}</strong><br/>
<b>{{ item.volumeInfo.authors | join }}</b>
</article>
What am I doing wrong? why does this work only sometimes ? how can I make it work correctly all the time?
class GoogleBooksController extends Controller
{
public function getVolumeAction($title)
{
$client =new client();
$response = $client- >get("https://www.googleapis.com/books/v1/volumes?q=$title");
$data=$response->json();
$items=$data['items'];
return $this->render('BookReviewBundle:GoogleBooks:volume.html.twig', array('items'=>$items
// ...
)); }
Thanks
I belive the description field is not mandatory, so you can do follow
{% if item.volumeInfo.description is defined %}
{{ item.volumeInfo.description }}
{% endif %}

Using doctrine database object in a template

I am new to Symfony and am finally beginning to understand how to query a database using a Doctrine. However, I am lost as far understanding how to use the database object content in a Twig template.
Lets say my database object contains product Id's, names, prices, for 50 different products. After I am done querying the database in the controller, I do the following, to pass the database object into the Twig template:
public function searchAction($word)
{
//query database using the $word slug and prepare database object accordingly
$dataObject; // contains query results
return $this->render('GreatBundle:Default:search.html.twig', array('word' => $word));
}
This is where I am stuck. Now I have a Twig template, I would like to pass the DB object from the controller and then print out the database data in my Twig template.
I appreciate any suggestions as to how I can accomplish this.
Many thanks in advance!
I'll respond with an example (more easier for me to explain)
You want to search something with a slug (the var $word in your example). Let's say you want to find a article with that.
So your controller :
public function searchAction($word)
{
//query database using the $word slug and prepare database object accordingly
// Search the list of articles with the slug "$word" in your model
$articleRepository = $this->getDoctrine()->getRepositoy('GreatBundle:Article');
$dataObject = $articleRepository->findBySlug($word);
// So the result is in $dataObject and to print the result in your twig, your pass the var in your template
return $this->render('GreatBundle:Default:search.html.twig', array('result' => $dataObject));
}
The twig template 'GreatBundle:Default:search.html.twig'
{% for item in result %}
{{ item.title }} : {{ item.content }}
{% endfor %}
Just look the second example in the Symfony2 Book (Sf2 Book - templating), you have to use the function "for" to parse your object (like an array in php !)
Example in your twig template :
{% for item in word %}
{{ item.id }} - {{ item.name }} - {{ item.description }}{# etc... #}<br>
{% else %}
<h2>Aoutch ! No data !</h2>
{% endfor %}
Ah, and it's not the good var in your render method (but it's was for your example !)
public function searchAction($word)
{
//query database using the $word slug and prepare database object accordingly
$dataObject; // contains query results
return $this->render('GreatBundle:Default:search.html.twig', array('word' => $dataObject));
}

Table sheet like data in symfony2 form

I need to have this type of data in my form :
value11, value12, value13 ...
value21, value22, value23
value31, value32, value33
value41, value42, value43
...
This is how the form should look like :
Is there any form type that can do that automatically or the form must be created manually? There is collection type but its seems cant fit in my case. I am planing to serialize data from this table sheet and story it to single db table column (single property of entity class).
Is there any better way to store table sheet data type presented in this example? Preserving all values in their own entity properties is not an option, because there will be plenty of table data in that form.
You should create an entity class with x , y and value properties.
x & y being unique combinations, representing the position in your matrix.
This entity does not have to be stored in your database and can only be used to generate the form.
Then create a form-collection which renders your fields in this matrix form.
Finally: use a DataTransformer to transform the collection of entities to an array as desired.
Now save the array in your database.
I have tried couple of solutions and this is best result by my opinion:
Step 1.
Creation of macro for generating tableSheet in separate twig file (for later usage in other forms).
{# in Default:form.html.twig #}
{% macro tablesheet(name, rows, colums, titles) %}
... titles header ...
{% for i in 1..rows %}
{% for j in 1..colums %}
<input name="{{ name }}_{{ i }}{{ j }}" type="text" class="flat-form" />
{% endfor %}
{% endfor %}
{% endmacro %}
Step 2.
Import macro and create tables of input fields, in add/edit template :
{% import "ProjectSomeBundle:Default:form.html.twig" as forms %}
...
{{ form_widget(form) }}
{{ forms.tablesheet('table1', 4, 3, {0:'col1', 1:'col2', 2:'col3'}) }}
{{ forms.tablesheet('table2', 7, 3, {0:'col1', 1:'col2', 2:'col3'}) }}
...
</form>
Step 3.
Create jQuery function to serialize/deserialize form (input elements with flat-form class attr in this case). For serilize I used something like this http://css-tricks.com/snippets/jquery/serialize-form-to-json/ . Serialized values are stored in single entity filed as string and saved to db like that.
How it's works :
on opening edit form, jQuery deserialized data from that entity field and loaded to tablesheet input fields. Before sending it back (post) to server values are collect and put in that same field via serialized function.

Resources