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.