python tool function code

1. Implement a decorator that determines the presence of the redis server

from functools import wraps
from flask import g

from rmon.common.rest import RestException


class ObjectMustBeExist:
    """The ornament is used for a Server Instance deletion check to ensure operation object must exist
    """

    def __init__(self, object_class):
        """
        Args:
            object_class (class): Database Objects
        """

        self.object_class = object_class

    def __call__(self, func):
        """Decorator implementation
        """
        @wraps(func)
        def wrapper(*args, **kwargs):
            """
            Args:
                object_id (int): SQLAlchemy object id
            """

            object_id = kwargs.get('object_id')
            if object_id is None:
                raise RestException(404, 'object not exist')

            obj = self.object_class.query.get(object_id)
            if obj is None:
                raise RestException(404, 'object not exist')

            g.instance = obj
            return func(*args, **kwargs)

        return wrapper

Python's decorator is generally a function and callable, but the ObjectMustBeExist implemented here is a class, but its instances can also be used like regular decorator functions because u is defined call_u Special methods, such as decorating func functions with the syntax of ObjectMustBeExist(Server)(func), can be used. During ObjectMustBeExist initialization, a library model class can be passed, similar to the Server Redis server database model defined earlier, so that the entire decorator can determine if the corresponding model object exists. When passing through object_ When the ID finds the corresponding model instance, it stores it in flask. In the G object, it is an application context variable and each HTTP request comes in with a different instance of the model object.

With the ObjectMustBeExist decorator, /server/sever_ The API implementation for ID is simpler:

# Omit completed code

class ServerDetail(RestView):
    """ Redis Server List
    """

    # Write an instance of the adorner class into the adorner tuple for invocation
    method_decorators = (ObjectMustBeExist(Server), )

    def get(self, object_id):
        """Get server details
        """
        # schema.dump returns a marshmallow. Schema. Instances of the MarshalResult class
        # The instance holds two dictionaries: the Redis server instance dictionary and the error information dictionary.
        # This get method dispatch_in the RestView class Called in request method
        # Decorators have been added before the call, and RestException exceptions are triggered if there are errors
        data, _ = ServerSchema().dump(g.instance)
        return data

    def put(self, object_id):
        """Update Server
        """
        # Here you need to assign a value to the context property when instantiating the schema vector
        # That is, object_ The Server instance corresponding to ID is stored in self. In the context dictionary
        schema = ServerSchema(context={'instance': g.instance})
        # Data is a dictionary object that holds data sent by the client
        data = request.get_json()
        # partial=True updates fields from the data dictionary to the context. In instance
        # That is, the returned server is the updated g.instance object
        # This is create_in ServerSchema Or_ Update method implemented
        # If the value of this parameter is False, only the data dictionary will be deserialized
        # In this case there must be something in the errors dictionary
        server, errors = schema.load(data, partial=True)
        if errors:
            return errors, 400
        server.save()
        return {'ok': True}

    def delete(self, object_id):
        """Delete Server
        """
        g.instance.delete()
        # Status code 204 means the request executes successfully, but does not jump to a new page or refresh the current page
        return {'ok': True}, 204

The code maximizes the use of the Server object, or g.instance, that is saved at runtime by the ObjectMustBeExist instance decorator. It is important to note that the code schema that initializes ServerSchema in the put method = ServerSchema (context={'instance': g.instance}), since in subsequent ServerSchemas. During load deserialization, you need to verify that there is a Server object with the same name, but to distinguish between update and create operations, the currently updated Server object is passed in here through the context property.

2. Creation and modification time of Datetime in flask-sqlalchemy, default, server_default, onupdate

The following two fields in falsk

create_time1 = db.Column(db.DateTime, default=datetime.now)

create_time2 = db.Column(db.DateTime, default=datetime.now())

The difference between the two:

The first insertion is expected, and the insertion time of the data is automatically generated based on the current time when each data is inserted

The second is a fixed time, when the program is deployed, when all the data is

In fact, the default values are not reflected in the mysql database, which sqlalchemy adds when inserting data

If you want to use server_as a default value in the generated table Default
There is a difference between the default value of SQLAlchemy and the default value of SQL. Typically, setting the default value for SQLAlchemy is sufficient, as you should not normally interact directly with the server. If you do want to render defaults on the server side, use server_default and server_onupdate parameter to Column.

name = db.Column(db.String(45), server_default='hh')

When we want to specify server_for Boolean type text is required when default

from sqlalchemy import text
is_domain = db.Column(db.Boolean,default=False,server_default=text('0'))

Since mysql's datetime-type data does not support functions, it is not possible to specify a default value of the current time

Record the time of each change, onupdate

update_time = db.Column(db.DateTime, default=datetime.now,onupdate=datetime.now)

Also, consider using datetime.now changes to datetime.utcnow, because UTC is easier to use behind the scenes. Convert to local time only when something is displayed to the user.

Example:

class Base(db.Model):
    """ All model A base class with a timestamp added by default
    """

    __abstract__ = True  # Indicates not to treat this class as a Model class

    # Neither default nor onupdate timestamps need to be maintained by themselves
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow,
                        onupdate=datetime.utcnow)


# Inherit Base Class Instead
class User(Base):
    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(32), unique=True, index=True, nullable=False)
    publish_courses = db.relationship('Course')

# Inherit Base Class Instead
class Course(Base):
    __tablename__ = 'course'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), unique=True, index=True, nullable=False)
    author_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'))
    author= db.relationship('User', uselist=False)

3. Creation of flask-sqlalchemy User table

User table:

from werkzeug.security import generate_password_hash, check_password_hash

class User(Base):
    __tablename__ = 'user'

    # Numeric representation of roles makes it easy to determine if you have permission, such as having an operation with an employee role
    # Only users above can do so, so long as the user is judged. Role >= ROLE_ STAFF
    # That's fine. A 10-point interval between values is set to allow you to add other roles later on.
    ROLE_USER = 10
    ROLE_STAFF = 20
    ROLE_ADMIN = 30

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(32), unique=True, index=True, nullable=False)
    email = db.Column(db.String(64), unique=True, index=True, nullable=False)
    # By default, sqlalchemy defines the column name as the field name, but here is _ Password, so explicitly specify the database table column name as password
    _password = db.Column('password', db.String(256), nullable=False)
    role = db.Column(db.SmallInteger, default=ROLE_USER)
    job = db.Column(db.String(64))
    publish_courses = db.relationship('Course')

    def __repr__(self):
        return '<User:{}>'.format(self.username)

    @property
    def password(self):
        """ Python Stylistic getter """
        return self._password

    @password.setter
    def password(self, orig_password):
        """ Python Stylistic setter, Set up like this user.password Will
        Automatically for password Generate Hash Value Save In _password field
        """
        self._password = generate_password_hash(orig_password)

    def check_password(self, password):
        """ Determine the password and storage entered by the user hash Is Password Equal
        """
        return check_password_hash(self._password, password)

    @property
    def is_admin(self):
        return self.role == self.ROLE_ADMIN

    @property
    def is_staff(self):
        return self.role == self.ROLE_STAFF

First add email, _to the user table password, role, job field. Job represents the user's job, position. Role is the role of the user. Currently, we only create three roles: normal user (ROLE_USER), company employee (ROLE_STAFF), and administrator (ROLE_ADMIN). The default after user registration is the normal user role.

Users'passwords are generally not stored in clear text for data security purposes, but are generally stored as hashes of the original password. Flask's underlying library, werkzeug, provides the function generate_to generate password hashes Password_ Hash and check_to detect if password hash and password are equal Password_ Hash function.

This will _ Password is set to a private field, exposing the password property. Python-style setter s use generate_when setting password Password_ Hash generates and sets a hash password. Next, a check_is encapsulated The password function method tests whether the password entered by the user at login is equal to the stored hash password. Then there are two properties that make it easy to determine if a user is in an employee or administrator role.

Then relive the use of this property

When we define a database field class, we often need to make some restrictions on its class properties, which are generally written by get and set methods. In python, how can we write less code, elegantly implement the desired restrictions and reduce errors, then we need our @property@

Examples with code are easier to understand, such as a student's report card defined as this:

class Student(object):

    def get_score(self):
        return self._score

    def set_score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

We need to call it this way:

>>> s = Student()
>>> s.set_score(60) # ok!
>>> s.get_score()
60
>>> s.set_score(9999)
Traceback (most recent call last):
  ...
ValueError: score must between 0 ~ 100!

But for convenience and time saving, we don't want to write s.set_score(9999). Isn't it faster to write s.score = 9999 directly? Adding methods to limit the number of calls doesn't make them cumbersome. @property is coming to help....

class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self,value):
        if not isinstance(value, int):
            raise ValueError('Scores must be integers')
        if value < 0 or value > 100:
            raise ValueError('Score must be 0-100 Between')
        self._score = value

Looking at the code above, changing a get method to an attribute requires only the @property decorator, which in turn creates another decorator @score.setter, responsible for turning set methods into assignments to attributes, after doing so, we call them both controllable and convenient

>>> s = Student()
>>> s.score = 60 # OK, actually converted to s.set_score(60)
>>> s.score # OK, actually converted to s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
  ...
ValueError: score must between 0 ~ 100!

Keywords: Python Back-end Flask python-web

Added by coho75 on Mon, 28 Feb 2022 19:28:57 +0200