The background that

With the vigorous development of the light application, developers have to learn a little background development technology, but if you want to a reception or operations, directly to fit the one based on Java Web framework, effect is not ideal, for obvious reasons, directly to fit both against the project launched quickly, more easily when using learning because of the high entry of sad, The problem is, isn’t there a quick web framework to choose from? Of course, there are, and there are not too many choices, but today we only introduce one combination, fastApi and Peegee combination.

Introduction to the

The basic reason for choosing Python is that it’s easy to use, and the language is so widely used that even if it’s not for web development, I personally recommend learning Python. Of course, Learning Python is not our focus here.

precondition

Although this is 200 lines, it does not say zero-based, because zero-based development is really not very good for some actual combat, so this article sets up some preconditions

  • Python, needless to say, is a good idea to have some basic knowledge of Python, such as common dictionaries, arrays, and classes. Otherwise, let alone writing the code, it is difficult to read the code
  • SQL, the current Web development is bound to include SQL, of course, since we this is 200 lines of code, it must realize the business function is very limited, the use of the database related functions are also very limited, but it is best to suggest a certain SQL foundation, other not to say, the most basic add, delete, change or check to understand it, Because that’s what we’re doing in 200 lines
  • ORM, object relational mapping (ORM) This is a cliche. I prefer to use ORM to work with databases, which is one of the reasons why I chose Peegee. It is good to have a general understanding of the ORM
  • Others, such as Redis, are used to cache user information in order to simplify user logins

With that in mind, fastApt and Peewee are now in our hands

fastApi

FastApi framework, high Performance, Easy to Learn, fast to Code, Ready for Production, The translation is high performance, easy to use, fast coding, ready to produce. Listen to these four introduction, that still think of what, is certainly into the pit (here as for why high performance, why the production of these we can later open a separate detailed explanation). Since the official introduction is so tempting, we hesitate again, that is too sorry the official description, two words, into the pit

Peewee

Peewee has also used SQLAlchemy in the project before, but when it is used, it feels like it is available, but it is not good enough to use, and I can’t tell where it is not good enough to use. Until I see Peewee, this framework feels like a feature, simple, it is used to inherit Model directly without too much configuration, and then the data operation is ORM mode

Code up

First of all, refer to fastApi and Peewee’s requirements for the local installation of the required package. Requirement is recommended

Peewee ==3.13.1 uvicorn==0.11.3 FastAPI ==0.52.0 PyMySQL>=0.9.3 redis>=3.4.1 PyJWT>=1.7.1Copy the code

Uvicorn runs the server, PyMySQL is the mysql driver (mysql is used to store data here), and PyJWT is the authentication token toolkit. Create a directory by referring to the following table

Functional analysis

Referring to the actual development function, we make a little simplification here, the goal is to achieve a notepad function, after the user logs in, and then can add, delete and query the content of notepad. After the above analysis, we realize four interfaces, namely login interface (POST), add interface (POST), delete interface (POST), query interface (GET), refer to the fastApi official website example

@app.post('/')
def hello(a):
    return {"info":"hello World"}
Copy the code

Interface specification

So our interface reference is as follows, and the basic code could be


@app.post('/lee/user/login')
def login(user: LoginUser):
    "' log in ' ' '
    pass
    
@app.post('/lee/item/save', dependencies=[Depends(verify_token)])
def save_item(param: dict):
    Content saving
    pass

@app.post('/lee/item/all', dependencies=[Depends(verify_token)])
def items(param: dict):
    "' query ' ' '
    pass
@app.post('/lee/item/delete', dependencies=[Depends(verify_token)])
def delete_item(param: dict):
    "' delete ' ' '
    pass
Copy the code

Dependencies =[verify_token

Token associated

At present, in the form of the separation of front and background, our access control also uses the form of token. I will repeat the simple process of token here. Using token, there are basically two things to do: one is to generate token (the login interface verifies the user name and password and returns the token). The other thing is to validate the token (add a token field to each Http request, usually put it in the Header, and then the system gets the token). Now that things are confirmed, I’ll code it

def app_create_token(username, password):
    "' create a token" '
    conn = redis.Redis(connection_pool=POOL) Use Redis to store user information
    # print(conn.get('app_dev_password_' + username).decode('utf-8'))
    if conn.get('app_dev_username_' + username) is None:
        raise Exception('User is temporarily unavailable')
    if conn.get('app_dev_password_' + username).decode('utf-8') != password:
        raise Exception('Password error')
    Check whether the user exists in Redis
    token_name = conn.get('app_username_' + username)
    if token_name is not None:
        If the token is already in redis, it will be returned
        return token_name.decode()
    token = jwt.encode({'user': username}, JWT_SECRET,
                       algorithm='HS256') # Call JWT's package to generate tokens
    token_name = token.decode('utf-8')
    conn.set(name='app_username_' + username, value=token_name)
    return token_name # return token string
Copy the code

Check token

def app_verify_token(token):
    Authentication token ' ' ' ' ' '
    infos = jwt.decode(token, JWT_SECRET, algorithms=['HS256']) If the token is correctly parsed, it is the correct token (timeout can also be added here)
    return infos['user']
Copy the code

This completes the basic token generation and token verification. However, another problem lies in front of us: how to verify tokens? We have stated that we need to get the token information in the header. How do we get the header on each request? In Flask, you can use the features of Python modifiers to modify methods, adding additional information. FastApi introduces a new thing called Dependencies

The dependencies fastApi

Dependencies: Dependency Injection dependencies: Java development dependencies: Dependency Injection dependencies: Java development dependencies: Java development dependencies 🐶🐶🐶 and DI are not described in this section. Here we list the appropriate references to use dependencies on the official website

  • When there is repetitive logic code
  • When sharing database links
  • Security, authentication, or role related
  • The other… Except for the fourth one, I think all the others are familiar to us, and our token basically falls into the third category
#token.py
class TokenException(Exception):
    code: int
    msg: str
    detail: str

    def __init__(self, code, msg, detail=None):
        self.code = code
        self.msg = msg
        self.detail = detail
Let's first define an exception, the purpose of which will be discussed later

# Then we define a method (verify_token), which is determined as described in the documentation
from fastapi import Header


def verify_token(token=Header(None)):
    if token is None:
        raise TokenException(701.'there is no token') The TokenException class is defined to facilitate subsequent unification
    try:
        app_verify_token(token)
    except Exception:
        raise TokenException(702, token + 'Invalid, please confirm token validity')
Once the method is defined, write it in each request as follows
@app.post('/lee/item/all', dependencies=[Depends(verify_token)])
def items(param: dict):
    "' query ' ' '
    pass

Copy the code

As for the parameter method verify_token header why do you write, the reference document fastapi.tiangolo.com/tutorial/he fastApi… , and then add formatting for TokenException

#main.py
class StatusInfo(a):
    def __init__(self, code: int, msg: str, data: dict = None):
        self.code = code
        self.msg = msg
        self.data = data


@app.exception_handler(TokenException)
def error_handler(request, exc: TokenException):
    return JSONResponse(content=jsonable_encoder(StatusInfo(exc.code, exc.msg), include_none=False),
                        media_type="application/json")

Copy the code

In this case, token authentication is implemented to access the interface. Requests without tokens are returned uniformly

{
    "code": 701."msg": "There is no token"
}
Copy the code

If the token is incorrect or invalid (add invalid logic processing), all returns are returned

The business logic

After the above coding, we can now feel at ease to write our business logic code

#sys_conf.py
from peewee import *

db = MySQLDatabase('day_todo', user='username', password='password',
                         host='localhost', port=3306)
Copy the code

First refer to the above link to define the database using Peewee, and then define the corresponding Model for the table

# item.py
from conf.sys_conf import db
from peewee import *
from datetime import datetime

# Notepad class
class Item(Model):
    id = IntegerField(primary_key=True)
    info = CharField()
    insert_time = DateTimeField()
    last_update_time = DateTimeField()
    is_finish = BooleanField()
    is_delete = BooleanField()
    date_batch = CharField()
    user_id = CharField()

    @staticmethod
    def default_create(info):
        Static default object creation method
        item = Item()
        item.is_finish = False
        item.is_delete = False
        item.insert_time = datetime.now()
        item.info = info
        item.last_update_time = datetime.now()
        item.date_batch = datetime.strftime(datetime.now(), '%Y%m%d')
        return item

    def to_json(self):
        "' turn bean json" '
        json = {}
        json['id'] = self.id
        json['info'] = self.info
        json['insert_time'] = self.insert_time
        json['last_update_time'] = str(self.last_update_time)
        json['is_finish'] = self.is_finish
        json['is_delete'] = self.is_delete
        json['date_batch'] = self.date_batch
        json['user_id'] = self.user_id
        return json

    class Meta:
        database = db The corresponding database link name
        table_name = 'lee_item' Create table name for mapping
Copy the code

Let’s take one of the delete interfaces as an example

# main.py
@app.post('/lee/item/delete', dependencies=[Depends(verify_token)])
def delete_item(param: dict): Json parameters are passed using param and are of type dict
    try:
        id = param['id']
        user = param['user']
        db.connect()

        list = Item.select().where(Item.id == id, Item.user_id == user) # use peewee to query the notepad information that matches the conditions
        if len(list) < 1:
            return create_response(200.'No data to change')
        item = list[0]
        item.is_delete = True Select delete from 'delete'
        item.save()  # save
        return create_response(200.'success') # format return
    except Exception as e:
        return create_response(201.'Delete record failed' + str(e))
    finally:
        db.commit()
        db.close()
      
# format return
def create_response(code: int = 200, msg: str = 'success', data: dict = None):
    return JSONResponse(content=jsonable_encoder(StatusInfo(code=code, msg=msg, data=data), include_none=False),
                        media_type="application/json")
Copy the code

Other interfaces can be developed by referring to this interface

conclusion

Through the above explanation we should use fastApi and Peewee fast development background API have a basic understanding, the project detailed code reference source code. The article inevitably has the fallacy, welcome everybody to exchange more, give directions more.