Database and SQLAlchemy

In this blog we will explore using programs with data, focused on Databases. We will use SQLite Database to learn more about using Programs with Data.

  • College Board talks about ideas like

    • Program Usage. "iterative and interactive way when processing information"
    • Managing Data. "classifying data are part of the process in using programs", "data files in a Table"
    • Insight "insight and knowledge can be obtained from ... digitally represented information"
    • Filter systems. 'tools for finding information and recognizing patterns"
    • Application. "the preserve has two databases", "an employee wants to count the number of book"
  • PBL, Databases, Iterative/OOP

    • Iterative. Refers to a sequence of instructions or code being repeated until a specific end result is achieved
    • OOP. A computer programming model that organizes software design around data, or objects, rather than functions and logic
    • SQL. Structured Query Language, abbreviated as SQL, is a language used in programming, managing, and structuring data

Imports and Flask Objects

Defines and key object creations

  • Comment on where you have observed these working?
  1. Flask app object
  2. SQLAlchemy object
"""
These imports define the key objects
"""

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

"""
These object and definitions are used throughout the Jupyter Notebook.
"""

# Setup of key Flask object (app)
app = Flask(__name__)
# Setup SQLAlchemy object and properties for the database (db)
database = 'sqlite:///files/sqlite.db'  # path and filename of database
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = database
app.config['SECRET_KEY'] = 'SECRET_KEY'
db = SQLAlchemy()


# This belongs in place where it runs once per project
db.init_app(app)

Model Definition

Define columns, initialization, and CRUD methods for users table in sqlite.db

  • Comment on these items in the class
  • class User purpose
  • db.Model inheritance
  • init method
  • @property, @.setter</li>
  • additional methods
  • </ul> </div> </div> </div>
    """ database dependencies to support sqlite examples """
    import datetime
    from datetime import datetime
    import json
    
    from sqlalchemy.exc import IntegrityError
    from werkzeug.security import generate_password_hash, check_password_hash
    
    
    ''' Tutorial: https://www.sqlalchemy.org/library.html#tutorials, try to get into a Python shell and follow along '''
    
    # Define the User class to manage actions in the 'users' table
    # -- Object Relational Mapping (ORM) is the key concept of SQLAlchemy
    # -- a.) db.Model is like an inner layer of the onion in ORM
    # -- b.) User represents data we want to store, something that is built on db.Model
    # -- c.) SQLAlchemy ORM is layer on top of SQLAlchemy Core, then SQLAlchemy engine, SQL
    class User(db.Model):
        __tablename__ = 'users'  # table name is plural, class name is singular
    
        # Define the User schema with "vars" from object
        id = db.Column(db.Integer, primary_key=True)
        _name = db.Column(db.String(255), unique=False, nullable=False)
        _uid = db.Column(db.String(255), unique=True, nullable=False)
        _password = db.Column(db.String(255), unique=False, nullable=False)
        _dob = db.Column(db.Date)
    
        # constructor of a User object, initializes the instance variables within object (self)
        def __init__(self, name, uid, password="123qwerty", dob=datetime.today()):
            self._name = name    # variables with self prefix become part of the object, 
            self._uid = uid
            self.set_password(password)
            if isinstance(dob, str):  # not a date type     
                dob = date=datetime.today()
            self._dob = dob
    
        # a name getter method, extracts name from object
        @property
        def name(self):
            return self._name
        
        # a setter function, allows name to be updated after initial object creation
        @name.setter
        def name(self, name):
            self._name = name
        
        # a getter method, extracts email from object
        @property
        def uid(self):
            return self._uid
        
        # a setter function, allows name to be updated after initial object creation
        @uid.setter
        def uid(self, uid):
            self._uid = uid
            
        # check if uid parameter matches user id in object, return boolean
        def is_uid(self, uid):
            return self._uid == uid
        
        @property
        def password(self):
            return self._password[0:10] + "..." # because of security only show 1st characters
    
        # update password, this is conventional setter
        def set_password(self, password):
            """Create a hashed password."""
            self._password = generate_password_hash(password, method='sha256')
    
        # check password parameter versus stored/encrypted password
        def is_password(self, password):
            """Check against hashed password."""
            result = check_password_hash(self._password, password)
            return result
        
        # dob property is returned as string, to avoid unfriendly outcomes
        @property
        def dob(self):
            dob_string = self._dob.strftime('%m-%d-%Y')
            return dob_string
        
        # dob should be have verification for type date
        @dob.setter
        def dob(self, dob):
            if isinstance(dob, str):  # not a date type     
                dob = date=datetime.today()
            self._dob = dob
        
        @property
        def age(self):
            today = datetime.today()
            return today.year - self._dob.year - ((today.month, today.day) < (self._dob.month, self._dob.day))
        
        # output content using str(object) in human readable form, uses getter
        # output content using json dumps, this is ready for API response
        def __str__(self):
            return json.dumps(self.read())
    
        # CRUD create/add a new record to the table
        # returns self or None on error
        def create(self):
            try:
                # creates a person object from User(db.Model) class, passes initializers
                db.session.add(self)  # add prepares to persist person object to Users table
                db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
                return self
            except IntegrityError:
                db.session.remove()
                return None
    
        # CRUD read converts self to dictionary
        # returns dictionary
        def read(self):
            return {
                "id": self.id,
                "name": self.name,
                "uid": self.uid,
                "dob": self.dob,
                "age": self.age,
            }
    
        # CRUD update: updates user name, password, phone
        # returns self
        def update(self, name="", uid="", password=""):
            """only updates values with length"""
            if len(name) > 0:
                self.name = name
            if len(uid) > 0:
                self.uid = uid
            if len(password) > 0:
                self.set_password(password)
            db.session.commit()
            return self
    
        # CRUD delete: remove self
        # None
        def delete(self):
            db.session.delete(self)
            db.session.commit()
            return None
    

    Initial Data

    Uses SQLALchemy db.create_all() to initialize rows into sqlite.db

    • Comment on how these work?
    1. Create All Tables from db Object
    2. User Object Constructors
    3. Try / Except
    """Database Creation and Testing """
    
    
    # Builds working data for testing
    def initUsers():
        with app.app_context():
            """Create database and tables"""
            db.create_all()
            """Tester data for table"""
            u1 = User(name='Thomas Edison', uid='toby', password='123toby', dob=date(1847, 2, 11))
            u2 = User(name='Nikola Tesla', uid='niko', password='123niko')
            u3 = User(name='Alexander Graham Bell', uid='lex', password='123lex')
            u4 = User(name='Eli Whitney', uid='whit', password='123whit')
            u5 = User(name='Indiana Jones', uid='indi', dob=datetime(1920, 10, 21))
            u6 = User(name='Marion Ravenwood', uid='raven', dob=datetime(1921, 10, 21))
    
    
            users = [u1, u2, u3, u4, u5, u6]
    
            """Builds sample user/note(s) data"""
            for user in users:
                try:
                    '''add user to table'''
                    object = user.create()
                    print(f"Created new uid {object.uid}")
                except:  # error raised if object nit created
                    '''fails with bad or duplicate data'''
                    print(f"Records exist uid {user.uid}, or error.")
                    
    initUsers()
    
    ---------------------------------------------------------------------------
    OperationalError                          Traceback (most recent call last)
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/base.py:145, in Connection.__init__(self, engine, connection, _has_events, _allow_revalidate, _allow_autobegin)
        144 try:
    --> 145     self._dbapi_connection = engine.raw_connection()
        146 except dialect.loaded_dbapi.Error as err:
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/base.py:3275, in Engine.raw_connection(self)
       3254 """Return a "raw" DBAPI connection from the connection pool.
       3255 
       3256 The returned object is a proxied version of the DBAPI
       (...)
       3273 
       3274 """
    -> 3275 return self.pool.connect()
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:455, in Pool.connect(self)
        448 """Return a DBAPI connection from the pool.
        449 
        450 The connection is instrumented such that when its
       (...)
        453 
        454 """
    --> 455 return _ConnectionFairy._checkout(self)
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:1271, in _ConnectionFairy._checkout(cls, pool, threadconns, fairy)
       1270 if not fairy:
    -> 1271     fairy = _ConnectionRecord.checkout(pool)
       1273     if threadconns is not None:
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:719, in _ConnectionRecord.checkout(cls, pool)
        718 else:
    --> 719     rec = pool._do_get()
        721 try:
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/impl.py:169, in QueuePool._do_get(self)
        168 with util.safe_reraise():
    --> 169     self._dec_overflow()
        170 raise
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/util/langhelpers.py:147, in safe_reraise.__exit__(self, type_, value, traceback)
        146     self._exc_info = None  # remove potential circular references
    --> 147     raise exc_value.with_traceback(exc_tb)
        148 else:
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/impl.py:166, in QueuePool._do_get(self)
        165 try:
    --> 166     return self._create_connection()
        167 except:
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:396, in Pool._create_connection(self)
        394 """Called by subclasses to create a new ConnectionRecord."""
    --> 396 return _ConnectionRecord(self)
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:681, in _ConnectionRecord.__init__(self, pool, connect)
        680 if connect:
    --> 681     self.__connect()
        682 self.finalize_callback = deque()
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:906, in _ConnectionRecord.__connect(self)
        905     with util.safe_reraise():
    --> 906         pool.logger.debug("Error on connect(): %s", e)
        907 else:
        908     # in SQLAlchemy 1.4 the first_connect event is not used by
        909     # the engine, so this will usually not be set
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/util/langhelpers.py:147, in safe_reraise.__exit__(self, type_, value, traceback)
        146     self._exc_info = None  # remove potential circular references
    --> 147     raise exc_value.with_traceback(exc_tb)
        148 else:
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:901, in _ConnectionRecord.__connect(self)
        900 self.starttime = time.time()
    --> 901 self.dbapi_connection = connection = pool._invoke_creator(self)
        902 pool.logger.debug("Created new connection %r", connection)
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/create.py:641, in create_engine.<locals>.connect(connection_record)
        639             return connection
    --> 641 return dialect.connect(*cargs, **cparams)
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/default.py:580, in DefaultDialect.connect(self, *cargs, **cparams)
        578 def connect(self, *cargs, **cparams):
        579     # inherits the docstring from interfaces.Dialect.connect
    --> 580     return self.loaded_dbapi.connect(*cargs, **cparams)
    
    OperationalError: unable to open database file
    
    The above exception was the direct cause of the following exception:
    
    OperationalError                          Traceback (most recent call last)
    /Users/hbadkoobehi/Documents/GitHub/yasha-fastpages/_notebooks/2023-03-13-AP-unit2-4a.ipynb Cell 8 in <cell line: 30>()
         <a href='vscode-notebook-cell:/Users/hbadkoobehi/Documents/GitHub/yasha-fastpages/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X10sZmlsZQ%3D%3D?line=26'>27</a>                 '''fails with bad or duplicate data'''
         <a href='vscode-notebook-cell:/Users/hbadkoobehi/Documents/GitHub/yasha-fastpages/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X10sZmlsZQ%3D%3D?line=27'>28</a>                 print(f"Records exist uid {user.uid}, or error.")
    ---> <a href='vscode-notebook-cell:/Users/hbadkoobehi/Documents/GitHub/yasha-fastpages/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X10sZmlsZQ%3D%3D?line=29'>30</a> initUsers()
    
    /Users/hbadkoobehi/Documents/GitHub/yasha-fastpages/_notebooks/2023-03-13-AP-unit2-4a.ipynb Cell 8 in initUsers()
          <a href='vscode-notebook-cell:/Users/hbadkoobehi/Documents/GitHub/yasha-fastpages/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X10sZmlsZQ%3D%3D?line=5'>6</a> with app.app_context():
          <a href='vscode-notebook-cell:/Users/hbadkoobehi/Documents/GitHub/yasha-fastpages/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X10sZmlsZQ%3D%3D?line=6'>7</a>     """Create database and tables"""
    ----> <a href='vscode-notebook-cell:/Users/hbadkoobehi/Documents/GitHub/yasha-fastpages/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X10sZmlsZQ%3D%3D?line=7'>8</a>     db.create_all()
          <a href='vscode-notebook-cell:/Users/hbadkoobehi/Documents/GitHub/yasha-fastpages/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X10sZmlsZQ%3D%3D?line=8'>9</a>     """Tester data for table"""
         <a href='vscode-notebook-cell:/Users/hbadkoobehi/Documents/GitHub/yasha-fastpages/_notebooks/2023-03-13-AP-unit2-4a.ipynb#X10sZmlsZQ%3D%3D?line=9'>10</a>     u1 = User(name='Thomas Edison', uid='toby', password='123toby', dob=date(1847, 2, 11))
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/flask_sqlalchemy/extension.py:884, in SQLAlchemy.create_all(self, bind_key)
        867 def create_all(self, bind_key: str | None | list[str | None] = "__all__") -> None:
        868     """Create tables that do not exist in the database by calling
        869     ``metadata.create_all()`` for all or some bind keys. This does not
        870     update existing tables, use a migration library for that.
       (...)
        882         Added the ``bind`` and ``app`` parameters.
        883     """
    --> 884     self._call_for_binds(bind_key, "create_all")
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/flask_sqlalchemy/extension.py:865, in SQLAlchemy._call_for_binds(self, bind_key, op_name)
        862     raise sa.exc.UnboundExecutionError(message) from None
        864 metadata = self.metadatas[key]
    --> 865 getattr(metadata, op_name)(bind=engine)
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/sql/schema.py:5581, in MetaData.create_all(self, bind, tables, checkfirst)
       5557 def create_all(
       5558     self,
       5559     bind: _CreateDropBind,
       5560     tables: Optional[_typing_Sequence[Table]] = None,
       5561     checkfirst: bool = True,
       5562 ) -> None:
       5563     """Create all tables stored in this metadata.
       5564 
       5565     Conditional by default, will not attempt to recreate tables already
       (...)
       5579 
       5580     """
    -> 5581     bind._run_ddl_visitor(
       5582         ddl.SchemaGenerator, self, checkfirst=checkfirst, tables=tables
       5583     )
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/base.py:3225, in Engine._run_ddl_visitor(self, visitorcallable, element, **kwargs)
       3219 def _run_ddl_visitor(
       3220     self,
       3221     visitorcallable: Type[Union[SchemaGenerator, SchemaDropper]],
       3222     element: SchemaItem,
       3223     **kwargs: Any,
       3224 ) -> None:
    -> 3225     with self.begin() as conn:
       3226         conn._run_ddl_visitor(visitorcallable, element, **kwargs)
    
    File ~/opt/anaconda3/lib/python3.9/contextlib.py:119, in _GeneratorContextManager.__enter__(self)
        117 del self.args, self.kwds, self.func
        118 try:
    --> 119     return next(self.gen)
        120 except StopIteration:
        121     raise RuntimeError("generator didn't yield") from None
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/base.py:3215, in Engine.begin(self)
       3188 @contextlib.contextmanager
       3189 def begin(self) -> Iterator[Connection]:
       3190     """Return a context manager delivering a :class:`_engine.Connection`
       3191     with a :class:`.Transaction` established.
       3192 
       (...)
       3213 
       3214     """
    -> 3215     with self.connect() as conn:
       3216         with conn.begin():
       3217             yield conn
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/base.py:3251, in Engine.connect(self)
       3228 def connect(self) -> Connection:
       3229     """Return a new :class:`_engine.Connection` object.
       3230 
       3231     The :class:`_engine.Connection` acts as a Python context manager, so
       (...)
       3248 
       3249     """
    -> 3251     return self._connection_cls(self)
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/base.py:147, in Connection.__init__(self, engine, connection, _has_events, _allow_revalidate, _allow_autobegin)
        145         self._dbapi_connection = engine.raw_connection()
        146     except dialect.loaded_dbapi.Error as err:
    --> 147         Connection._handle_dbapi_exception_noconnection(
        148             err, dialect, engine
        149         )
        150         raise
        151 else:
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/base.py:2413, in Connection._handle_dbapi_exception_noconnection(cls, e, dialect, engine, is_disconnect, invalidate_pool_on_disconnect, is_pre_ping)
       2411 elif should_wrap:
       2412     assert sqlalchemy_exception is not None
    -> 2413     raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
       2414 else:
       2415     assert exc_info[1] is not None
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/base.py:145, in Connection.__init__(self, engine, connection, _has_events, _allow_revalidate, _allow_autobegin)
        143 if connection is None:
        144     try:
    --> 145         self._dbapi_connection = engine.raw_connection()
        146     except dialect.loaded_dbapi.Error as err:
        147         Connection._handle_dbapi_exception_noconnection(
        148             err, dialect, engine
        149         )
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/base.py:3275, in Engine.raw_connection(self)
       3253 def raw_connection(self) -> PoolProxiedConnection:
       3254     """Return a "raw" DBAPI connection from the connection pool.
       3255 
       3256     The returned object is a proxied version of the DBAPI
       (...)
       3273 
       3274     """
    -> 3275     return self.pool.connect()
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:455, in Pool.connect(self)
        447 def connect(self) -> PoolProxiedConnection:
        448     """Return a DBAPI connection from the pool.
        449 
        450     The connection is instrumented such that when its
       (...)
        453 
        454     """
    --> 455     return _ConnectionFairy._checkout(self)
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:1271, in _ConnectionFairy._checkout(cls, pool, threadconns, fairy)
       1262 @classmethod
       1263 def _checkout(
       1264     cls,
       (...)
       1267     fairy: Optional[_ConnectionFairy] = None,
       1268 ) -> _ConnectionFairy:
       1270     if not fairy:
    -> 1271         fairy = _ConnectionRecord.checkout(pool)
       1273         if threadconns is not None:
       1274             threadconns.current = weakref.ref(fairy)
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:719, in _ConnectionRecord.checkout(cls, pool)
        717     rec = cast(_ConnectionRecord, pool._do_get())
        718 else:
    --> 719     rec = pool._do_get()
        721 try:
        722     dbapi_connection = rec.get_connection()
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/impl.py:169, in QueuePool._do_get(self)
        167     except:
        168         with util.safe_reraise():
    --> 169             self._dec_overflow()
        170         raise
        171 else:
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/util/langhelpers.py:147, in safe_reraise.__exit__(self, type_, value, traceback)
        145     assert exc_value is not None
        146     self._exc_info = None  # remove potential circular references
    --> 147     raise exc_value.with_traceback(exc_tb)
        148 else:
        149     self._exc_info = None  # remove potential circular references
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/impl.py:166, in QueuePool._do_get(self)
        164 if self._inc_overflow():
        165     try:
    --> 166         return self._create_connection()
        167     except:
        168         with util.safe_reraise():
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:396, in Pool._create_connection(self)
        393 def _create_connection(self) -> ConnectionPoolEntry:
        394     """Called by subclasses to create a new ConnectionRecord."""
    --> 396     return _ConnectionRecord(self)
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:681, in _ConnectionRecord.__init__(self, pool, connect)
        679 self.__pool = pool
        680 if connect:
    --> 681     self.__connect()
        682 self.finalize_callback = deque()
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:906, in _ConnectionRecord.__connect(self)
        904 except BaseException as e:
        905     with util.safe_reraise():
    --> 906         pool.logger.debug("Error on connect(): %s", e)
        907 else:
        908     # in SQLAlchemy 1.4 the first_connect event is not used by
        909     # the engine, so this will usually not be set
        910     if pool.dispatch.first_connect:
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/util/langhelpers.py:147, in safe_reraise.__exit__(self, type_, value, traceback)
        145     assert exc_value is not None
        146     self._exc_info = None  # remove potential circular references
    --> 147     raise exc_value.with_traceback(exc_tb)
        148 else:
        149     self._exc_info = None  # remove potential circular references
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/pool/base.py:901, in _ConnectionRecord.__connect(self)
        899 try:
        900     self.starttime = time.time()
    --> 901     self.dbapi_connection = connection = pool._invoke_creator(self)
        902     pool.logger.debug("Created new connection %r", connection)
        903     self.fresh = True
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/create.py:641, in create_engine.<locals>.connect(connection_record)
        638         if connection is not None:
        639             return connection
    --> 641 return dialect.connect(*cargs, **cparams)
    
    File ~/opt/anaconda3/lib/python3.9/site-packages/sqlalchemy/engine/default.py:580, in DefaultDialect.connect(self, *cargs, **cparams)
        578 def connect(self, *cargs, **cparams):
        579     # inherits the docstring from interfaces.Dialect.connect
    --> 580     return self.loaded_dbapi.connect(*cargs, **cparams)
    
    OperationalError: (sqlite3.OperationalError) unable to open database file
    (Background on this error at: https://sqlalche.me/e/20/e3q8)

    Check for given Credentials in users table in sqlite.db

    Use of ORM Query object and custom methods to identify user to credentials uid and password

    • Comment on purpose of following
    1. User.query.filter_by
    2. user.password
    def find_by_uid(uid):
        with app.app_context():
            user = User.query.filter_by(_uid=uid).first()
        return user # returns user object
    
    # Check credentials by finding user and verify password
    def check_credentials(uid, password):
        # query email and return user record
        user = find_by_uid(uid)
        if user == None:
            return False
        if (user.is_password(password)):
            return True
        return False
            
    #check_credentials("indi", "123qwerty")
    

    Create a new User in table in Sqlite.db

    Uses SQLALchemy and custom user.create() method to add row.

    • Comment on purpose of following
    1. user.find_by_uid() and try/except
    2. user = User(...)
    3. user.dob and try/except
    4. user.create() and try/except
    def create():
        # optimize user time to see if uid exists
        uid = input("Enter your user id:")
        user = find_by_uid(uid)
        try:
            print("Found\n", user.read())
            return
        except:
            pass # keep going
        
        # request value that ensure creating valid object
        name = input("Enter your name:")
        password = input("Enter your password")
        
        # Initialize User object before date
        user = User(name=name, 
                    uid=uid, 
                    password=password
                    )
        
        # create user.dob, fail with today as dob
        dob = input("Enter your date of birth 'YYYY-MM-DD'")
        try:
            user.dob = datetime.strptime(dob, '%Y-%m-%d').date()
        except ValueError:
            user.dob = datetime.today()
            print(f"Invalid date {dob} require YYYY-mm-dd, date defaulted to {user.dbo}")
               
        # write object to database
        with app.app_context():
            try:
                object = user.create()
                print("Created\n", object.read())
            except:  # error raised if object not created
                print("Unknown error uid {uid}")
            
    create()
    

    Reading users table in sqlite.db

    Uses SQLALchemy query.all method to read data

    • Comment on purpose of following
    1. User.query.all
    2. json_ready assignment
    # SQLAlchemy extracts all users from database, turns each user into JSON
    def read():
        with app.app_context():
            table = User.query.all()
        json_ready = [user.read() for user in table] # each user adds user.read() to list
        return json_ready
    
    read()
    

    Hacks

    • Add this Blog to you own Blogging site. In the Blog add notes and observations on each code cell.
    • Add Update functionality to this blog.
    • Add Delete functionality to this blog.
    </div>