analitics

Pages

Saturday, August 24, 2019

Python 3.7.3 : Using the flask - part 015.

In this tutorial, I will show you how to migrate using the Database Migrations in flask project.
Because my laptop is gone I use my old Linux.
First you need to install these python modules with --user argument for Linux:
[mythcat@desk my_flask]$ pip3 install flask-migrate --user
...
[mythcat@desk my_flask]$ pip3 install flask-script --user
Let's test this new issue with server.py file by adding these python modules:
#migrate 
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
...
# create migrate object with db 
migrate = Migrate(app, db)
# create manager 
manager = Manager(app)
# create db command for manager 
manager.add_command('db', MigrateCommand)
...
# add new columns into database 
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)
    gender = db.Column(db.String(5), unique=True)
    work = db.Column(db.String(33), unique=True)
    city = db.Column(db.String(15), unique=True)
...
# the default name main
if __name__ == '__main__':
    manager.run()
    app.run(debug=True)
Let's fix this migrate issue with the new command:
[mythcat@desk my_flask]$ python3 server.py db init 
  Creating directory /home/mythcat/project_github/my_flask/migrations ... done
  Creating directory /home/mythcat/project_github/my_flask/migrations/versions ... done
  Generating /home/mythcat/project_github/my_flask/migrations/script.py.mako ... done
  Generating /home/mythcat/project_github/my_flask/migrations/env.py ... done
  Generating /home/mythcat/project_github/my_flask/migrations/alembic.ini ... done
  Generating /home/mythcat/project_github/my_flask/migrations/README ... done
  Please edit configuration/connection/logging settings in '/home/mythcat/project_github/my_flask/migrations
/alembic.ini'
  before proceeding.

[mythcat@desk my_flask]$ python3 server.py db migrate
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added column 'user.city'
INFO  [alembic.autogenerate.compare] Detected added column 'user.gender'
INFO  [alembic.autogenerate.compare] Detected added column 'user.work'
INFO  [alembic.autogenerate.compare] Detected added unique constraint 'None' on '['city']'
INFO  [alembic.autogenerate.compare] Detected added unique constraint 'None' on '['gender']'
INFO  [alembic.autogenerate.compare] Detected added unique constraint 'None' on '['work']'
  Generating /home/mythcat/project_github/my_flask/migrations/versions/ca70c42b5b7a_.py ... done

[mythcat@desk my_flask]$ python3 server.py db upgrade 
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> ca70c42b5b7a, empty message
ERROR [root] Error: No support for ALTER of constraints in SQLite dialect
The database fiels is changed by this command.
Let's see with sqlite3 tool:
[mythcat@desk my_flask]$ sqlite3 server.sqlite 
SQLite version 3.26.0 2018-12-01 12:34:55
Enter ".help" for usage hints.
sqlite> .tables
alembic_version  user           
sqlite> .schema user
CREATE TABLE user (
        id INTEGER NOT NULL, 
        username VARCHAR(80), 
        email VARCHAR(120), city VARCHAR(15), gender VARCHAR(5), work VARCHAR(33), 
        PRIMARY KEY (id), 
        UNIQUE (username), 
        UNIQUE (email)
);
You can see my source code here.

Thursday, August 22, 2019

Python 3.7.3 : Using the inotify.

About this tool you can read here:
inotify functionality is available from the Linux kernel and allows you to register one or more directories for watching, and to simply block and wait for notification events.
[mythcat@desk ~]$ pip3 install inotify --user
...
Successfully installed inotify-0.2.10 nose-1.3.7
Let's test it and see how this can be load it:
[mythcat@desk ~]$ python3
Python 3.7.4 (default, Jul  9 2019, 16:32:37) 
[GCC 9.1.1 20190503 (Red Hat 9.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import inotify
>>> from inotify import *
>>> print(dir(inotify))
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', 
'__path__', '__spec__', '__version__']
>>> import inotify.adapters
>>> print(dir(inotify))
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', 
'__path__', '__spec__', '__version__', 'adapters', 'calls', 'constants', 'library']
>>> print(dir(inotify.adapters))
['EINTR', 'EventTimeoutException', 'Inotify', 'InotifyTree', 'InotifyTrees', 'TerminalEventException', 
'_BaseTree', '_DEFAULT_EPOLL_BLOCK_DURATION_S', '_DEFAULT_TERMINAL_EVENTS', '_HEADER_STRUCT_FORMAT', 
'_INOTIFY_EVENT', '_IS_DEBUG', '_LOGGER', '_STRUCT_HEADER_LENGTH', '__builtins__', '__cached__', '__doc__',
 '__file__', '__loader__', '__name__', '__package__', '__spec__', 'collections', 'inotify', 'logging', 'os',
 'select', 'struct', 'time']
>>> import inotify.calls
>>> print(dir(inotify.calls))
['InotifyError', '_LIB', '_LOGGER', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', 
'__name__', '__package__', '__spec__', '_check_nonnegative', '_check_nonzero', '_check_zero', 'ctypes', 
'errno', 'inotify', 'inotify_add_watch', 'inotify_init', 'inotify_rm_watch', 'logging']
>>> import inotify.constants
>>> print(dir(inotify.constants))
['IN_ACCESS', 'IN_ALL_EVENTS', 'IN_ATTRIB', 'IN_CLOEXEC', 'IN_CLOSE', 'IN_CLOSE_NOWRITE', 'IN_CLOSE_WRITE', 
'IN_CREATE', 'IN_DELETE', 'IN_DELETE_SELF', 'IN_DONT_FOLLOW', 'IN_IGNORED', 'IN_ISDIR', 'IN_MASK_ADD', 
'IN_MODIFY', 'IN_MOVE', 'IN_MOVED_FROM', 'IN_MOVED_TO', 'IN_MOVE_SELF', 'IN_NONBLOCK', 'IN_ONESHOT', 
'IN_ONLYDIR', 'IN_OPEN', 'IN_Q_OVERFLOW', 'IN_UNMOUNT', 'MASK_LOOKUP', '__builtins__', '__cached__', 
'__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
>>> import inotify.library
>>> print(dir(inotify.library))
['_FILEPATH', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', 
'__spec__',
 'ctypes', 'instance']
Let's test it with a simple watch issue for a file named test_file.
First, let create this file:
[mythcat@desk ~]$ touch /tmp/test_file
[mythcat@desk ~]$ ll /tmp/test_file 
-rw-rw-r--. 1 mythcat mythcat 0 Aug 22 13:54 /tmp/test_file
Now, I can use the next script to watch on it:
import inotify.adapters
def notif_tmp():
    adapter_tmp = inotify.adapters.Inotify()

    adapter_tmp.add_watch('/tmp')

    with open('/tmp/test_file', 'w'):
        pass

    events = adapter_tmp.event_gen(yield_nones=False, timeout_s=1)
    events = list(events)

    print(events)
if __name__ == '__main__':
    notif_tmp()
The result is this:
$ python3 notif.py 
[(_INOTIFY_EVENT(wd=1, mask=2, cookie=0, len=16), ['IN_MODIFY'], '/tmp', 'test_file'), 
(_INOTIFY_EVENT(wd=1, mask=32, cookie=0, len=16), ['IN_OPEN'], '/tmp', 'test_file'), 
(_INOTIFY_EVENT(wd=1, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], '/tmp', 'test_file')]
If remove this file the result will be this:
$ python3 notif.py 
[(_INOTIFY_EVENT(wd=1, mask=256, cookie=0, len=16), ['IN_CREATE'], '/tmp', 'test_file'), 
(_INOTIFY_EVENT(wd=1, mask=32, cookie=0, len=16), ['IN_OPEN'], '/tmp', 'test_file'), 
(_INOTIFY_EVENT(wd=1, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], '/tmp', 'test_file')]
Let's test another example:
import inotify.adapters
import logging
_LOGGER = logging.getLogger(__name__)
def test():
    i = inotify.adapters.Inotify()
    i.add_watch('/tmp')

    try:
        for event in i.event_gen():
            if event is not None:
                (header, type_names, path, filename) = event
                _LOGGER.info("WD=(%d) MASK=(%d) COOKIE=(%d) LEN=(%d) MASK->NAMES=%s "
                             "FILENAME=[%s]",
                             header.wd, header.mask, header.cookie, header.len, type_names,
                             filename)
                print(header, type_names, path, filename)
    finally:
        i.remove_watch('/tmp')

if __name__ == '__main__':
    test()
If you run this and will try to edit with vim editor the /tmp/text.txt file the output will be this:
$ python3 notif_002.py
_INOTIFY_EVENT(wd=1, mask=32, cookie=0, len=16) ['IN_OPEN'] /tmp text.txt
_INOTIFY_EVENT(wd=1, mask=256, cookie=0, len=16) ['IN_CREATE'] /tmp .text.txt.swp
_INOTIFY_EVENT(wd=1, mask=32, cookie=0, len=16) ['IN_OPEN'] /tmp .text.txt.swp
...
If you want to use many paths then use this:
    paths = [
        '/tmp',
    ]
    i = Inotify(paths=paths)
This module is not well documented but can be successfully used for certain tasks.

Wednesday, August 21, 2019

Python Qt5 : contextMenu example.

A context menu is a menu in a graphical user interface (GUI) that appears upon user interaction, such as a right-click mouse operation.
I create the default application and I use QMenu to create this context menu with New, Open and Quit.
from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu
import sys


class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.title = "PyQt5 Context Menu"
        self.top = 100
        self.left = 100
        self.width = 640
        self.height = 480
        self.InitWindow()


    def InitWindow(self):
        self.setWindowIcon(QtGui.QIcon("icon.png"))
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.show()

    def contextMenuEvent(self, event):
        contextMenu = QMenu(self)
        new_Act = contextMenu.addAction("New")
        open_Act = contextMenu.addAction("Open")
        quit_Act = contextMenu.addAction("Quit")
        action = contextMenu.exec_(self.mapToGlobal(event.pos()))
        if action == quit_Act:
            self.close()


App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())

Tuesday, August 20, 2019

Python Qt5 : the QTimer class.

I haven't written about PyQt5 in a while and today I decided to add a short tutorial on this python module.
The QTimer class is a high-level programming interface for timers and provides repetitive and single-shot timers.
I this example I call a method every second with these lines:
        self.timer = QTimer()
        self.timer.timeout.connect(self.handleTimer)
        self.timer.start(1000)
The timer is stop when the value check by handleTimer has value 100 else the ProgressBar increment the default value with 1.
The full source of code is this:
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QMainWindow, QProgressBar
from PyQt5.QtCore import Qt

class QTimer_ProgressBar(QMainWindow):

    def __init__(self):
        super().__init__()

        self.pbar = QProgressBar(self)
        self.pbar.setGeometry(30, 70,400, 50)
        self.pbar.setValue(0)

        self.setWindowTitle("QTimer Progressbar")
        self.setGeometry(64,64,640,480)
        self.show()

        self.timer = QTimer()
        self.timer.timeout.connect(self.handleTimer)
        self.timer.start(1000)

    def handleTimer(self):
        value = self.pbar.value()
        if value < 100:
            value = value + 1
            self.pbar.setValue(value)
        else:
            self.timer.stop()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = QTimer_ProgressBar()
    sys.exit(app.exec_())
You can also use it like this:
def Qt():
    try:
        # Do things
    finally:
        QTimer.singleShot(5000, Qt)

Qt()

Wednesday, August 14, 2019

Python 3.7.3 : Using the flask - part 014.

Today I worked on YouTube search with flask and Google A.P.I. project.
The source code is simple to understand and you can test any A.P.I. from google using this way.
I created a new Google project with YouTube A.P.I. version 3 and with the A.P.I. key.
I use this key to connect with flask python module.
I used the isodate python module.
You can see the source code on my GitHub repo named flask_yt.
The result is this:

Friday, August 9, 2019

Python 3.7.3 : Using the flask - part 013.

Flask uses Jinga2 template engine.
The Jinga2 template engine uses the following delimiters for escaping from HTML.
We can use this:
  • {% ... %} for Statements
  • {{ ... }} for Expressions to print to the template output
  • {# ... #} for Comments not included in the template output
  • # ... ## for Line Statements
The documentation webpage comes with all information about how can be used.
I create a new HTML5 file named layout.html and I will use this like an example:
{% if current_user.is_authenticated %}
...
{% else %}
...
{% endif %}
This file can be add into another file like this:
{% extends "layout.html" %}
{% block content %}
...
{% endblock content %}
For example, my about.html webpage comes with this source code:
{% extends "layout.html" %}
{% block content %}
    

About Page

{% endblock content %}
The routes.py this webpage will have this call:
@app.route("/about")
def about():
    return render_template('about.html', title='About')
In this way, I will add more HTML5 files to the project.


Thursday, August 8, 2019

Python 3.7.3 : Using the flask - part 012.

The goal of this tutorial step is to understand how the project can use the new features and implementation of the project.
Because in the last tutorial I used the flask_mail python module, now I will add into my project structure.
One good issue is registration issue for users.
First, you need to see the full project and changes at my GitHub project.
I used the itsdangerous python module to use tokens.
Let's install this with the pip tool:
C:\Python373\Scripts>pip install itsdangerous
For registration is need to have login issues and we can use LoginManager, see the extensions.py
#use SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
#use LoginManager
from flask_login import LoginManager
#create login_manager
login_manager = LoginManager()
#create db
db = SQLAlchemy()
Let's see the new User model, see the models.py:
#imports for user model
from datetime import datetime
from .extensions import db
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from blue_test import login_manager
from flask_login import UserMixin

# import db from base folder, see dot
from .extensions import db 

#load the user by id
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

# create the Texts mode
class Texts(db.Model):
    # primary key 
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(50))
    txt_content = db.Column(db.String(1000))
    
# create the User model
'''
id = id key for user
username = name
email = user email
password = user password

'''
class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(60), nullable=False)

    def get_reset_token(self, expires_sec=1800):
        s = Serializer(app.config['SECRET_KEY'], expires_sec)
        return s.dumps({'user_id': self.id}).decode('utf-8')

    @staticmethod
    def verify_reset_token(token):
        s = Serializer(app.config['SECRET_KEY'])
        try:
            user_id = s.loads(token)['user_id']
        except:
            return None
        return User.query.get(user_id)

    def __repr__(self):
        return f"User('{self.username}', '{self.email}')"
In the blue_test folder project I created the templates folder with two HTML5 files: home.html and register.html.
I created also the forms.py file and I update with my forms: RegistrationForm,RequestResetForm, ResetPasswordForm.
#import python for create forms 
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
# import python for validate forms field
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
#import python files project
from .extensions import db, login_manager

class RegistrationForm(FlaskForm):
    username = StringField('Username',
                           validators=[DataRequired(), Length(min=2, max=25)])
    email = StringField('Email',
                        validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    confirm_password = PasswordField('Confirm Password',
                                     validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Sign Up')

    def validate_username(self, username):
        user = User.query.filter_by(username=username.data).first()
        if user:
            raise ValidationError('That username is taken.')

    def validate_email(self, email):
        user = User.query.filter_by(email=email.data).first()
        if user:
            raise ValidationError('That email is taken.')

class RequestResetForm(FlaskForm):
    email = StringField('Email',
                        validators=[DataRequired(), Email()])
    submit = SubmitField('Password Reset')

    def validate_email(self, email):
        #get user by email
        
        if user is None:
            raise ValidationError('There is no account with that email.')

class ResetPasswordForm(FlaskForm):
    password = PasswordField('Password', validators=[DataRequired()])
    confirm_password = PasswordField('Confirm Password',
                                     validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Reset Password')
In the __init__.py file I add this:
#import python files project
from .extensions import db, login_manager
...
    login_manager.init_app(app)
The routers.py will have the new RegistrationForm:
@api.route("/register", methods=['GET', 'POST'])
def register():
    # check if the user is authenticated
    if current_user.is_authenticated:
        return redirect(url_for('home'))
    form = RegistrationForm()
    if form.validate_on_submit():
        flash('Your account has been created!, 'success')
        return redirect(url_for('login'))
    return render_template('register.html', title='Register', form=form)
Depending on your needs, we will send and redirect these forms to the HTML files.
Depending on your needs (users, page access, ...) we will be able to write the source code further.

Wednesday, August 7, 2019

Python 3.7.3 : Using the flask - part 011.

The tutorial for today is focused on the email issue.
I will start with the new python module for flask named flask_mail.
Let's install it:
C:\Python373>cd Scripts

C:\Python373\Scripts>pip3 install flask_mail
Collecting flask_mail
...
Installing collected packages: blinker, flask-mail
Successfully installed blinker-1.4 flask-mail-0.9.1
The next source code let show you how can use this python module.
from flask import Flask 
from flask_mail import Mail, Message

app = Flask(__name__)
app.config['DEBUG'] = True
app.config['TESTING'] = False
app.config['MAIL_SERVER'] = 'smtp...'
app.config['MAIL_PORT'] = 25
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = False
#app.config['MAIL_DEBUG'] = True # debug 
app.config['MAIL_USERNAME'] = None
app.config['MAIL_PASSWORD'] = None
# this will send with the name of another@mail.com
#app.config['MAIL_DEFALT_SENDER'] = ('Another mail', 'another@mail.com')
app.config['MAIL_DEFALT_SENDER'] = None
app.config['MAIL_MAX_EMAILS'] = None # limit the messages send
#app.config['MAIL_SUPRESS_SEND'] = False # testing
app.config['MAIL_ASCII_ATTACHMENTS'] = False

# send an email with the flask application
mail = Mail(app)

@app.route('/mail')
def mail():
    #msg = Message('Hey', sender='another@mail.com')
    #msg = Message('Hey', recipients=['catafest@yahoo.com', 'another@mail.com'])
    #msg.add_recipient = ('another@mail.com')
    msg = Message('Hey', recipients=['catafest@yahoo.com'])

    # you can use body or HTML, using both wills receive the HTML first
    #msg.body = 'This is a body text message!'
    msg.html = 'This is a body text message with HTML5 tags!'

    #add a file attachment to message 
    with app.open_resource('photo.jpg') as add_file_res:
         msg.attach('photo.jpg', 'image/jpeg', add_file_res.read())
    '''
    #create a template message 
    msg = Message(
    subject ='',
    recipients=[],
    body = '',
    html = '',
    sender = '',
    cc = [],
    bcc = [],
    attachments = [],
    reply_to = [],
    date = '',
    charset = [],
    extra_headers = {'':''},
    mail_options = [],
    rcpt_options = []
    )
    '''
    #send mail
    mail.send(msg)
    return 'Message sent!'

@app.route('/bulk')
def bulk():
    users = [{'name':'Me', 'email':'another@mail.com'}]
    # open an connection 
    with mail.connect() as con:
         for user in users:
             msg = Message('Bulk message!', recipients=[user['email']]
             msg.body = 'Body message!'
             con.send(msg)
'''
# another example 
@app.route('/bulk')
def bulk():
    users = [{'name':'Me', 'email':'another@mail.com'}]
    # open an connection 
    with mail.connect() as con:
         for user in users:
             msg = Message('Bulk message!', recipients=[user.email]
             msg.body = 'Body message!'
             con.send(msg)
'''
if __name__ == '__main__':
    app.run()
You can see you need to set the settings for your mail server and then use it into flaks application.
The source code is easy to understand if you follow the commented rows.
I add also put into comments alternative examples for the template message and the bulk function.
This is the first step into sending emails with flask.
We can have a complete implementation on the project but all depends on project structure.

Tuesday, August 6, 2019

Python 3.7.3 : Using the flask - part 010.

If you read my last tutorial about flask then you understand how to use the structure flask project with views.py and models.py.
If you run it and open the browser with http://127.0.0.1:5000/texts/ the result will be this:
{"texts":[{"title":"first title","txt_content":"this is first content"},{"title":null,"txt_content":null}]}
Let's create a file .env into the base folder named my_flask and add this source code:
SECRET_KEY='secret key'
DEBUG=True
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_DATABASE_URI = sqlite:///texts.sqlite
Let's create a settings.py file into blue_test folder to get these settings:
import os
from os import environ
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
My blue_test project comes with the old views.py and a new routes.py file with this source code:
from flask import Blueprint, jsonify, request

api = Blueprint('api', __name__)

@api.route('/')
def home():
    return jsonify({'result' : 'You are in main page!'}) 
I create an extensions.py python script to deal with the database, see the source code:
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
With this new python file will solve the avoid circular importing of circular dependency of importing db.
This changes can be made on models.py and views.py, like this:
# import db from base folder, see dot
from .extensions import db 
This two files views.py and routes.py come with two Blueprint's: main and api.
Into the __init__.py will need to import and register both :
from .extensions import db
from .models import Texts
from .routes import api
from .views import main
...
    app.register_blueprint(main)
    app.register_blueprint(api)
If you run it into my_flask folder with:
C:\Python373\my_flask>set FLASK_APP=blue_test

C:\Python373\my_flask>flask run
 * Serving Flask app "blue_test"
 * Environment: production
...
The result into the browser area with http://127.0.0.1:5000/ for the routes.py blueprint api will be:
{"result":"You are in main page!"}
The result into the browser area with http://127.0.0.1:5000/texts/ for the views.py blueprint main will be:
{"texts":[{"title":"first title","txt_content":"this is first content"},{"title":null,"txt_content":null}]}
This shows you how to link multiple blueprints into one project.
You can see the full project at my GitHub project.

Monday, August 5, 2019

Python 3.7.3 : Using the flask - part 009.

In this tutorial, I will show you how to use blueprints with a new type of flask project using multiple python files.
I show you just how to create the A.P.I. not the REACT front end.
Flask uses a concept of blueprints for making application components and supporting common patterns within an application or across applications. Blueprints can greatly simplify how large applications work and provide a central means for Flask extensions to register operations on applications. A Blueprint object works similarly to a Flask application object, but it is not actually an application. Rather it is a blueprint of how to construct or extend an application., see the official webpage.
In my my_flask folder, I create a blue_test folder with three python files: __init__.py, views.py, and models.py.
The __init__.py file has this source code:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

# create path for SQLAlchemy
import os
basedir = os.path.abspath(os.path.dirname(__file__))

db = SQLAlchemy()

def create_app():
    app = Flask(__name__)
    # instantiate config for SQLAlchemy
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'texts.sqlite')
    # init db 
    db.init_app(app)
    # avoid circular importing of circular dependency 
    from .views import main
    app.register_blueprint(main)
    return app
The views.py come with this source code:
from flask import Blueprint
from flask import jsonify, request
from . import db
from .models import Texts

main = Blueprint('main', __name__)
@main.route('/add_text', methods = ['POST'])
def add_text():
    txt_data = request.get_json()
    new_txt = Texts(title=txt_data['title'], txt_content=txt_data['txt_content'])
    db.session(new_txt)
    db.session.commit()
    # 201 status code for create successfully
    return 'Done', 201
@main.route('/texts/')
def texts():
    txt_list=Texts.query.all()
    texts=[]
    for txt in txt_list:
        texts.append({'title': txt.title, 'txt_content':txt.txt_content})
    return jsonify ({'texts':texts})
The models.py has this source code:
# import db from base folder, see dot
from . import db 

class Texts(db.Model):
    # primary key 
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(50))
    txt_content = db.Column(db.String(1000))
Use the base folder of the blue_test folder to instantiate the database named texts.sqlite with a table texts.
C:\Python373>cd my_flask

C:\Python373\my_flask>python
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD6
4)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from blue_test.models import Texts
>>> from blue_test import db, create_app
>>> db.create_all(app=create_app())
>>> exit()
If not run into the base folder then you get this error:
    from . import db
ImportError: attempted relative import with no known parent package
Now you will have a texts.sqlite file for the database.
Use this command to set FLASK_APP:
C:\Python373\my_flask>set FLASK_APP=blue_test
C:\Python373\my_flask>flask run
Now, using the postman you can test it by running the python script and call these methods.



Sunday, August 4, 2019

Python 3.7.3 : Using the flask - part 008.

The tutorial for today will show you how to understand the flash method and fix exceptions.
First, the Flask module contains a flash method which passes a message to the next request, which generally is a template.
This lets you create feedback to users of a web application is critical, from notifications and error messages to warnings and progress alerts.
This system allows us to record a message at any point within a request, then display it at the start of the next request (and only the next request), see the documentation.
You need to import the flash with:
from flask import flash
The flash function takes up to 2 arguments, a message and a category like this: flash("message", "category").
In the next example, the random range output named out will send a flash message by category.
The flash warning category will send the number get by the random and will be into a range of 1 and 3.
Flashed messages are stored in the session until they are read it.
The HTML5 page named home.html can be found at my GitHub project.
This source code will send one message by flash and show two messages into a webpage.
@app.route('/',methods = ['GET','POST'])
def home():
    # test flash message
    out = random.randint(1,10)
    if out in range(1,3):
        flash(str(out),"warning" )
    if out in range(4,6):
        flash("This is a flash test for home.html with result:","success")
    if out in range(7,10):
        flash("This is a flash test for home.html with result:","danger")  
    return render_template("home.html")
The next example shows you how to use flash and exceptions to create an output error with render_template_string.
The output exception sends by flash without an HTML5 page request.
from flask import render_template_string
...
# fix Exception error , like 404
@app.errorhandler(Exception)
def page_not_found(e):
    flash(e, type(e))  
    return render_template_string('''
      {% with messages = get_flashed_messages(with_categories=true) %}
        {% if messages %}
          {% set printed_messages = dict() %}
          {% for category, message in messages %}
            {% if message not in printed_messages %}
              
{{message}}
{% set x = printed_messages.__setitem__(message, "value") %} {% endif %} {% endfor %} {% endif %} {% endwith %} ''')
If you put a bad path URL intro the server ( http://127.0.0.1:5000/bad ) then you get the result of the exception 404:
404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
Using the flash you can create your own notification system.

Saturday, August 3, 2019

Python 3.7.3 : Using the flask - part 007.

This will be a long tutorial because will try to link some information's from the last tutorials.
First, the structure of the project can see into my GitHub project.
I create new templates and another python script named tserv.py for testing.
You can see easier how the POST method works and how to deal with a python database issue.
This script comes with an upload file feature and upload database with sqlite3 python module.
First, for upload a file we need the HTML5 file from templates folder named upload.html.
I update the base.html file to use the bootstrap framework.
import sqlite3
class UploadForm(FlaskForm):
    file = FileField()
    submit = SubmitField("submit")
    
@app.route('/upload',methods = ['GET','POST'])
def upload():
    form = UploadForm()
    
    if request.method == "POST" and form.validate():
        if form.validate_on_submit():
            file_name = form.file.data
            file_database(name = file_name.filename,data = file_name.read())
            print("File {}".format(file_name.filename))
            return render_template("upload.html", form = form)
    return render_template("upload.html", form = form)
def file_database(name,data):
    con=sqlite3.connect("file_upload.db")
    cursor = con.cursor()
    cursor.execute("""CREATE TABLE IF NOT EXISTS my_table (name TEXT, data BLOP) """)
    cursor.execute("""INSERT INTO my_table (name , data ) VALUES (?,?) """, (name, data))
    con.commit()
    cursor.close()
    con.close()
Let's run it:
python tserv.py
Into the main folder a database will be create and will fill with the file uploaded.
Now, if you want to add more we can create a download button into our UploadForm, is no need to have another python class.
First create your form tag into upload.html file and link with the python tserv.py code show bellow:
from flask import send_file
from io import BytesIO
...
@app.route('/download', methods=['GET','POST'])
def download():
    form = UploadForm()
    if request.method == "POST":
        con = sqlite3.connect("file_upload.db")
        cursor = con.cursor()
        cur_ex = cursor.execute(""" SELECT * FROM my_table """)
        for i in cur_ex.fetchall():
            name=i[0]
            data=i[1]
            break
        con.commit()
        cursor.close()
        con.close()
        return send_file(BytesIO(data), attachment_filename='test', as_attachment=True)
    return render_template("home.html", form = form)
When you run it the download button will download the first file upload into database.
This example tutorial can be more complex.
For example, you can show the database content into a new HTML file and then save with an open dialog to disk.

Friday, August 2, 2019

Python 3.7.3 : Using the flask - part 006.

Today I will show you how to use the RESTful API application with flask python module that uses HTTP requests to GET, PUT, POST and DELETE data.
When HTTP is used, as is most common, the operations (HTTP methods) available are GET, HEAD, POST, PUT, PATCH, DELETE, CONNECT, OPTIONS and TRACE.[2], see the Wikipedia article.
All of these HTTP methods will be tested with postman software.
You need to have an account and use the downloaded software in order to interrogate with these methods.
Let's see the source new code first:
@app.route("/users/", methods=['GET'])
def users():
    users = User.query.all()
    #return users_schema.jsonify(users)
    all_users = users_schema.dump(users)
    return jsonify(all_users.data)

@app.route("/users/", methods=['POST'])
def user_post(id):
    user_post = User.query.get(id)
    print(user_post)
    username = request.json['username']
    email = request.json['email']
    user_post.username = username
    user_post.email = email
    db.session.commit()
    return user_schema.jsonify(user_post)

@app.route("/users/", methods=['PUT'])
def user_put(id):
    user_put = User.query.get(id)
    print(user_put)
    username = request.json['username']
    email = request.json['email']
    user_put.username = username
    user_put.email = email
    db.session.commit()
    return user_schema.jsonify(user_put)

@app.route("/users/", methods=['DELETE'])
def user_delete(id):
    user_delete = User.query.get(id)
    print(user_delete)
    db.session.delete(user_delete)
    db.session.commit()
    return user_schema.jsonify(user_delete)
Using the postman you can test it by running the python script and call these methods at http://127.0.0.1:5000/users/.