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!