类似库联合查询系统

 源码地址:https://github.com/FFlask/search_db

1、需求分析

N个数据库,依次模糊查询后返回结果,汇总后展示到界面上

数据库是保存在本地数据库中,并在后台界面动态的添加到查询系统中

为了保证安全,加个登陆,但不开放注册,统一在后台界面添加新用户

用户分为管理员和普通用户,管理员可以添加普通用户

单一IP暴力破解登陆的问题使用Nginx+验证码(暂未实现)来解决

2、数据库设计

共需要三张表,用户表和权限表,数据库管理表(用于保存需要查询的数据库)

3、框架选择

由于最近在学习Flask框架,本项目采用Flask框架和其配套模块

前端使用Bootstrap框架

由于功能简单(代码500行内),就所有代码就写在一个文件上了

4、具体功能实现

4.1 实现说明

已实现功能:登陆,汇总查询,数据表的增删改查,权限控制,普通用户的增删改查

未实现功能:验证码

系统的难点:数据表和本地数据库表的动态关联,使用了元类,通过字符串找类,动态创建变量名

初始化:初始化创建表的时候应将元类创建模型的部分注释掉

4.2 代码实现

  1 # -*- coding: UTF-8 -*-
  2 from flask import Flask,render_template,url_for,redirect,request,flash,session,abort
  3 from flask_sqlalchemy import SQLAlchemy
  4 from flask_wtf import FlaskForm
  5 from wtforms import StringField,PasswordField,SubmitField,SelectField,ValidationError
  6 from wtforms.validators import Email,EqualTo,Length,DataRequired
  7 from werkzeug.security import generate_password_hash,check_password_hash
  8 from flask_login import LoginManager,UserMixin,login_required,login_user,logout_user,current_user
  9 from functools import wraps
 10 
 11 
 12 app = Flask(__name__)
 13 login_manager = LoginManager()
 14 #设置验证强度,以提供不同的安全等级防止用户会话遭篡改
 15 #设为'strong' 时,Flask-Login 会记录客户端IP地址和浏览器的用户代理信息,如果发现异动就登出用户
 16 login_manager.session_protection = 'strong'
 17 #设置登陆失败后的跳转
 18 login_manager.login_view = 'index'
 19 
 20 
 21 class Config:
 22     SECRET_KEY = '1a1@z2'
 23     SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:root@127.0.0.1/world?charset=utf8'
 24 
 25     #SQLALCHEMY_TRACK_MODIFICATIONS = True
 26     @staticmethod
 27     def init_app(app):
 28         pass
 29 
 30 app.config.from_object(Config)
 31 db=SQLAlchemy(app)
 32 
 33 #数据库模型
 34 
 35 @login_manager.user_loader
 36 def load_user(user_id):
 37     return User.query.get(int(user_id))
 38 login_manager.init_app(app)
 39 #用户表
 40 class User(UserMixin,db.Model):
 41     __tablename__ = 'user'
 42     id = db.Column(db.Integer,primary_key=True,index=True)
 43     username = db.Column(db.String(64),unique=True,index=True)
 44     password_hash = db.Column(db.String(128))
 45     role_id = db.Column(db.Integer, db.ForeignKey('role.id'))
 46 
 47     @property
 48     def password(self):
 49         raise AttributeError('password unread')
 50 
 51     @password.setter
 52     def password(self,password):
 53         self.password_hash = generate_password_hash(password)
 54 
 55     def verify_password(self,password):
 56         return check_password_hash(self.password_hash,password)
 57 
 58     #新增用户默认是普通用户权限
 59     def __init__(self,*args,**kwargs):
 60         super(User,self).__init__(*args,**kwargs)
 61         if self.role is None:
 62             if self.username == 'admin':
 63                 self.role = Role.query.filter_by(role_name='Admin').first()
 64             else:
 65                 self.role = Role.query.filter_by(role_name='User').first()
 66 
 67     def can(self,permissions):
 68         return self.role is not None and (self.role.permissions & permissions) == permissions
 69 
 70     def is_admin(self):
 71         return self.can(Permissions.Admin)
 72 
 73     def __repr__(self):
 74         return '<User %r>'%(self.username)
 75 
 76 
 77 #权限类
 78 class Permissions:
 79     Visit = 0x01
 80     Read = 0x02
 81     Write = 0x04
 82     Manager = 0x08
 83     Admin = 0xFF
 84 
 85 #角色权限表
 86 class Role(db.Model):
 87     __tablename__ = 'role'
 88     id = db.Column(db.Integer,primary_key=True,index=True)
 89     role_name = db.Column(db.String(64),unique=True,index=True)
 90     permissions = db.Column(db.Integer)
 91     user = db.relationship('User',backref='role')
 92 
 93     @staticmethod
 94     def init_role():
 95         roles = {
 96             'Visitor':Permissions.Visit,
 97             'User':Permissions.Visit|Permissions.Read|Permissions.Write,
 98             'Manager':Permissions.Visit|Permissions.Read|Permissions.Write|Permissions.Manager,
 99             'Admin':Permissions.Admin
100         }
101         for role_name,role_permissions in roles.items():
102             role = Role(role_name=role_name,permissions=role_permissions)
103             db.session.add(role)
104             db.session.commit()
105 
106     def __repr__(self):
107         return '<Role %r>'%(self.role_name)
108 
109 
110 
111 #数据库表
112 class DBTable(db.Model):
113     __tablename__ = 'db_table'
114     id = db.Column(db.Integer, primary_key=True, index=True)
115     table_name = db.Column(db.String(64), unique=True)
116     table_src = db.Column(db.String(64))
117     def __repr__(self):
118         return 'db_table <%r>'%(self.table_name)
119 
120 
121 
122 #动态创建数据库模型
123 table1s = DBTable.query.all()
124 #把字符串变成变量
125 createVar = locals()
126 for table1 in table1s:
127     #使用元类动态创建表中存储的表名
128     createVar[str(table1.table_name)] = type(str(table1.table_name),(db.Model,),{
129         '__tablename__':table1.table_name,
130         'id':db.Column(db.Integer, primary_key=True, index=True),
131         'username':db.Column(db.String(64), unique=True,index=True),
132         'password':db.Column(db.String(64)),
133         'email':db.Column(db.String(64), unique=True,index=True),
134         })
135 
136 
137 #表单类
138 #搜索框
139 class SearchForm(FlaskForm):
140     search = StringField(validators=[DataRequired()],
141                          render_kw={'class':'form-control',
142                                     'size':'50',
143                                     'placeholder':'input search content'})
144     kind = SelectField(choices=[
145         ('username','username'),('email','email')
146     ],render_kw={'class':'form-control'})
147     submmit = SubmitField('Search',render_kw={'class':'btn btn-info'})
148 
149 #登陆框
150 class LoginForm(FlaskForm):
151     username = StringField('Username', validators=[DataRequired(), Length(1, 64)],
152                            render_kw={'class':'form-control','placeholder':'enter your username'})
153     password = PasswordField('Password', validators=[DataRequired()],
154                              render_kw={'class':'form-control','placeholder':'enter your password'})
155     verify_code = StringField('CheckCode',
156                               render_kw={'class': 'form-control', 'placeholder': 'zzzzzzzzz'})
157     submmit = SubmitField('Login',render_kw={'class':'btn btn-info'})
158 
159 #新增数据库
160 class DBAddForm(FlaskForm):
161     table_name = StringField('table_name', validators=[DataRequired()],
162                            render_kw={'class':'form-control','placeholder':'table_name'})
163     table_src = StringField('table_src', validators=[DataRequired()],
164                               render_kw={'class': 'form-control', 'placeholder': 'table_src'})
165     submmit = SubmitField('Add',render_kw={'class':'btn btn-info'})
166 
167 #编辑数据库
168 class DBEditForm(FlaskForm):
169     table_name = StringField('table_name', validators=[DataRequired()],
170                            render_kw={'class':'form-control','placeholder':'table_name'})
171     table_src = StringField('table_src', validators=[DataRequired()],
172                               render_kw={'class': 'form-control', 'placeholder': 'table_src'})
173     submmit = SubmitField('Edit',render_kw={'class':'btn btn-info'})
174 
175 #新增用户
176 class UserAddForm(FlaskForm):
177     username = StringField('username', validators=[DataRequired()],
178                            render_kw={'class':'form-control','placeholder':'username'})
179     password = StringField('password', validators=[DataRequired()],
180                               render_kw={'class': 'form-control', 'placeholder': 'password'})
181     submmit = SubmitField('Add',render_kw={'class':'btn btn-info'})
182 
183     def validate_username(self,field):
184         if User.query.filter_by(username = field.data).first():
185             raise ValidationError('username is repeat')
186 
187 #修改用户信息
188 class UserEditForm(FlaskForm):
189     username = StringField('username', validators=[DataRequired()],
190                            render_kw={'class':'form-control','readonly':'readonly'})
191     new_password = StringField('new_password',
192                               render_kw={'class': 'form-control', 'placeholder': 'new password'})
193     role = SelectField(coerce=int,render_kw={'class':'form-control'})
194     submmit = SubmitField('Edit',render_kw={'class':'btn btn-info'})
195     def __init__(self,*args,**kwargs):
196         super(UserEditForm,self).__init__(*args,**kwargs)
197         self.role.choices = [(role.id,role.role_name) for role in Role.query.order_by(Role.role_name).all()
198                              if role.role_name=='Admin' or role.role_name == 'User']
199 
200 #权限控制装饰器
201 def permission_required(permission):
202     def decorator(func):
203         @wraps(func)
204         def decorated_function(*args, **kwargs):
205             if not current_user.can(permission):
206                 abort(403)
207             return func(*args, **kwargs)
208         return decorated_function
209     return decorator
210 #具有admin权限
211 def is_admin(func):
212     return permission_required(Permissions.Admin)(func)
213 
214 
215 
216 #业务逻辑
217 #初次运行时运行一下,创建admin账户
218 @app.route('/create_user')
219 def create_user():
220     user1 = User.query.filter_by(username = 'admin').first()
221     if not user1:
222         user2 = User(username='admin',password='admin')
223         db.session.add(user2)
224         db.session.commit()
225     return 'ok'
226 
227 #测试
228 @app.route('/test')
229 def test():
230     return 'test'
231 
232 #登陆页面
233 @app.route('/',methods=['GET','POST'])
234 def index():
235     login_form = LoginForm()
236     if current_user.is_authenticated:
237         print(current_user.username)
238     if login_form.validate_on_submit():
239         user1 = User.query.filter_by(username = login_form.username.data).first()
240         if user1 and user1.verify_password(login_form.password.data):
241             login_user(user1)
242             return redirect(request.args.get('next') or url_for('search_db'))
243         else:
244             flash('username or password error')
245             return redirect(url_for('index'))
246     return render_template('index.html',login_form=login_form)
247 
248 @app.route('/logout')
249 @login_required
250 def logout():
251     logout_user()
252     return redirect(url_for('index'))
253 
254 #搜索页面
255 @app.route('/search_db',methods=['GET','POST'])
256 @login_required
257 def search_db():
258     search_form = SearchForm()
259     ip = request.remote_addr
260     results = {}
261     if search_form.validate_on_submit():
262         search_item = search_form.kind.data
263         for index,table1 in enumerate(DBTable.query.all()):
264             try:
265                 #动态创建变量名
266                 createVar[str(table1.table_name)+str(index)] =
267                     globals()[table1.table_name].query.filter(getattr(globals()[table1.table_name],search_item)
268                                                               .like('%'+'%s'%search_form.search.data+'%')).all()
269                 if createVar[str(table1.table_name)+str(index)] is not None:
270                     results.update({table1.table_src: createVar[str(table1.table_name)+str(index)]})
271             except:
272                 flash('Your db is not have table <%s>,Please update it!'%(str(table1.table_name)))
273     return render_template('search_db.html',search_form=search_form,results=results,ip=ip)
274 
275 #数据库管理/查
276 @app.route('/admin_db/query')
277 @login_required
278 @is_admin
279 def admin_db_query():
280     return render_template('admin_db_query.html', table1s = DBTable.query.all())
281 
282 #数据库管理/增
283 @app.route('/admin_db/add',methods=['POST','GET'])
284 @login_required
285 @is_admin
286 def admin_db_add():
287     db_add_form = DBAddForm()
288     if db_add_form.validate_on_submit():
289         new_table = DBTable(
290             table_name=db_add_form.table_name.data,
291             table_src=db_add_form.table_src.data,
292         )
293         db.session.add(new_table)
294         db.session.commit()
295         return redirect(url_for('admin_db_query'))
296     return render_template('admin_db_add.html',db_add_form=db_add_form)
297 
298 #数据库管理/改
299 @app.route('/admin_db/edit',methods=['POST','GET'])
300 @login_required
301 @is_admin
302 def admin_db_edit():
303     edit_id = request.args.get('edit_id')
304     db_table1 = DBTable.query.filter_by(id=edit_id).first()
305     db_edit_form = DBEditForm()
306     if db_edit_form.validate_on_submit():
307         db_table1.table_name = db_edit_form.table_name.data
308         db_table1.table_src = db_edit_form.table_src.data
309         db.session.add(db_table1)
310         db.session.commit()
311         return redirect(url_for('admin_db_query'))
312     db_edit_form.table_name.data = db_table1.table_name
313     db_edit_form.table_src.data = db_table1.table_src
314     return render_template('admin_db_edit.html',db_edit_form=db_edit_form,db_table1=db_table1)
315 
316 #数据库管理/删
317 @app.route('/admin_db/delete')
318 @login_required
319 @is_admin
320 def admin_db_delete():
321     delete_id = request.args.get('delete_id')
322     db_table1 = DBTable.query.filter_by(id=delete_id).first()
323     db.session.delete(db_table1)
324     db.session.commit()
325     return redirect(url_for('admin_db_query'))
326 
327 
328 #用户管理/查
329 @app.route('/admin_user/query',methods=['POST','GET'])
330 @login_required
331 @is_admin
332 def admin_user_query():
333     return render_template('admin_user_query.html',table1s=User.query.all())
334 
335 #用户管理/增
336 @app.route('/admin_user/add',methods=['POST','GET'])
337 @login_required
338 @is_admin
339 def admin_user_add():
340     user_add_form = UserAddForm()
341     if user_add_form.validate_on_submit():
342         new_user = User(
343             username=user_add_form.username.data,
344             password=user_add_form.password.data
345         )
346         db.session.add(new_user)
347         db.session.commit()
348         return redirect(url_for('admin_user_query'))
349     return render_template('admin_user_add.html',user_add_form=user_add_form)
350 
351 #用户管理/改
352 @app.route('/admin_user/edit',methods=['POST','GET'])
353 @login_required
354 @is_admin
355 def admin_user_edit():
356     edit_id = request.args.get('edit_id')
357     user1 = User.query.filter_by(id=edit_id).first()
358     user_edit_form = UserEditForm()
359     if user_edit_form.validate_on_submit():
360         user1.username = user_edit_form.username.data
361         user1.role_id = user_edit_form.role.data
362         if user_edit_form.new_password.data:
363             user1.password = user_edit_form.new_password.data
364         db.session.add(user1)
365         db.session.commit()
366         return redirect(url_for('admin_user_query'))
367     user_edit_form.username.data = user1.username
368     user_edit_form.role.data = user1.role_id
369     return render_template('admin_user_edit.html',user_edit_form=user_edit_form,user1=user1)
370 
371 #用户管理/删
372 @app.route('/admin_user/delete',methods=['POST','GET'])
373 @login_required
374 @is_admin
375 def admin_user_delete():
376     delete_id = request.args.get('delete_id')
377     user1 = User.query.filter_by(id=delete_id).first()
378     db.session.delete(user1)
379     db.session.commit()
380     return redirect(url_for('admin_user_query'))
381 
382 
383 if __name__ == '__main__':
384     app.run(host="0.0.0.0", port=5000)
app.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>首页</title>
 6         <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
 7     <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
 8     <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
 9 </head>
10 <body>
11 <div class="col-sm-6 col-sm-offset-3">
12     <h2>登陆</h2><br>
13     <form method="POST" action="{{ url_for('index') }}" >
14         {{ login_form.hidden_tag() }}
15         {{ login_form.username }}<br>
16         {{ login_form.password }}<br>
17         {{ login_form.submmit }}
18     </form>
19     <div>
20     {% for msg in get_flashed_messages() %}
21         {{ msg }}
22     {% endfor %}
23     </div>
24 </div>
25 
26 </body>
27 </html>
index.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>搜索</title>
 6     <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
 7     <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
 8     <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
 9 </head>
10 <body>
11 <div class="col-sm-6 col-sm-offset-3">
12 <h2>查询,查询,查询!</h2>
13 <form method="POST" action="{{ url_for('search_db') }}" class="form-inline" >
14     {{ search_form.hidden_tag() }}
15     {{ search_form.search }}
16     {{ search_form.kind }}
17     {{ search_form.submmit }}
18 </form>
19 
20 <br>
21 {% for msg in get_flashed_messages() %}
22     <strong style="color: red">{{ msg }}</strong>
23 {% endfor %}
24 <hr>
25 <table border="1" class="table table-bordered">
26     <thead>
27         <tr class="info">
28             <td><strong>来源</strong></td>
29             <td><strong>用户</strong></td>
30             <td><strong>密码</strong></td>
31             <td><strong>邮箱</strong></td>
32         </tr>
33     </thead>
34     {% for result_key,result_value in results.items() %}
35         {% for result in result_value %}
36         <tr>
37             <td>{{ result_key }}</td>
38             <td>{{ result.username }}</td>
39             <td>{{ result.password }}</td>
40             <td>{{ result.email }}</td>
41         </tr>
42         {% endfor %}
43     {% endfor %}
44 </table>
45 </div>
46 <div class="col-sm-2">
47     <a href="{{ url_for('index') }}">logout</a>&nbsp;&nbsp;
48     {% if current_user.username == 'admin' %}
49     <a href="{{ url_for('admin_db_query') }}">后台</a>
50     {% endif %}
51     <br>欢迎{{ current_user.username }}<br>
52     你的IP是:<{{ ip }}><br>
53     不要乱搞呦!
54 </div>
55 </body>
56 </html>
search_db.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>后台管理</title>
 6     <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
 7     <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
 8     <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
 9 </head>
10 <body>
11 <div class="col-sm-6 col-sm-offset-3">
12 <ul class="nav nav-tabs nav-justified">
13  <li role="presentation" class="active"><a href="#">数据库管理</a></li>
14   <li role="presentation"><a href="{{ url_for('admin_user_query') }}">用户管理</a></li>
15   <li role="presentation"><a href="{{ url_for('search_db') }}">返回查询</a></li>
16 </ul>
17 <ul class="nav nav-pills nav-justified">
18   <li role="presentation" ><a href="{{ url_for('admin_db_query') }}"></a></li>
19   <li role="presentation" class="active"><a href="{{ url_for('admin_db_add') }}"></a></li>
20   <li role="presentation"><a href="#"></a></li>
21     <li role="presentation"><a href="#"></a></li>
22 </ul>
23 <hr>
24     <div class="jumbotron">
25             <h4>新增数据库说明</h4>
26             <ul>
27                 <li>本地数据库名必须为search_db</li>
28                 <li>新增表名必须与数据库中表名完全一致</li>
29                 <li>列名至少要包含username,password,email</li>
30             </ul>
31         </div>
32     <div class="col-sm-10 col-sm-offset-1">
33         <form method="POST" action="{{ url_for('admin_db_add') }}" >
34         {{ db_add_form.hidden_tag() }}
35         {{ db_add_form.table_name }}<br>
36         {{ db_add_form.table_src }}<br>
37         {{ db_add_form.submmit }}
38     </form>
39     </div>
40 </div>
41 </body>
42 </html>
admin_db_add.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>后台管理</title>
 6     <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
 7     <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
 8     <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
 9 </head>
10 <body>
11 <div class="col-sm-6 col-sm-offset-3">
12 <ul class="nav nav-tabs nav-justified">
13  <li role="presentation" class="active"><a href="#">数据库管理</a></li>
14   <li role="presentation"><a href="#">用户管理</a></li>
15   <li role="presentation"><a href="{{ url_for('search_db') }}">返回查询</a></li>
16 </ul>
17 <ul class="nav nav-pills nav-justified">
18   <li role="presentation" ><a href="{{ url_for('admin_db_query') }}"></a></li>
19   <li role="presentation" ><a href="{{ url_for('admin_db_add') }}"></a></li>
20   <li role="presentation" class="active"><a href="#"></a></li>
21     <li role="presentation"><a href="#"></a></li>
22 </ul>
23 <hr>
24     <div class="jumbotron">
25             <h4>修改数据库说明</h4>
26             <ul>
27                 <li>本地数据库名必须为search_db</li>
28                 <li>新增表名必须与数据库中表名完全一致</li>
29                 <li>列名至少要包含username,password,email</li>
30             </ul>
31         </div>
32     <div class="col-sm-10 col-sm-offset-1">
33         <form method="POST" action="{{ url_for('admin_db_edit',edit_id = db_table1.id) }}" >
34         {{ db_edit_form.hidden_tag() }}
35         {{ db_edit_form.table_name }}<br>
36         {{ db_edit_form.table_src }}<br>
37         {{ db_edit_form.submmit }}
38     </form>
39     </div>
40 </div>
41 </body>
42 </html>
admin_db_edit.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>后台管理</title>
 6     <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
 7     <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
 8     <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
 9 </head>
10 <body>
11 <div class="col-sm-6 col-sm-offset-3">
12 <ul class="nav nav-tabs nav-justified">
13  <li role="presentation" class="active"><a href="#">数据库管理</a></li>
14   <li role="presentation"><a href="{{ url_for('admin_user_query') }}">用户管理</a></li>
15   <li role="presentation"><a href="{{ url_for('search_db') }}">返回查询</a></li>
16 </ul>
17 <ul class="nav nav-pills nav-justified">
18   <li role="presentation" class="active"><a href="{{ url_for('admin_db_query') }}"></a></li>
19   <li role="presentation"><a href="{{ url_for('admin_db_add') }}"></a></li>
20   <li role="presentation"><a href="#"></a></li>
21     <li role="presentation"><a href="#"></a></li>
22 </ul>
23 <hr>
24     <div class="col-sm-10 col-sm-offset-1">
25         <table border="1" class="table table-bordered">
26             <thead>
27                 <tr class="info">
28                     <td>id</td>
29                     <td>表名</td>
30                     <td>来源</td>
31                     <td>操作</td>
32                 </tr>
33             </thead>
34             {% for table in table1s %}
35                 <tr>
36                     <td>{{ table.id }}</td>
37                     <td>{{ table.table_name }}</td>
38                     <td>{{ table.table_src }}</td>
39                     <td><a href="{{ url_for('admin_db_edit',edit_id = table.id) }}">修改</a>|<a href="{{ url_for('admin_db_delete',delete_id = table.id) }}">删除</a></td>
40                 </tr>
41             {% endfor %}
42         </table>
43     </div>
44 </div>
45 </body>
46 </html>
admin_db_query.html

4.3 界面展示

5、经验总结

所有后端代码都写一个文件还是有点乱,下次还是分开

用户登陆,权限控制应当制作成一个模块,以后每次复制粘贴就行了

原文地址:https://www.cnblogs.com/cx59244405/p/10238303.html