Flask 菜品管理
common/libs/Helper.py
getDictFilterField() 方法
用于在web/templates/food/index.html中展示菜品分类
如何能够通过food里面的cat_id获取分类信息呢?只能通过for循环,这样会很麻烦,所以定义了这个方法。
这个方法可以的查询返回结果集dic,按照某一个字段构建成某一个字典,这的key值一般是cat_id,
select_filed 要查询的字段
key_field 希望返回结果集dic里面key键的字段
id_list 希望select_filed要查询的这个字段里等于哪些值
'''
根据某个字段获取一个dic出来
'''
def getDictFilterField( db_model,select_filed,key_field,id_list ):ret = {}query = db_model.queryif id_list and len( id_list ) > 0:query = query.filter( select_filed.in_( id_list ) )list = query.all()if not list:return retfor item in list:if not hasattr( item,key_field ):breakret[ getattr( item,key_field ) ] = itemreturn ret
如在Food.py
cat_mapping = getDictFilterField( FoodCat,FoodCat.id,"id",[] )
这段代码的作用是根据给定的数据库模型、选择字段、键字段和ID列表,返回一个字典。其中,字典的键是根据键字段从数据库中获取的值,值是对应的数据库模型对象。
具体来说,函数getDictFilterField
接受四个参数:db_model
表示数据库模型,select_field
表示选择字段,key_field
表示键字段,id_list
表示ID列表。函数首先创建一个空字典ret
,然后获取数据库模型的查询对象query
。如果ID列表不为空,则将选择字段与ID列表进行过滤,即只选择ID在ID列表中的记录。接下来,函数执行查询并将结果存储在列表list
中。
然后,函数遍历列表中的每个对象,检查对象是否具有键字段。
如果对象没有键字段,跳出循环。
否则,函数将键字段的值作为字典的键,将对象作为字典的值存储在字典ret
中。
最后,函数返回字典ret
,其中包含了根据键字段从数据库中获取的值和对应的数据库模型对象。
问题1: hasattr() 是什么函数?有什么功能?
Python hasattr() 函数 | 菜鸟教程
hasattr(object, name)
如果对象有该属性返回 True,否则返回 False。
问题2: getattr() 是什么函数?有什么功能?
Python getattr() 函数 | 菜鸟教程
getattr() 函数用于返回一个对象属性值。
getattr(object, name[, default])
- object -- 对象。
- name -- 字符串,对象属性。
- default -- 默认返回值,如果不提供该参数,在没有对应属性时,将触发 AttributeError。
返回对象属性值。
web/templates/food/index.html
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_food.html" %}
<div class="row"><div class="col-lg-12"><form class="form-inline wrap_search"><div class="row m-t p-w-m"><div class="form-group"><select name="status" class="form-control inline"><option value="-1">请选择状态</option>{% for tmp_key in status_mapping %}<option value="{{ tmp_key }}" {% if tmp_key == search_con['status'] %} selected {% endif %}>{{ status_mapping[ tmp_key ] }}</option>{% endfor %}</select></div><div class="form-group"><select name="cat_id" class="form-control inline"><option value="0">请选择分类</option>{% for tmp_key in cat_mapping %}<option value="{{ tmp_key }}" {% if tmp_key|string == search_con['cat_id'] %} selected {% endif %} >{{ cat_mapping[ tmp_key].name }}</option>{% endfor %}</select></div><div class="form-group"><div class="input-group"><input type="text" name="mix_kw" placeholder="请输入关键字" class="form-control" value="{{ search_con['mix_kw'] }}"><input type="hidden" name="p" value="{{ search_con['p'] }}"><span class="input-group-btn"><button type="button" class="btn btn-primary search"><i class="fa fa-search"></i>搜索</button></span></div></div></div><hr><div class="row"><div class="col-lg-12"><a class="btn btn-w-m btn-outline btn-primary pull-right" href="{{ buildUrl('/food/set') }}"><i class="fa fa-plus"></i>美食</a></div></div></form><table class="table table-bordered m-t"><thead><tr><th>美食名</th><th>分类</th><th>价格</th><th>库存</th><th>标签</th><th>操作</th></tr></thead><tbody>{% if list %}{% for item in list %}<tr><td>{{ item.name }}</td><td>{{ cat_mapping[ item.cat_id].name }}</td><td>{{ item.price }}</td><td>{{ item.stock }}</td><td>{{ item.tags }}</td><td><a href="{{ buildUrl('/food/info') }}?id={{ item.id }}"><i class="fa fa-eye fa-lg"></i></a>{% if item.status == 1 %}<a class="m-l" href="{{ buildUrl('/food/set') }}?id={{ item.id }}"><i class="fa fa-edit fa-lg"></i></a><a class="m-l remove" href="javascript:void(0);" data="{{ item.id }}"><i class="fa fa-trash fa-lg"></i></a>{% else %}<a class="m-l recover" href="javascript:void(0);" data="{{ item.id }}"><i class="fa fa-rotate-left fa-lg"></i></a>{% endif %}</td></tr>{% endfor %}{% else %}<tr><td colspan="6">暂无数据~~</td></tr>{% endif %}</tbody></table><!--分页代码已被封装到统一模板文件中-->{% include 'common/pagenation.html' %}</div>
</div>
{% endblock %}
{% block js %}
<script src="{{ buildStaticUrl('/js/food/index.js') }}"></script>
{% endblock %}
cat_mapping在 后端Food.py中有定义
此处的search_con是在web/controllers/member/Member.html中定义过的,只是不太确认这玩意啥时候成了全局变量,估计是登陆成功后这信息也随之保存在cookie之类的登录信息里了。
req = request.values
resp_data['search_con'] = req
search_con['status'] 是已经在Member.py中定义的req即request.values,被包装在resp_data里后,使用ops_render渲染并返回resp_data。
问题1: tmp_key|string 中|string的作用
此处从 cat_mapping获得的是一个整型,得需要转换成字符串
|string 是过滤函数,
web/templates/food/cat_set.html
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_food.html" %}
<div class="row m-t wrap_cat_set"><div class="col-lg-12"><h2 class="text-center">分类设置</h2><div class="form-horizontal m-t m-b"><div class="form-group"><label class="col-lg-2 control-label">分类名称:</label><div class="col-lg-10"><input type="text" name="name" class="form-control" placeholder="请输入分类名称~~" value="{{ info.name }}"></div></div><div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">权重:</label><div class="col-lg-10"><input type="text" name="weight" class="form-control" placeholder="请输入分类名称~~" value="{% if info and info.weight > 0 %}{{ info.weight }}{% else %}1{% endif%}"></div></div><div class="hr-line-dashed"></div><div class="form-group"><div class="col-lg-4 col-lg-offset-2"><input type="hidden" name="id" value="{{ info.id }}"><button class="btn btn-w-m btn-outline btn-primary save">保存</button></div></div></div></div>
</div>
{% endblock %}
{% block js %}
<script src="{{ buildStaticUrl('/js/food/cat_set.js') }}"></script>
{% endblock %}
这段代码是一个网页模板,使用了模板引擎语法。下面我将逐段解析代码的功能和作用:
-
<input type="text" name="name" class="form-control" placeholder="请输入分类名称~~" value="{{ info.name }}">
:这行代码定义了一个文本输入框元素,其中包含了一个名为"name"的属性和一个名为"info.name"的变量。 -
<div class="hr-line-dashed"></div>
:这行代码定义了一个带有样式类名的div元素,用于创建一条虚线。 -
<input type="text" name="weight" class="form-control" placeholder="请输入分类名称~~" value="{% if info and info.weight > 0 %}{{ info.weight }}{% else %}1{% endif%}">
:这行代码定义了一个文本输入框元素,其中包含了一个名为"weight"的属性和一个名为"info.weight"的变量。 -
<div class="hr-line-dashed"></div>
:这行代码定义了一个带有样式类名的div元素,用于创建一条虚线。 -
<div class="col-lg-4 col-lg-offset-2">
:这行代码定义了一个带有样式类名的div元素,并设置了偏移量。 -
<input type="hidden" name="id" value="{{ info.id }}">
:这行代码定义了一个隐藏的输入框元素,其中包含了一个名为"id"的属性和一个名为"info.id"的变量。 -
<button class="btn btn-w-m btn-outline btn-primary save>保存</button>
:这行代码定义了一个按钮元素,其中包含了一些样式类名。
以上是对代码的功能和作用的解析。这段代码主要是用于展示一个分类设置的表单页面,包含了分类名称、权重等输入框,并且有一个保存按钮。
web/static/js/cat_set.js
set.js里面写整个页面的一些操作
;
var food_cat_set_ops = {init:function(){this.eventBind();},eventBind:function(){$(".wrap_cat_set .save").click(function(){var btn_target = $(this);if( btn_target.hasClass("disabled") ){common_ops.alert("正在处理!!请不要重复提交~~");return;}var name_target = $(".wrap_cat_set input[name=name]");var name = name_target.val();var weight_target = $(".wrap_cat_set input[name=weight]");var weight = weight_target.val();if( name.length < 1 ){common_ops.tip( "请输入符合规范的分类名称~~",name_target );return false;}if( parseInt( weight ) < 1 ){common_ops.tip( "请输入符合规范的权重,并且至少要大于1~~",weight_target );return false;}btn_target.addClass("disabled");var data = {name: name,weight: weight,id:$(".wrap_cat_set input[name=id]").val()};$.ajax({url:common_ops.buildUrl( "/food/cat-set" ),type:'POST',data:data,dataType:'json',success:function( res ){btn_target.removeClass("disabled");var callback = null;if( res.code == 200 ){callback = function(){window.location.href = common_ops.buildUrl("/food/cat");}}common_ops.alert( res.msg,callback );}});});}
};$(document).ready( function(){food_cat_set_ops.init();
} );
这段代码是一个Python Flask应用中的一个接口函数,用于处理/cat-set接口的GET请求和POST请求。下面是对代码的解释:
-
引用中的代码是一个HTML模板文件,其中使用了{%block js %}和{% endblock %}来引入一个JavaScript文件。这个JavaScript文件的路径是通过buildStaticUrl函数生成的。
-
引用中的代码是一个Python蓝图(Blueprint)中的一个路由函数,用于处理/cat-set接口的请求。这个函数首先判断请求的方法是GET还是POST,然后根据不同的方法执行不同的逻辑。
-
如果请求的方法是GET,那么函数会获取请求参数中的id,并根据id查询数据库中对应的FoodCat对象。然后将查询到的对象和当前的cat(可能是一个全局变量)放入resp_data字典中。
-
最后,函数会调用ops_render函数渲染一个HTML模板文件(food/cat_set.html),并将resp_data作为参数传入。
-
在JavaScript代码中,food_cat_set_ops是一个对象,其中包含了一个init方法和一个eventBind方法。init方法用于初始化页面,eventBind方法用于绑定事件。
这段代码是一个jQuery的事件处理函数,当点击".wrap_cat_set .save"元素时触发。下面是对代码各部分的解释:
-
var btn_target = $(this);
:将当前点击的元素保存到变量btn_target
中。 -
if( btn_target.hasClass("disabled") ){...}
:判断btn_target
元素是否有类名为"disabled",如果有,则弹出提示信息并返回。 -
var name_target = $(".wrap_cat_set input[name=name]");
:选取类名为"wrap_cat_set"的元素下的name属性为"name"的input元素,并将其保存到变量name_target
中。 -
var name = name_target.val();
:获取name_target
元素的值,并将其保存到变量name
中。 -
var weight_target = $(".wrap_cat_set input[name=weight]");
:选取类名为"wrap_cat_set"的元素下的name属性为"weight"的input元素,并将其保存到变量weight_target
中。 -
var weight = weight_target.val();
:获取weight_target
元素的值,并将其保存到变量weight
中。 -
if( name.length < 1 ){...}
:判断name
的长度是否小于1,如果是,则弹出提示信息并返回。 -
if( parseInt( weight ) < 1 ){...}
:将weight
转换为整数,并判断是否小于1,如果是,则弹出提示信息并返回。 -
btn_target.addClass("disabled");
:给btn_target
元素添加类名"disabled"。 -
var data = {...};
:创建一个对象data
,包含name、weight和id属性,分别对应输入框的值和隐藏域的值。
这段代码的功能是在点击保存按钮时,获取输入框中的值,并进行一些验证,然后将获取到的数据保存到data
对象中。
web/templates/food/cat.html
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_food.html" %}
<div class="row"><div class="col-lg-12"><form class="form-inline wrap_search"><div class="row m-t p-w-m"><div class="form-group"><select name="status" class="form-control inline"><option value="-1">请选择状态</option>{% for tmp_key in status_mapping %}<option value="{{ tmp_key }}" {% if tmp_key == search_con['status'] %} selected {% endif %}>{{ status_mapping[ tmp_key ] }}</option>{% endfor %}</select></div></div><hr><div class="row"><div class="col-lg-12"><a class="btn btn-w-m btn-outline btn-primary pull-right"href="{{ buildUrl('/food/cat-set') }}"><i class="fa fa-plus"></i>分类</a></div></div></form><table class="table table-bordered m-t"><thead><tr><th>序号</th><th>分类名称</th><th>状态</th><th>权重</th><th>操作</th></tr></thead><tbody>{% if list %}{% for item in list %}<tr><td>{{ item.id }}</td><td>{{ item.name }}</td><td>{{ item.status_desc }}</td><td>{{ item.weight }}</td><td>{% if item.status == 1 %}<a class="m-l" href="{{ buildUrl('/food/cat-set') }}?id={{ item.id }}"><i class="fa fa-edit fa-lg"></i></a><a class="m-l remove" href="javascript:void(0);" data="{{ item.id }}"><i class="fa fa-trash fa-lg"></i></a>{% else %}<a class="m-l recover" href="javascript:void(0);" data="{{ item.id }}"><i class="fa fa-rotate-left fa-lg"></i></a>{% endif %}</td></tr>{% endfor %}{% else %}<tr><td colspan="5">暂无数据</td></tr>{% endif %}</tbody></table></div>
</div>
{% endblock %}
{% block js %}
<script src="{{ buildStaticUrl('/js/food/cat.js') }}"></script>
{% endblock %}
web/static/js/food/cat.js
;
var food_cat_ops = {init:function(){this.eventBind();},eventBind:function(){var that = this;$(".wrap_search select[name=status]").change(function(){$(".wrap_search").submit();});$(".remove").click( function(){that.ops( "remove",$(this).attr("data") );} );$(".recover").click( function(){that.ops( "recover",$(this).attr("data") );} );},ops:function( act,id ){var callback = {'ok':function(){$.ajax({url:common_ops.buildUrl( "/food/cat-ops" ),type:'POST',data:{act:act,id:id},dataType:'json',success:function( res ){var callback = null;if( res.code == 200 ){callback = function(){window.location.href = window.location.href;}}common_ops.alert( res.msg,callback );}});},'cancel':null};common_ops.confirm( ( act == "remove" ? "确定删除?":"确定恢复?" ), callback );}};$(document).ready( function(){food_cat_ops.init();
} );
web/templates/food/set.html
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_food.html" %}
<div class="row mg-t20 wrap_food_set" style=""><div class="col-lg-12" style=""><h2 class="text-center">设置</h2><div class="form-horizontal m-t" style=""><div class="form-group"><label class="col-lg-2 control-label">分类:</label><div class="col-lg-10"><select name="cat_id" class="form-control select2-hidden-accessible" tabindex="-1"aria-hidden="true"><option value="0">请选择分类</option>{% for item in cat_list %}<option value="{{ item.id }}" {% if item.id == info.cat_id %} selected {% endif %}>{{ item.name }}</option>{% endfor %}</select></div></div><div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">名称:</label><div class="col-lg-10"><input type="text" class="form-control" placeholder="请输入名称" name="name" value="{{ info.name }}"></div></div><div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">价格:</label><div class="col-lg-10"><input type="text" class="form-control" placeholder="请输入售价" name="price" value="{{ info.price }}"></div></div><div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">封面图:</label><div class="col-lg-10"><form class="upload_pic_wrap" target="upload_file" enctype="multipart/form-data" method="POST" action="{{ buildUrl('/upload/pic') }}"><div class="upload_wrap pull-left"><i class="fa fa-upload fa-2x"></i><input type="file" name="pic" accept="image/png, image/jpeg, image/jpg,image/gif"></div>{% if info and info.main_image %}<span class="pic-each"><img src="{{ buildImageUrl( info.main_image ) }}"/><span class="fa fa-times-circle del del_image" data="{{ info.main_image }}"></span></span>{% endif %}</form></div></div><div class="hr-line-dashed"></div><div class="form-group" style=""><label class="col-lg-2 control-label">描述:</label><div class="col-lg-10"><textarea id="editor" name="summary" style="height: 300px;">{{ info.summary }}</textarea></div></div><div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">库存:</label><div class="col-lg-2"><input type="text" name="stock" class="form-control" value="{{ info.stock }}"></div></div><div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">标签:</label><div class="col-lg-10"><input type="text" class="form-control" name="tags" value="{{ info.tags }}"></div></div><div class="hr-line-dashed"></div><div class="form-group"><div class="col-lg-4 col-lg-offset-2"><input type="hidden" name="id" value="{{ info.id }}"><button class="btn btn-w-m btn-outline btn-primary save">保存</button></div></div></div></div>
</div>
<iframe name="upload_file" class="hide"></iframe>
{% endblock %}
{% block css %}
<link href="{{ buildStaticUrl( '/plugins/select2/select2.min.css' ) }}" rel="stylesheet">
<link href="{{ buildStaticUrl( '/plugins/tagsinput/jquery.tagsinput.min.css' ) }}" rel="stylesheet">
{% endblock %}
{% block js %}
<script src="{{ buildStaticUrl( '/plugins/ueditor/ueditor.config.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/ueditor/ueditor.all.min.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/ueditor/lang/zh-cn/zh-cn.js' ) }}"></script><script src="{{ buildStaticUrl( '/plugins/select2/select2.pinyin.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/select2/zh-CN.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/select2/pinyin.core.js' ) }}"></script><script src="{{ buildStaticUrl( '/plugins/tagsinput/jquery.tagsinput.min.js' ) }}"></script><script src="{{ buildStaticUrl( '/js/food/set.js' ) }}"></script>
{% endblock %}
web/templates/food/info.html
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_food.html" %}
<style type="text/css">.wrap_info img {width: 70%;}
</style>
<div class="row m-t wrap_info"><div class="col-lg-12"><div class="row"><div class="col-lg-12"><div class="m-b-md"><a class="btn btn-outline btn-primary pull-right" href="{{ buildUrl('/food/set') }}?id={{ info.id }}"><i class="fa fa-pencil"></i>编辑</a><h2>美食信息</h2></div></div></div><div class="row"><div class="col-lg-12"><p class="m-t">美食名:{{ info.name }}</p><p>售价:{{ info.price }}</p><p>库存总量:{{ info.stock }}</p><p>图书标签:{{ info.tags }}</p><p>封面图:<img src="{{ buildImageUrl( info.main_image ) }}" style="width: 50px;height: 50px;"></p><p>描述:</p><p>{{ info.summary | safe }}</p><p></p></div></div><div class="row m-t"><div class="col-lg-12"><div class="panel blank-panel"><div class="panel-heading"><div class="panel-options"><ul class="nav nav-tabs"><li class="active"><a href="#tab-1" data-toggle="tab" aria-expanded="false">销售历史</a></li><li><a href="#tab-2" data-toggle="tab" aria-expanded="true">库存变更</a></li></ul></div></div><div class="panel-body"><div class="tab-content"><div class="tab-pane active" id="tab-1"><table class="table table-striped"><thead><tr><th>会员名称</th><th>购买数量</th><th>购买价格</th><th>订单状态</th></tr></thead><tbody><tr><td colspan="4">暂无销售记录</td></tr></tbody></table></div><div class="tab-pane" id="tab-2"><table class="table table-striped"><thead><tr><th>变更</th><th>备注</th><th>时间</th></tr></thead><tbody>{% if stock_change_list %}{% for item in stock_change_list %}<tr><td>{{ item.unit }}</td><td>{{ item.note }}</td><td>{{ item.created_time }}</td></tr>{% endfor %}{% else %}<tr><td colspan="3">暂无数据~~</td></tr>{% endif %}</tbody></table></div></div></div></div></div></div></div>
</div>
{% endblock %}
web/templates/food/set.html
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_food.html" %}
<div class="row mg-t20 wrap_food_set" style=""><div class="col-lg-12" style=""><h2 class="text-center">设置</h2><div class="form-horizontal m-t" style=""><div class="form-group"><label class="col-lg-2 control-label">分类:</label><div class="col-lg-10"><select name="cat_id" class="form-control select2-hidden-accessible" tabindex="-1"aria-hidden="true"><option value="0">请选择分类</option>{% for item in cat_list %}<option value="{{ item.id }}" {% if item.id == info.cat_id %} selected {% endif %}>{{ item.name }}</option>{% endfor %}</select></div></div><div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">名称:</label><div class="col-lg-10"><input type="text" class="form-control" placeholder="请输入名称" name="name" value="{{ info.name }}"></div></div><div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">价格:</label><div class="col-lg-10"><input type="text" class="form-control" placeholder="请输入售价" name="price" value="{{ info.price }}"></div></div>{#封面图部分代码#}<div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">封面图:</label><div class="col-lg-10"><form class="upload_pic_wrap" target="upload_file" enctype="multipart/form-data" method="POST" action="{{ buildUrl('/upload/pic') }}"><div class="upload_wrap pull-left"><i class="fa fa-upload fa-2x"></i><input type="file" name="pic" accept="image/png, image/jpeg, image/jpg,image/gif"></div>{% if info and info.main_image %}<span class="pic-each"><img src="{{ buildImageUrl( info.main_image ) }}"/><span class="fa fa-times-circle del del_image" data="{{ info.main_image }}"></span></span>{% endif %}</form></div></div><div class="hr-line-dashed"></div><div class="form-group" style=""><label class="col-lg-2 control-label">描述:</label><div class="col-lg-10"><textarea id="editor" name="summary" style="height: 300px;">{{ info.summary }}</textarea></div></div><div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">库存:</label><div class="col-lg-2"><input type="text" name="stock" class="form-control" value="{{ info.stock }}"></div></div><div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">标签:</label><div class="col-lg-10"><input type="text" class="form-control" name="tags" value="{{ info.tags }}"></div></div><div class="hr-line-dashed"></div><div class="form-group"><div class="col-lg-4 col-lg-offset-2"><input type="hidden" name="id" value="{{ info.id }}"><button class="btn btn-w-m btn-outline btn-primary save">保存</button></div></div></div></div>
</div>
<iframe name="upload_file" class="hide"></iframe>
{% endblock %}
{% block css %}
<link href="{{ buildStaticUrl( '/plugins/select2/select2.min.css' ) }}" rel="stylesheet">
<link href="{{ buildStaticUrl( '/plugins/tagsinput/jquery.tagsinput.min.css' ) }}" rel="stylesheet">
{% endblock %}
{% block js %}
<script src="{{ buildStaticUrl( '/plugins/ueditor/ueditor.config.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/ueditor/ueditor.all.min.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/ueditor/lang/zh-cn/zh-cn.js' ) }}"></script><script src="{{ buildStaticUrl( '/plugins/select2/select2.pinyin.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/select2/zh-CN.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/select2/pinyin.core.js' ) }}"></script><script src="{{ buildStaticUrl( '/plugins/tagsinput/jquery.tagsinput.min.js' ) }}"></script><script src="{{ buildStaticUrl( '/js/food/set.js' ) }}"></script>
{% endblock %}
问题1: python 中返回结果集 是什么意思
在Python中,返回结果集指的是函数或方法执行后返回的结果。当我们调用一个函数或方法时,它会执行一系列的操作,并最终返回一个值作为结果。这个返回的值可以是任何数据类型,例如整数、字符串、列表等。
返回结果集非常重要,因为它允许我们在程序中使用函数的输出来进行后续的操作。通过获取函数的返回结果,我们可以将其赋值给变量,进行条件判断,进行数学运算等等。
下面是一个简单的示例,演示了如何使用函数的返回结果:
def add_numbers(a, b):return a + bresult = add_numbers(3, 5)
print(result) # 输出:8
在上面的示例中,我们定义了一个名为add_numbers
的函数,它接受两个参数并返回它们的和。然后,我们调用这个函数并将返回的结果赋值给变量result
,最后打印出result
的值,即8。
通过返回结果集,我们可以方便地获取函数执行后的输出,并在程序中进行进一步的处理。
封面图部分代码:
这段代码是一个网页表单,用于上传图片并显示已上传的图片。具体作用如下:
-
这段代码包含了一个表单,其中包含了一个用于上传图片的文件输入框。用户可以选择图片文件并将其上传到服务器。上传的目标URL是
{{ buildUrl('/upload/pic') }}
,该URL由服务器端生成。上传的图片文件将被命名为pic
,并且只接受image/png
、image/jpeg
、image/jpg
和image/gif
这几种格式的图片文件。如果在表单中已经存在一个名为
info
的变量,并且该变量中包含了main_image
属性,则会显示一个已经上传的图片。该图片的URL由{{ buildImageUrl( info.main_image ) }}
生成。同时,还会显示一个删除按钮,用户可以点击该按钮删除已上传的图片。最后,代码中还包含了一个隐藏的
iframe
元素,用于接收上传图片的响应结果。
问题1:该代码中 target="upload_file"是什么
在给定的代码中,target="upload_file"
是用于指定表单提交的目标位置的属性。具体来说,它指定了一个名为"upload_file"的目标窗口或帧,用于接收表单提交的响应。在这种情况下,表单将被提交到名为"upload_file"的目标窗口或帧中,以便处理上传的图片。
HTML 表单
target | 规定 action 属性中地址的目标(默认:_self) |
也就是在此定位了发送目的地:
web/controllers/upload/pic 即 该目录Upload.py文件下装饰器
@route_upload.route("/pic",methods = [ "GET","POST" ])。
upload_file是一个iframe的名称,用于指定在表单提交时将结果加载到哪个iframe中。iframe是一个HTML元素,用于在网页中嵌入另一个网页。在这段代码中,upload_file被用作一个隐藏的iframe元素,用于接收表单提交的结果。通过将结果加载到该iframe中,可以实现异步上传文件的功能,而不会导致整个页面刷新。
问题2:该代码form中的值会被 服务器通过request.files获取到吗?
根据提供的引用内容,form中的值会被服务器通过request.files获取到。具体的代码如下所示:
<form class="upload_pic_wrap" target="upload_file" enctype="multipart/form-data" method="POST" action="{{ buildUrl('/upload/pic') }}"><input type="file" name="file"><input type="submit" value="上传">
</form>
在服务器端,可以通过request.files来获取上传的文件。例如,在Python的Flask框架中,可以使用以下代码来获取上传的文件:
from flask import Flask, requestapp = Flask(__name__)@app.route('/upload/pic', methods=['POST'])
def upload_pic():file = request.files['file']# 处理上传的文件return '文件上传成功'if __name__ == '__main__':app.run()
在上述代码中,通过request.files[‘file’]可以获取到上传的文件对象,然后可以对文件进行处理。
只不过在这里将单一多个的按钮合并为了一个总按钮save。
问题3:该代码form中target可以不设置吗?
form中的target属性用于指定表单提交后的响应结果在哪个窗口或框架中显示。如果不设置target属性,表单提交后会在当前窗口中显示响应结果。所以,可以不设置target属性,但是需要根据实际需求来决定是否设置。
web/static/js/food/set.js
;
var upload = {error: function (msg) {common_ops.alert(msg);},success: function (file_key) {if (!file_key) {return;}var html = '<img src="' + common_ops.buildPicUrl(file_key) + '"/>'+ '<span class="fa fa-times-circle del del_image" data="' + file_key + '"></span>';if ($(".upload_pic_wrap .pic-each").size() > 0) {$(".upload_pic_wrap .pic-each").html(html);} else {$(".upload_pic_wrap").append('<span class="pic-each">' + html + '</span>');}food_set_ops.delete_img();}
};
var food_set_ops = {init: function () {this.ue = null;this.eventBind();this.initEditor();this.delete_img();},eventBind: function () {var that = this;$(".wrap_food_set .upload_pic_wrap input[name=pic]").change(function () {$(".wrap_food_set .upload_pic_wrap").submit();});$(".wrap_food_set select[name=cat_id]").select2({language: "zh-CN",width: '100%'});$(".wrap_food_set input[name=tags]").tagsInput({width: 'auto',height: 40,onAddTag: function (tag) {},onRemoveTag: function (tag) {}});$(".wrap_food_set .save").click(function () {var btn_target = $(this);if (btn_target.hasClass("disabled")) {common_ops.alert("正在处理!!请不要重复提交~~");return;}var cat_id_target = $(".wrap_food_set select[name=cat_id]");var cat_id = cat_id_target.val();var name_target = $(".wrap_food_set input[name=name]");var name = name_target.val();var price_target = $(".wrap_food_set input[name=price]");var price = price_target.val();var summary = $.trim(that.ue.getContent());var stock_target = $(".wrap_food_set input[name=stock]");var stock = stock_target.val();var tags_target = $(".wrap_food_set input[name=tags]");var tags = $.trim(tags_target.val());if (parseInt(cat_id) < 1) {common_ops.tip("请选择分类~~", cat_id_target);return;}if (name.length < 1) {common_ops.alert("请输入符合规范的名称~~");return;}if (parseFloat(price) <= 0) {common_ops.tip("请输入符合规范的售卖价格~~", price_target);return;}if ($(".wrap_food_set .pic-each").size() < 1) {common_ops.alert("请上传封面图~~");return;}if (summary.length < 10) {common_ops.tip("请输入描述,并不能少于10个字符~~", price_target);return;}if (parseInt(stock) < 1) {common_ops.tip("请输入符合规范的库存量~~", stock_target);return;}if (tags.length < 1) {common_ops.alert("请输入标签,便于搜索~~");return;}btn_target.addClass("disabled");var data = {cat_id: cat_id,name: name,price: price,main_image: $(".wrap_food_set .pic-each .del_image").attr("data"),summary: summary,stock: stock,tags: tags,id: $(".wrap_food_set input[name=id]").val()};$.ajax({url: common_ops.buildUrl("/food/set"),type: 'POST',data: data,dataType: 'json',success: function (res) {btn_target.removeClass("disabled");var callback = null;if (res.code == 200) {callback = function () {window.location.href = common_ops.buildUrl("/food/index");}}common_ops.alert(res.msg, callback);}});});},initEditor: function () {var that = this;that.ue = UE.getEditor('editor', {toolbars: [['undo', 'redo', '|','bold', 'italic', 'underline', 'strikethrough', 'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', '|', 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 'selectall', '|', 'rowspacingtop', 'rowspacingbottom', 'lineheight'],['customstyle', 'paragraph', 'fontfamily', 'fontsize', '|','directionalityltr', 'directionalityrtl', 'indent', '|','justifyleft', 'justifycenter', 'justifyright', 'justifyjustify', '|', 'touppercase', 'tolowercase', '|','link', 'unlink'],['imagenone', 'imageleft', 'imageright', 'imagecenter', '|','insertimage', 'insertvideo', '|','horizontal', 'spechars', '|', 'inserttable', 'deletetable', 'insertparagraphbeforetable', 'insertrow', 'deleterow', 'insertcol', 'deletecol', 'mergecells', 'mergeright', 'mergedown', 'splittocells', 'splittorows', 'splittocols']],enableAutoSave: true,saveInterval: 60000,elementPathEnabled: false,zIndex: 4,serverUrl: common_ops.buildUrl('/upload/ueditor')});},delete_img: function () {$(".wrap_food_set .del_image").unbind().click(function () {$(this).parent().remove();});}
};$(document).ready(function () {food_set_ops.init();
});
Var upload
问题1: var upload = {} 中 upload是变量还是对象还是什么
根据提供的引用内容,变量upload
是一个对象。在JavaScript中,对象是一种复合数据类型,用于存储键值对。对象可以包含属性和方法,而变量则用于存储对象的引用。
在给变量upload
赋值时,使用了花括号{}
来创建一个新的对象,并将该对象赋值给变量upload
。通过这个对象,您可以访问和操作对象的属性和方法。
请注意,由于引用中没有提供完整的代码,因此无法确定upload
对象的具体内容和用途。如果您需要更多关于upload
对象的信息,请提供更多的上下文或代码片段。
success:function(file_key){ }
这段代码是一个成功上传文件后的回调函数。当文件上传成功后,会执行这段代码。代码的功能是将上传成功的文件显示在页面上,并提供删除按钮。具体解释如下:
-
首先,判断上传成功后返回的文件key是否为空,如果为空则直接返回,不执行后续代码。
-
创建一个变量html,用于存储要显示在页面上的图片和删除按钮的HTML代码。代码中使用了common_ops.buildPicUrl(file_key)函数来构建图片的URL,这个函数可能是用来拼接图片的完整URL地址。
-
判断页面上是否已经存在图片,
如果存在则将新的图片替换原有的图片,jQuery 设置内容和属性 | 菜鸟教程
如果不存在则将新的图片添加到页面上。 jQuery 添加元素 | 菜鸟教程
-
最后,调用food_set_ops.delete_img()函数,可能是用来绑定删除按钮的点击事件。
这段代码的作用是在文件上传成功后,将上传的图片显示在页面上,并提供删除按钮供用户删除图片。
问题1: .size() 是指什么?
$(".upload_pic_wrap .pic-each").size()
是用来获取具有upload_pic_wrap
类和pic-each
类的元素的数量。size()
方法返回匹配元素的数量。在这种情况下,它返回具有这两个类的元素的数量。
问题2:var htm 究竟 是什么?
html是指要插入到.upload_pic_wrap .pic-each
元素中的HTML代码。在这段代码中,如果.upload_pic_wrap .pic-each
元素存在,就会将html
变量中的HTML代码插入到该元素中。这样做可以动态地更新页面上的图片显示。
问题3: .html() 又是什么?
.html()是jQuery中的一个方法,用于获取或设置元素的HTML内容。jQuery 设置内容和属性 | 菜鸟教程
在这个例子中,$(“.upload_pic_wrap .pic-each”)是一个jQuery选择器,用于选中class为"upload_pic_wrap"的元素下的class为"pic-each"的元素。.html(html)则是将选中的元素的HTML内容设置为变量html的值。
var food_set_ops = { }
问题1: .trim() 方法的作用是什么?
jQuery.trim() 方法 | 菜鸟教程
$.trim() 函数用于去除字符串两端的空白字符。
$.trim() 函数将在 jQuery 3.5 及以上版本废弃,可以使用 JavaScript 原生的 String.prototype.trim 代替。
注意:$.trim()函数会移除字符串开始和末尾处的所有换行符,空格(包括连续的空格)和制表符。如果这些空白字符在字符串中间时,它们将被保留,不会被移除。
.trim() 方法用于去除字符串头尾的空格。它返回一个新的字符串,原始字符串不会被修改。下面是一个使用 .trim() 方法的例子:
var str = ' aaa ';
console.log('字符串原先为:' + str + ' 长度为:' + str.length);
var str1 = str.trim();
console.log('使用trim后字符串为:' + str1 + ' 长度为:' + str1.length);
输出结果为:
字符串原先为: aaa 长度为: 6
使用trim后字符串为:aaa 长度为: 3
可以看到,使用 .trim() 方法去除了字符串头尾的空格,并返回了一个新的字符串。原始字符串的长度为6,去除空格后的新字符串长度为3。
如果你只想去除字符串的右侧空格,可以使用 .trimRight() 方法。例如:
var str = ' aaa ';
var str_1 = str.trimRight();
console.log('使用trimRight后字符串为:' + str_1 + ' 长度为:' + str_1.length);
输出结果为:
使用trimRight后字符串为: aaa 长度为: 4
.trimRight() 方法只去除了字符串右侧的空格,返回了一个新的字符串。原始字符串的长度为6,去除右侧空格后的新字符串长度为4。
问题2: .tagsInput() 及其配置 和 附属函数onAddTag 和 onRemoveTag
jQuery标签插件tagsinput.js
可以通过调用addTag()和removeTag()函数来添加和删除标记。
initEditor: function(){}
这段代码使用了UEditor库来创建一个名为"editor"的编辑器实例。在初始化过程中,我们可以设置一些选项来自定义编辑器的行为。在这个例子中,我们设置了以下选项:
toolbars: []
:禁用了编辑器的工具栏。enableAutoSave: true
:启用了自动保存功能。saveInterval: 60000
:设置了自动保存的时间间隔为60秒。elementPathEnabled: false
:禁用了编辑器底部的元素路径显示。zIndex: 4
:设置了编辑器的层级为4。serverUrl: common_ops.buildUrl('/upload/ueditor')
:设置了上传文件的服务器地址。
通过调用UE.getEditor('editor', options)
方法,我们可以创建一个名为"editor"的编辑器实例,并将其保存在that.ue
变量中。
问题1:var that = this;的作用是什么?
当我们使用function的时候,它的作用域已经变了,使用var that = this;当后面this的作用域变了以后可以使用that指定作用域。
var that = this; 这行代码的作用是将当前对象的引用赋值给变量that。这样做的目的是在函数内部可以使用that来引用当前对象,而不会受到函数内部this指向的改变的影响。
这种做法常见于JavaScript中的回调函数中,因为回调函数中的this指向可能会发生改变,为了确保在回调函数中仍然能够访问到当前对象,可以将当前对象的引用赋值给一个变量,然后在回调函数中使用该变量来引用当前对象。
这种做法还可以解决一些闭包中的作用域问题,确保在闭包中能够正确访问到当前对象。
范例:在initEditor函数中,将当前对象的引用赋值给变量that,以便在函数内部可以使用that来引用当前对象。
delete_img:function(){ }
知识点0: jQuery 选择器
jQuery 选择器 | 菜鸟教程
jQuery 选择器允许您对 HTML 元素组或单个元素进行操作。
jQuery 选择器基于元素的 id、类、类型、属性、属性值等"查找"(或选择)HTML 元素。 它基于已经存在的 CSS 选择器,除此之外,它还有一些自定义的选择器。
jQuery 中所有选择器都以美元符号开头:$()。
知识点1:jQuery .unbind()
jQuery unbind() 方法 | 菜鸟教程
unbind() 方法移除被选元素的事件处理程序。
该方法能够移除所有的或被选的事件处理程序,或者当事件发生时终止指定函数的运行。
该方法也可以通过 event 对象取消绑定的事件处理程序。该方法也用于对自身内部的事件取消绑定(比如当事件已被触发一定次数之后,删除事件处理程序)。
注意:如果未规定参数,则 unbind() 方法会删除指定元素的所有事件处理程序。
注意:unbind() 方法适用于任意由 jQuery 添加的事件处理程序。
知识点2: jQuery .remove()
jQuery 删除元素 | 菜鸟教程
如需删除元素和内容,一般可使用以下两个 jQuery 方法:
- remove() - 删除被选元素(及其子元素)
- empty() - 从被选元素中删除子元素
知识点3: jQuery .parent()
jQuery 遍历 – 祖先 | 菜鸟教程
parent() 方法返回被选元素的直接父元素。
该方法只会向上一级对 DOM 树进行遍历。
下面的例子返回每个 <span> 元素的直接父元素:
$(document).ready(function(){$("span").parents();
});
UEditor 后台上传配置
web/controllers/upload/upload.py
# -*- coding: utf-8 -*-
from flask import Blueprint,request,jsonify
from application import app
import re,json
from common.libs.UploadService import UploadService
from common.libs.UrlManager import UrlManager
from common.models.Image import Image
route_upload = Blueprint('upload_page', __name__)'''
参考文章:https://segmentfault.com/a/1190000002429055
'''@route_upload.route("/ueditor",methods = [ "GET","POST" ])
def ueditor():req = request.valuesaction = req['action'] if 'action' in req else ''if action == "config":root_path = app.root_pathconfig_path = "{0}/web/static/plugins/ueditor/upload_config.json".format( root_path )with open( config_path,encoding="utf-8" ) as fp:try:config_data = json.loads( re.sub( r'\/\*.*\*/' ,'',fp.read() ) )except:config_data = {}return jsonify( config_data )if action == "uploadimage":return uploadImage()if action == "listimage":return listImage()return "upload"@route_upload.route("/pic",methods = [ "GET","POST" ])
def uploadPic():file_target = request.filesupfile = file_target['pic'] if 'pic' in file_target else Nonecallback_target = 'window.parent.upload'if upfile is None:return "<script type='text/javascript'>{0}.error('{1}')</script>".format( callback_target,"上传失败" )ret = UploadService.uploadByFile(upfile)if ret['code'] != 200:return "<script type='text/javascript'>{0}.error('{1}')</script>".format(callback_target, "上传失败:" + ret['msg'])return "<script type='text/javascript'>{0}.success('{1}')</script>".format(callback_target,ret['data']['file_key'] )def uploadImage():resp = { 'state':'SUCCESS','url':'','title':'','original':'' }file_target = request.filesupfile = file_target['upfile'] if 'upfile' in file_target else Noneif upfile is None:resp['state'] = "上传失败"return jsonify(resp)ret = UploadService.uploadByFile( upfile )if ret['code'] != 200:resp['state'] = "上传失败:" + ret['msg']return jsonify(resp)resp['url'] = UrlManager.buildImageUrl( ret['data']['file_key'] )return jsonify( resp )def listImage():resp = { 'state':'SUCCESS','list':[],'start':0 ,'total':0 }req = request.valuesstart = int( req['start']) if 'start' in req else 0page_size = int( req['size']) if 'size' in req else 20query = Image.queryif start > 0:query = query.filter( Image.id < start )list = query.order_by( Image.id.desc() ).limit( page_size ).all()images = []if list:for item in list:images.append( { 'url': UrlManager.buildImageUrl( item.file_key ) } )start = item.idresp['list'] = imagesresp['start'] = startresp['total'] = len( images )return jsonify( resp )
@route_upload.route("/ueditor", methods = ["GET","POST"])
这段代码是一个Flask路由函数,用于处理上传图片的请求。下面是对各部分代码的解析:
-
@route_upload.route("/ueditor",methods = [ "GET","POST" ])
- 这是一个装饰器,将函数
ueditor()
绑定到路由/ueditor
上,并指定支持的请求方法为GET和POST。
- 这是一个装饰器,将函数
-
root_path = app.root_path
- 这行代码获取Flask应用程序的根目录路径。
-
config_path = "{0}/web/static/plugins/ueditor/upload_config.json".format( root_path )
- 这行代码构建配置文件的路径,其中
{0}
会被替换为root_path
的值。
- 这行代码构建配置文件的路径,其中
这段代码是一个Flask路由处理函数,根据请求的不同action参数执行不同的操作。当action为"config"时,读取配置文件并返回配置数据;当action为"uploadimage"时,调用uploadImage函数处理图片上传;当action为"listimage"时,调用listImage函数列出已上传的图片。对于其他action参数,返回"upload"字符串。
知识点1: with open( config_path,encoding="utf-8" ) as fp:
- 这是一个文件操作语句,打开配置文件并将其赋值给变量
fp
。
这段代码是使用Python中的with语句来打开一个文件,并将文件对象赋值给变量fp。with语句可以自动管理文件的打开和关闭,无需手动调用close()方法来关闭文件。
在with语句块中,可以执行对文件的操作,例如读取文件内容、写入文件内容等。在with语句块结束后,文件会自动关闭,无论代码是否发生异常。
下面是一个示例函数,演示了如何使用with open语句来读取文件内容并返回结果:
def read_file(file_path):try:with open(file_path, encoding="utf-8") as fp:content = fp.read()return contentexcept FileNotFoundError:return "文件不存在"except:return "读取文件出错"
这个函数接受一个文件路径作为参数,使用with open语句打开文件,并使用read()方法读取文件内容。如果文件不存在,会捕获FileNotFoundError异常并返回"文件不存在";如果读取文件出错,会捕获其他异常并返回"读取文件出错"。
使用这个函数可以方便地读取文件内容,同时也避免了忘记关闭文件的问题。
知识点2: try: config_data = json.loads( re.sub( r'\/\*.*\*/' ,'',fp.read() ) ) except: config_data = {}
- 这是一个异常处理语句,尝试将读取的配置文件内容进行处理并解析为JSON格式,如果解析失败,则将
config_data
赋值为空字典。
该段代码是一个try-except语句块,用于解析JSON格式的配置文件。下面是对该段代码的详细解析:
- 首先,代码中使用了
json.loads()
函数来解析JSON格式的数据。json.loads()
函数将JSON字符串转换为Python对象。 re.sub()
函数用于替换字符串中的匹配项。在这里,使用正则表达式r'\/\*.*\*/'
匹配注释块,并将其替换为空字符串。fp.read()
读取文件内容,并将其作为参数传递给re.sub()
函数进行处理。try
关键字表示尝试执行下面的代码块。- 如果解析成功,将解析结果赋值给
config_data
变量。 - 如果解析失败,即出现异常,
except
关键字后面的代码块将被执行。 - 在这里,如果解析失败,将
config_data
赋值为空字典{}
。 - 无论解析是否成功,最后都会执行下面的代码。
知识点3:{}在python中是对象还是字典还是什么
在Python中,{}可以表示多种数据结构,包括字典和集合。具体取决于{}的上下文使用方式。
-
如果{}中包含键值对,例如{“name”: “zhangsan”, “age”: 26},那么它表示一个字典。字典是一种无序的键值对集合,通过键来索引对应的值。在字典中,键必须是唯一的,而值可以重复。
-
如果{}中没有键值对,例如{1, 2, 3},那么它表示一个集合。集合是一种无序且不重复的元素集合。集合可以用于去除重复元素、判断元素是否存在等操作。
所以,{}在Python中既可以表示字典,也可以表示集合,具体取决于上下文的使用方式。
@route_upload.route("/pic", methods=["GET","POST"])
def uploadPic()
这段代码是一个Python的路由函数,用于处理上传图片的请求。下面是对代码的解析:
-
file_target = request.files
:request
是一个全局对象,用于获取当前请求的信息。request.files
是一个字典,包含了上传的文件对象。 -
upfile = file_target['pic'] if 'pic' in file_target else None
:这行代码用于获取上传的文件对象。如果file_target
字典中存在键为'pic'
的项,则将其赋值给upfile
变量,否则将upfile
赋值为None
。 -
callback_target = 'window.parent.upload'
:将字符串'window.parent.upload'
赋值给callback_target
变量。 [此处的upload应该值得是set.js中的var upload]
callback_target = ‘window.parent.upload’ 是一个字符串变量,它的值是 ‘window.parent.upload’。这个变量的作用是指定一个回调函数,当上传图片成功后,会调用这个回调函数来处理上传成功的逻辑。具体来说,‘window.parent.upload’ 是一个 JavaScript 中的函数,它是在父窗口中定义的。通过调用这个函数,可以在上传成功后执行一些操作,比如更新页面内容或显示上传成功的提示信息。 -
return "<script type='text/javascript'>{0}.error('{1}')</script>".format( callback_target,"上传失败" )
:返回一个包含JavaScript代码的字符串,该代码会调用callback_target
的error
方法,并传入字符串"上传失败"
作为参数。
该段代码是使用Python的字符串格式化方法来生成一个JavaScript脚本。代码中使用了format()
方法来将callback_target
和"上传失败"
两个变量的值插入到字符串中的占位符{0}
和{1}
中。
具体解析如下:
<script type='text/javascript'>
:这是JavaScript脚本的起始标签。{0}.error('{1}')
:这是JavaScript代码,其中{0}
表示callback_target
变量的值,.error()
是一个JavaScript对象的方法,'{1}'
表示"上传失败"
字符串。</script>
:这是JavaScript脚本的结束标签。
最终生成的JavaScript脚本是用于在前端页面中显示一个错误信息,其中callback_target
是一个JavaScript对象,调用了其error()
方法,并将"上传失败"
作为参数传递给该方法。
-
ret = UploadService.uploadByFile(upfile)
:调用UploadService
的uploadByFile
方法,将upfile
作为参数传入,并将返回值赋值给ret
变量。 -
return "<script type='text/javascript'>{0}.error('{1}')</script>".format(callback_target, "上传失败:" + ret['msg'])
:返回一个包含JavaScript代码的字符串,该代码会调用callback_target
的error
方法,并传入字符串"上传失败:" + ret['msg']
作为参数。 -
return "<script type='text/javascript'>{0}.success('{1}')</script>".format(callback_target,ret['data']['file_key'] )
:返回一个包含JavaScript代码的字符串,该代码会调用callback_target
的success
方法,并传入ret['data']['file_key']
作为参数
问题1: window.parent.upload 是什么意思?
window和parent是JavaScript中的两个特殊对象。它们分别代表当前窗口和父窗口。
-
window对象代表当前窗口,它是全局对象,可以通过window关键字来访问。window对象包含了当前窗口的所有属性和方法,例如document、location、history等。可以使用window对象来操作当前窗口的各种属性和方法。
-
parent对象代表父窗口,它是指包含当前窗口的上一级窗口。在嵌套的框架或iframe中,每个框架或iframe都有自己的window对象,而parent对象指向包含当前框架或iframe的窗口。通过parent对象,可以在当前窗口中访问父窗口的属性和方法。
-
HTML <iframe> 标签
在给定的代码中,callback_target = 'window.parent.upload’将一个字符串赋值给callback_target变量。这个字符串表示回调函数的目标,其中window.parent表示父窗口,upload表示父窗口中的一个名为upload的函数。这样,当上传成功或失败时,会调用父窗口中的upload函数来处理相应的结果。
def uploadImage(): 上传图像的功能
根据ueditor第三方软件官网信息:
因为ueditor需要resp这样的字典返回形式,所以我们按照其规矩好的需求元素返回。
接下来我们开始设置真正的上传图像的功能:
首先,通过request.files从客户端获取到上传的图像文件【客户端怎么上传图像文件?使用什么函数?】,并将该文件定义名为file_target
接下来,编程浪子老师先带领分析了一下file_target的值,然后才能根据值转换为ueditor要求的字典返回形式。
知识点1: file_target 的值
app.logger.info(file_target)
[2018-07-17 11:39:14, 414] INFO in Upload: ImmutableMultiDict([('upfile', <FileStorage:'food.jpg' ('image/jpeg')>)])
这段Python代码是用于解析上传文件的请求参数。根据代码中的日志信息,可以看出请求参数是一个ImmutableMultiDict对象,其中包含了一个键为’upfile’的文件对象。文件对象的名称是’food.jpg’,类型是’image/jpeg’。
这段代码的作用是将上传的文件解析为一个ImmutableMultiDict对象,并打印出文件的名称和类型。
FileStorage是一个类,用于表示上传的文件。它是Flask框架中的一个对象,用于处理文件上传。FileStorage对象包含了上传文件的相关信息,例如文件名、文件类型、文件大小等。在给定的引用中,FileStorage对象表示上传的文件为food.jpg,文件类型为image/jpeg。通过访问FileStorage对象的属性和方法,可以获取和处理上传文件的相关信息。
问题0: 这里面upfile是什么值?file_target['pic']中pic键又是从何而来?
从set.html可以发现pic的键是这个input标签
<input type="file" name="pic" accept="image/png, image/jpeg, image/jpg,image/gif">
file_target['pic']就是这个上传的文件对象了。
这里面file_target['pic']就是从html代码中来的对吗
是的,file_target['pic']
是从HTML代码中获取的。在HTML代码中,有一个文件上传的表单,其中的name="pic"
对应着file_target['pic']
。当用户选择了一个文件后,该文件会被上传到服务器,并通过request.files
获取到。然后,可以通过file_target['pic']
来获取上传的文件对象。如果用户没有选择文件,则file_target['pic']
为None
。
问题1: ImmutableMultiDict对象 是什么意思
ImmutableMultiDict对象是一个不可变的多值字典对象,它是Flask框架中用于存储HTTP请求数据的一种数据结构。它类似于Python中的字典,但与普通字典不同的是,ImmutableMultiDict对象可以存储多个相同键的值,并且不可修改。它通常用于存储表单数据或文件上传数据。
下面是一个演示ImmutableMultiDict对象的例子:
from werkzeug.datastructures import ImmutableMultiDict# 创建一个ImmutableMultiDict对象
data = ImmutableMultiDict([('name', 'zhangsan'), ('age', '18'), ('age', '20')])# 获取单个键的值
name = data.get('name')
print("Name:", name) # 输出:Name: zhangsan# 获取所有键的值
age = data.getlist('age')
print("Age:", age) # 输出:Age: ['18', '20']
在上面的例子中,我们创建了一个ImmutableMultiDict对象,并使用get()方法获取了单个键的值,使用getlist()方法获取了所有键的值。由于ImmutableMultiDict对象是不可变的,因此无法直接修改其中的值。
问题2:def uploadImage(): resp = { 'state':'SUCCESS','url':'','title':'','original':'' } 中resp是字典吗
是的,resp是一个字典。字典是Python中的一种数据结构,用于存储键值对。在这个例子中,resp字典包含了四个键值对:state、url、title和original。每个键值对由冒号分隔,键和值之间用逗号分隔。你可以通过键来访问字典中的值。
即确认收到前方客户端传来的图片信息后,执行真正的图片上传动作
ret = UploadService.uploadByFile( upfile )
这里我们对 上传动作 进行了单独地封装为 UploadService,因为该动作通用并且不拘束于单一图像功能的上传,在其他地方例如封面图像上传也可以用到上传动作。
def listImage():
问题1:start 和total的作用是什么?
start
和total
变量的作用如下:
-
start
:start
变量用于指定从数据库中获取照片数据的起始位置。它是一个整数值,表示上一次请求返回的最后一张照片的ID。通过将start
值传递给数据库查询,可以确保每次请求返回的照片数据不会重复。 -
total
:total
变量用于记录满足查询条件的照片总数。它是一个整数值,表示数据库中满足查询条件的照片总数。通过将total
值返回给前端,可以让前端知道满足查询条件的照片总数,以便进行分页等操作。
query = Image.queryif start > 0:query = query.filter( Image.id < start )
分页操作:改变id值来实现分页操作 两种方案一种是offset 一种是id
id值可以增加查询速度
这段代码是一个Python代码片段,它使用了一个名为Image
的查询对象。首先,它将query
变量设置为Image.query
,这表示创建了一个查询对象,用于从数据库中检索Image
对象的数据。
接下来,如果start
大于0,那么代码会执行一个过滤操作,使用query.filter()
方法来添加一个过滤条件。在这个例子中,过滤条件是Image.id < start
,它表示只选择id
小于start
的Image
对象。
总结起来,这段代码的作用是创建一个查询对象,并根据条件过滤出符合条件的Image
对象。
因为是desc()倒叙排列,即一开始有50张照片则伴随着start的减弱显示的照片id也会减弱,实现伴随着鼠标滚轮的转动实现即时动态快速过滤查询。
另一种方式 offset分页展示实现代码
page = start if start > 0 else 1
offset = (page -1)*page_sizelist = query.order_by(Image.id.desc()).offset(start).limit(page_size).all()resp['list'] = images
resp['start'] = start + 1
resp['total'] = len( images )
return jsonify( resp )
config/base_setting.py 上传文件的配置信息
UPLOAD = {'ext':[ 'jpg','gif','bmp','jpeg','png' ],'prefix_path':'/web/static/upload/','prefix_url':'/static/upload/'
}
common/libs/Helper.py
'''
获取当前时间
'''
def getCurrentDate( format = "%Y-%m-%d %H:%M:%S"):#return datetime.datetime.now().strftime( format )return datetime.datetime.now()
这段代码定义了一个名为getCurrentDate的函数,该函数有一个可选参数format,默认值为"%Y-%m-%d %H:%M:%S"。函数内部使用datetime模块来获取当前日期和时间,并将其组合成一个datetime对象。然后,函数返回这个datetime对象。
这段代码的注释部分是一个被注释掉的代码行,它使用strftime方法将datetime对象格式化为指定的字符串格式,然后返回格式化后的字符串。但是,当前代码中被注释掉了,所以函数返回的是一个datetime对象,而不是格式化后的字符串。
这段代码的作用是获取当前的日期和时间,并以datetime对象的形式返回。如果需要将其格式化为指定的字符串格式,可以取消注释并修改注释中的代码行。
common/libs/UploadService.py
# -*- coding: utf-8 -*-
from werkzeug.utils import secure_filename
from application import app,db
from common.libs.Helper import getCurrentDate
import datetime
import os,stat,uuid
from common.models.Image import Image
class UploadService(): #定义UploadService类:@staticmethoddef uploadByFile( file ): #定义uploadByFile静态方法config_upload = app.config['UPLOAD'] #获取上传文件的配置信息:resp = { 'code':200,'msg':'操作成功~~','data':{} } #初始化响应结果:filename = secure_filename( file.filename ) #获取上传文件的文件名和扩展名:ext = filename.rsplit(".",1)[1]if ext not in config_upload['ext']: #检查文件扩展名是否在允许的范围内:resp['code'] = -1resp['msg'] = "不允许的扩展类型文件"return resproot_path = app.root_path + config_upload['prefix_path'] #获取文件保存的根目录路径:#不使用getCurrentDate创建目录,为了保证其他写的可以用,这里改掉,服务器上好像对时间不兼容file_dir = datetime.datetime.now().strftime("%Y%m%d") #创建文件保存的目录:save_dir = root_path + file_dirif not os.path.exists( save_dir ):os.mkdir( save_dir )os.chmod( save_dir,stat.S_IRWXU | stat.S_IRGRP | stat.S_IRWXO ) #设置目录的权限。file_name = str( uuid.uuid4() ).replace("-","") + "." + ext #生成文件名并保存文件:file.save( "{0}/{1}".format( save_dir,file_name ) )model_image = Image() #创建一个Image对象并保存到数据库:model_image.file_key = file_dir + "/" + file_namemodel_image.created_time = getCurrentDate()db.session.add( model_image)db.session.commit()resp['data'] = {'file_key': model_image.file_key}return resp
知识点1: os.mkdir创建文件 及 os.chmod更改权限
if not os.path.exists( save_dir ):os.mkdir( save_dir )os.chmod( save_dir,stat.S_IRWXU | stat.S_IRGRP | stat.S_IRWXO )
代码首先使用os.path.exists()
函数检查目录save_dir
是否存在。如果目录不存在,则使用os.mkdir()
函数创建该目录。然后,使用os.chmod()
函数设置目录的权限。
chmod n. 更改文件属性;档案权限,修改文件权限;改变文件存取方式
Python3 os.chmod() 方法 | 菜鸟教程 更改权限
os.chmod()
函数用于更改文件或目录的权限。它接受三个参数:path
表示要更改权限的文件或目录的路径,mode
表示要设置的权限模式,dir_fd
和follow_symlinks
是可选参数。
在这段代码中,os.chmod()
函数的第一个参数是save_dir
,即要更改权限的目录路径。
第二个参数stat.S_IRWXU | stat.S_IRGRP | stat.S_IRWXO
是一个权限模式,它使用了位运算符|
将三个权限组合在一起。
stat.S_IRWXU
表示用户(拥有者)具有读、写和执行权限,
stat.S_IRGRP
表示组用户具有读权限,
stat.S_IRWXO
表示其他用户具有读、写和执行权限。
因此,这段代码的作用是如果目录save_dir
不存在,则创建该目录,并将其权限设置为拥有者具有读、写和执行权限,组用户具有读权限,其他用户具有读、写和执行权限。
知识点2: 时间设置
file_dir
是一个变量,它的值是当前日期的字符串形式,格式为"%Y%m%d"。具体来说,它使用datetime.datetime.now()
函数获取当前日期和时间,然后使用strftime()
方法将日期格式化为"%Y%m%d"的字符串。
知识点3: 文件名读取 及 格式信息获得
file_name = str( uuid.uuid4() ).replace("-","") + "." + ext #生成文件名并保存文件:file.save( "{0}/{1}".format( save_dir,file_name ) )
这段代码是用来生成一个唯一的文件名并保存文件的。具体的步骤如下:
- 使用
uuid.uuid4()
函数生成一个UUID(通用唯一标识符)。 - 使用
str()
函数将UUID转换为字符串。 - 使用
replace()
函数将字符串中的"-“替换为空字符串,以去除UUID中的”-"。 - 使用字符串拼接的方式将生成的文件名与文件扩展名拼接在一起,得到完整的文件名。
- 使用
file.save()
函数将文件保存到指定的目录中。
{0}/{1}
是一个字符串格式化的语法,用于将两个变量的值插入到字符串中的占位符位置。其中,{0}
和 {1}
分别表示第一个和第二个变量。
在给定的代码中,{0}/{1}
表示一个字符串,其中 {0}
和 {1}
分别会被 save_dir
和 file_name
的值替换。这样就可以生成一个完整的文件路径,用于保存文件。
举个例子,假设 save_dir
的值是 "/path/to/directory"
,file_name
的值是 "example.jpg"
,那么"{0}/{1}".format(save_dir, file_name)
将会被替换为 "/path/to/directory/example.jpg"
。
这种字符串格式化的语法可以用于动态地生成文件路径、URL、日志消息等等。它提供了一种灵活的方式来将变量的值插入到字符串中。
如图图片上传成功
common/libs/UrlManager.py
定义一个静态方法用于展示上传到数据库的照片所用的路径函数
class UrlManager(object):def __init__(self):pass...@staticmethoddef buildImageUrl(path):app_config = app.config['APP']url = app_config['domain'] + app.config['UPLOAD']['prefix_url'] + pathreturn url
config/base_setting.py
APP = {'domain':'http://192.168.10.10:8999'
}
common/models/Image.py 数据库
CREATE TABLE `images` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`file_key` varchar(60) NOT NULL DEFAULT '' COMMENT '文件名',`created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
flask-sqlacodegen 'mysql://root:root@127.0.0.1/food_db' --tables images --outfile "common/models/Image.py" --flask
web/controllers/food/Food.py
# -*- coding: utf-8 -*-
from flask import Blueprint,request,jsonify,redirect
from common.libs.Helper import ops_render,getCurrentDate,iPagination,getDictFilterField
from application import app,db
from common.models.food.Food import Food
from common.models.food.FoodCat import FoodCat
from common.models.food.FoodStockChangeLog import FoodStockChangeLog
from common.libs.UrlManager import UrlManager
from common.libs.food.FoodService import FoodService
from decimal import Decimal
from sqlalchemy import or_
route_food = Blueprint( 'food_page',__name__ )@route_food.route( "/index" )
def index():resp_data = {}req = request.valuespage = int(req['p']) if ('p' in req and req['p']) else 1query = Food.queryif 'mix_kw' in req:rule = or_(Food.name.ilike("%{0}%".format(req['mix_kw'])), Food.tags.ilike("%{0}%".format(req['mix_kw'])))query = query.filter( rule )if 'status' in req and int( req['status'] ) > -1 :query = query.filter( Food.status == int( req['status'] ) )if 'cat_id' in req and int( req['cat_id'] ) > 0 :query = query.filter( Food.cat_id == int( req['cat_id'] ) )page_params = {'total':query.count(),'page_size': app.config['PAGE_SIZE'],'page':page,'display':app.config['PAGE_DISPLAY'],'url': request.full_path.replace("&p={}".format(page),"")}pages = iPagination( page_params )offset = ( page - 1 ) * app.config['PAGE_SIZE']list = query.order_by( Food.id.desc() ).offset( offset ).limit( app.config['PAGE_SIZE'] ).all()cat_mapping = getDictFilterField( FoodCat,FoodCat.id,"id",[] )resp_data['list'] = listresp_data['pages'] = pagesresp_data['search_con'] = reqresp_data['status_mapping'] = app.config['STATUS_MAPPING']resp_data['cat_mapping'] = cat_mappingresp_data['current'] = 'index'return ops_render( "food/index.html",resp_data )@route_food.route( "/info" )
def info():resp_data = {}req = request.argsid = int(req.get("id", 0))reback_url = UrlManager.buildUrl("/food/index")if id < 1:return redirect( reback_url )info = Food.query.filter_by( id =id ).first()if not info:return redirect( reback_url )stock_change_list = FoodStockChangeLog.query.filter( FoodStockChangeLog.food_id == id )\.order_by( FoodStockChangeLog.id.desc() ).all()resp_data['info'] = inforesp_data['stock_change_list'] = stock_change_listresp_data['current'] = 'index'return ops_render( "food/info.html",resp_data )@route_food.route( "/set" ,methods = [ 'GET','POST'] )
def set():if request.method == "GET":resp_data = {}req = request.argsid = int( req.get('id',0) )info = Food.query.filter_by( id = id ).first()if info and info.status != 1:return redirect( UrlManager.buildUrl("/food/index") )cat_list = FoodCat.query.all()resp_data['info'] = inforesp_data['cat_list'] = cat_listresp_data['current'] = 'index'return ops_render( "food/set.html" ,resp_data)resp = {'code': 200, 'msg': '操作成功~~', 'data': {}}req = request.valuesid = int(req['id']) if 'id' in req and req['id'] else 0cat_id = int(req['cat_id']) if 'cat_id' in req else 0name = req['name'] if 'name' in req else ''price = req['price'] if 'price' in req else ''main_image = req['main_image'] if 'main_image' in req else ''summary = req['summary'] if 'summary' in req else ''stock = int(req['stock']) if 'stock' in req else ''tags = req['tags'] if 'tags' in req else ''if cat_id < 1:resp['code'] = -1resp['msg'] = "请选择分类~~"return jsonify(resp)if name is None or len(name) < 1:resp['code'] = -1resp['msg'] = "请输入符合规范的名称~~"return jsonify(resp)if not price or len( price ) < 1:resp['code'] = -1resp['msg'] = "请输入符合规范的售卖价格~~"return jsonify(resp)price = Decimal(price).quantize(Decimal('0.00'))if price <= 0:resp['code'] = -1resp['msg'] = "请输入符合规范的售卖价格~~"return jsonify(resp)if main_image is None or len(main_image) < 3:resp['code'] = -1resp['msg'] = "请上传封面图~~"return jsonify(resp)if summary is None or len(summary) < 3:resp['code'] = -1resp['msg'] = "请输入图书描述,并不能少于10个字符~~"return jsonify(resp)if stock < 1:resp['code'] = -1resp['msg'] = "请输入符合规范的库存量~~"return jsonify(resp)if tags is None or len(tags) < 1:resp['code'] = -1resp['msg'] = "请输入标签,便于搜索~~"return jsonify(resp)food_info = Food.query.filter_by(id=id).first()before_stock = 0if food_info:model_food = food_infobefore_stock = model_food.stockelse:model_food = Food()model_food.status = 1model_food.created_time = getCurrentDate()model_food.cat_id = cat_idmodel_food.name = namemodel_food.price = pricemodel_food.main_image = main_imagemodel_food.summary = summarymodel_food.stock = stockmodel_food.tags = tagsmodel_food.updated_time = getCurrentDate()db.session.add(model_food)ret = db.session.commit()FoodService.setStockChangeLog( model_food.id,int(stock) - int(before_stock),"后台修改" )return jsonify(resp)@route_food.route( "/cat" )
def cat():resp_data = {}req = request.valuesquery = FoodCat.queryif 'status' in req and int( req['status'] ) > -1:query = query.filter( FoodCat.status == int( req['status'] ) )list = query.order_by( FoodCat.weight.desc(),FoodCat.id.desc() ).all()resp_data['list'] = listresp_data['search_con'] = reqresp_data['status_mapping'] = app.config['STATUS_MAPPING']resp_data['current'] = 'cat'return ops_render( "food/cat.html",resp_data )@route_food.route( "/cat-set",methods = [ "GET","POST" ] )
def catSet():if request.method == "GET":resp_data = {}req = request.argsid = int(req.get("id", 0))info = Noneif id:info = FoodCat.query.filter_by( id = id ).first()resp_data['info'] = inforesp_data['current'] = 'cat'return ops_render( "food/cat_set.html" ,resp_data )resp = {'code': 200, 'msg': '操作成功~~', 'data': {}}req = request.valuesid = req['id'] if 'id' in req else 0name = req['name'] if 'name' in req else ''weight = int( req['weight'] ) if ( 'weight' in req and int( req['weight']) > 0 ) else 1if name is None or len( name ) < 1:resp['code'] = -1resp['msg'] = "请输入符合规范的分类名称~~"return jsonify( resp )food_cat_info = FoodCat.query.filter_by( id = id ).first()if food_cat_info:model_food_cat = food_cat_infoelse:model_food_cat = FoodCat()model_food_cat.created_time = getCurrentDate()model_food_cat.name = namemodel_food_cat.weight = weightmodel_food_cat.updated_time = getCurrentDate()db.session.add(model_food_cat)db.session.commit()return jsonify( resp )@route_food.route("/cat-ops",methods = [ "POST" ])
def catOps():resp = {'code': 200, 'msg': '操作成功~~', 'data': {}}req = request.valuesid = req['id'] if 'id' in req else 0act = req['act'] if 'act' in req else ''if not id :resp['code'] = -1resp['msg'] = "请选择要操作的账号~~"return jsonify(resp)if act not in [ 'remove','recover' ] :resp['code'] = -1resp['msg'] = "操作有误,请重试~~"return jsonify(resp)food_cat_info = FoodCat.query.filter_by( id= id ).first()if not food_cat_info:resp['code'] = -1resp['msg'] = "指定分类不存在~~"return jsonify(resp)if act == "remove":food_cat_info.status = 0elif act == "recover":food_cat_info.status = 1food_cat_info.update_time = getCurrentDate()db.session.add( food_cat_info )db.session.commit()return jsonify(resp)@route_food.route("/ops",methods=["POST"])
def ops():resp = { 'code':200,'msg':'操作成功~~','data':{} }req = request.valuesid = req['id'] if 'id' in req else 0act = req['act'] if 'act' in req else ''if not id :resp['code'] = -1resp['msg'] = "请选择要操作的账号~~"return jsonify(resp)if act not in [ 'remove','recover' ]:resp['code'] = -1resp['msg'] = "操作有误,请重试~~"return jsonify(resp)food_info = Food.query.filter_by( id = id ).first()if not food_info:resp['code'] = -1resp['msg'] = "指定美食不存在~~"return jsonify(resp)if act == "remove":food_info.status = 0elif act == "recover":food_info.status = 1food_info.updated_time = getCurrentDate()db.session.add(food_info)db.session.commit()return jsonify( resp )
@route_food.route("/index")
cat_mapping = getDictFilterField( FoodCat,FoodCat.id,"id",[] )
@route_food.route(/set", methods=["GET","POST"])
这段代码是一个Flask路由函数,用于处理GET和POST请求。根据请求的方法不同,函数会执行不同的逻辑。
对于GET请求,函数会首先获取请求参数中的id,并根据id查询数据库中对应的食品信息。如果查询到的信息存在且状态不为1,则会重定向到食品列表页面。接着,函数会查询所有的食品分类,并将查询结果和食品信息添加到响应数据中。最后,函数会渲染并返回"food/set.html"模板页面。
对于POST请求,函数会首先获取请求参数中的id、cat_id、name、price、main_image、summary和stock。接着,函数会根据这些参数进行相应的处理。最后,函数会返回一个包含操作结果的JSON响应。
这段代码是一个条件判断的示例,根据不同的条件返回不同的结果。具体来说,它首先检查请求中是否存在’tags’字段,如果存在则将其赋值给变量tags,否则将tags赋值为空字符串。然后,它依次检查cat_id、name、price、main_image、summary和stock这些变量的取值是否符合规范,如果不符合规范,则返回一个包含错误代码和错误消息的JSON响应。
这段代码的作用是对请求中的一些字段进行验证,确保它们的取值符合要求。如果有任何一个字段的取值不符合要求,就会返回一个包含错误信息的JSON响应。
这段代码是一个后端接口的实现,用于处理前端传递过来的数据并进行相应的操作。具体来说,这段代码实现了以下功能:
- 判断是否传入了标签,如果没有传入或者标签长度小于1,则返回错误信息。
- 根据传入的id查询食品信息,如果存在则更新该食品的信息,如果不存在则创建一个新的食品。
- 更新食品的分类id、名称、价格、主图、摘要、库存和标签,并更新更新时间。
- 将食品信息添加到数据库中,并提交事务。
- 调用FoodService的setStockChangeLog方法,记录库存变动日志。
- 返回处理结果。
这段代码的主要作用是处理食品信息的增加或修改,并记录库存变动日志。在前端接收到支付链接后,可以调用这个接口来更新食品信息。
问题1: id = int( req.get('id',0) ) 中的0代表什么意思
在代码 id = int( req.get('id',0) )
中, 0
是一个默认值。如果 req
字典中不存在键为 'id'
的值,或者该值无法转换为整型,那么 id
变量将被赋值为 0
。这样做的目的是在获取 'id'
值时,如果出现错误或者缺失,可以提供一个默认值以避免程序出错。
问题2: 为什么将字典 resp_data
中的键 'current'
的值设置为字符串 'index'?
函数中 resp['current']= 'index' resp['current']= 'cat' 有什么区别和作用?
通过查找,在web/templates/food/index.html中的选项卡功能代码中:
<ul class="nav nav-pills"><li class="current"><a href="{{ buildUrl('/food/index') }}">美食列表</a></li><li><a href="{{ buildUrl('/food/cat') }}">分类列表</a></li></ul>
问题3: Decimal()的作用是什么?
这段代码的作用是将变量price的值转换为Decimal类型,并将其保留两位小数。具体解析如下:
price = Decimal(price).quantize(Decimal('0.00'))
Decimal(price)
:将变量price转换为Decimal类型。Decimal是Python中用于处理精确小数运算的模块。.quantize(Decimal('0.00'))
:使用quantize方法对转换后的Decimal对象进行舍入操作,保留两位小数。参数Decimal(‘0.00’)表示要保留的小数位数。
例如,如果price的值为10.5678,经过上述代码处理后,price的值将变为10.57。
common/libs/food/FoodService.py
# -*- coding: utf-8 -*-
from application import app,db
from common.models.food.FoodStockChangeLog import FoodStockChangeLog
from common.models.food.Food import Food
from common.libs.Helper import getCurrentDate
class FoodService():@staticmethoddef setStockChangeLog( food_id = 0,quantity = 0,note = '' ):if food_id < 1:return Falsefood_info = Food.query.filter_by( id = food_id ).first()if not food_info:return Falsemodel_stock_change = FoodStockChangeLog()model_stock_change.food_id = food_idmodel_stock_change.unit = quantitymodel_stock_change.total_stock = food_info.stockmodel_stock_change.note = notemodel_stock_change.created_time = getCurrentDate()db.session.add(model_stock_change)db.session.commit()return True
可视化编辑操作:Ueditor
包含3个js文件
<script src="{{ buildStaticUrl('/plugins/ueditor/ueditor.config.js') }}"></script>
<script src="{{ buildStaticUrl('/plugins/ueditor/ueditor.all.min.js') }}"></script>
<script src="{{ buildStaticUrl('/plugins/ueditor/lang/zh-cn/zh-cn.js') }}"></script>
Ueditor是一款基于JavaScript的富文本编辑器,它提供了丰富的功能和插件,可以方便地嵌入到网页中,用于编辑和管理文本内容。根据你提供的引用内容,我可以为你介绍一下如何在Ueditor中进行一些自定义修改。
- 在ueditor\ueditor.all.js文件中,找到editorui[“simpleupload”] = function (editor){}这一行代码后面,添加你想要的自定义代码。例如:
editorui["simpleupload"] = function (editor){// 自定义代码console.log("这是我的自定义代码"); }
- 在ueditor\ueditor.all.js文件中,找到UE.plugins[‘defaultfilter’]这一行代码后面,新增你想要的自定义代码。例如:
UE.plugins['defaultfilter'] = function(){// 自定义代码return ; }
请注意,以上只是示例代码,你可以根据自己的需求进行相应的修改和扩展。
分类优化插件 select2
web/static/plugins/select2/ 在set.html中插入使用进行分类优化
{% block css %}
<link href="{{ buildStaticUrl( '/plugins/select2/select2.min.css' ) }}" rel="stylesheet">{% endblock %}{% block js %}<script src="{{ buildStaticUrl( '/plugins/select2/select2.pinyin.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/select2/zh-CN.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/select2/pinyin.core.js' ) }}"></script>{% endblock %}
web/static/js/food/set.js
var food_set_ops: ={...$(".wrap_food_set select[name=cat_id]").select2({language: "zh-CN",width: '100%'});...}
web/templates/food/set.html
效果图: 下拉框可实现输入搜索选择功能
关键字标签插件
web/templates/food/set.html
{% block content %}{# 标签 #}<div class="hr-line-dashed"></div><div class="form-group"><label class="col-lg-2 control-label">标签:</label><div class="col-lg-10"><input type="text" class="form-control" name="tags" value="{{ info.tags }}"></div></div>{% endblock %}{% block css %}<link href="{{ buildStaticUrl( '/plugins/tagsinput/jquery.tagsinput.min.css' ) }}" rel="stylesheet">
{% endblock %}{% block js %}<script src="{{ buildStaticUrl( '/plugins/tagsinput/jquery.tagsinput.min.js' ) }}"></script>{% endblock %}
添加关键字标签info.tags 存取到数据库Food.py
web/static/js/food/set.js
...$(".wrap_food_set input[name=tags]").tagsInput({width: 'auto',height: 40,onAddTag: function (tag) {},onRemoveTag: function (tag) {}});...
效果图如图
输入标签名,tab创建并切割
jQuery标签插件tagsinput.js
无刷新的上传图片 无刷新技术【上传文件等】
HTML中form表单不可以嵌套。
我们可以将form表单提交到iframe,
web/templates/food/set.html
{% block content %}
<iframe name="upload_file" class="hide"></iframe>{% endblock %}
为什么 iframe name ="upload_file"?
form 中target定义的哪个地方,iframe name就指向哪个地方。
web/static/js/food/set.js 添加监控上传图片这个动作的变化事件,然后自动触发form表单的自动上传的事件
$(".wrap_food_set .upload_pic_wrap input[name=pic]").change(function () {$(".wrap_food_set .upload_pic_wrap").submit();});
这也就是为什么非得用form,因为form表单有submit提交事件
<div class="row mg-t20 wrap_food_set" style="">
<form class="upload_pic_wrap">
<input type="file" name="pic" accept="image/png, image/jpeg, image/jpg,image/gif">
这段代码是一个事件绑定的函数,当名为"pic"的输入框的值发生改变时,会触发change事件。在事件处理函数中,会找到名为"wrap_food_set"的元素下的class为"upload_pic_wrap"的元素,并将其提交。换句话说,当用户选择了一个新的图片文件后,该代码会将选择的图片文件提交给服务器Upload.py进行处理。
这段代码的作用是实现了一个图片上传功能,当用户选择了一个新的图片文件后,会自动将该图片文件提交到服务器进行处理。具体的处理逻辑需要在服务器端的代码中实现
赘述:
该段代码使用了jQuery库来实现一个事件绑定的功能。具体来说,代码中的$(".wrap_food_set .upload_pic_wrap input[name=pic]").change(function () { $(".wrap_food_set .upload_pic_wrap").submit(); });
是一个事件绑定的语句,它会在.wrap_food_set .upload_pic_wrap input[name=pic]
元素的change
事件触发时执行一个回调函数。回调函数中的$(".wrap_food_set .upload_pic_wrap").submit();
则是在事件触发时执行的操作,它会将.wrap_food_set .upload_pic_wrap
元素的表单提交。
具体来说,代码中的选择器$(".wrap_food_set .upload_pic_wrap input[name=pic]")
会选中.wrap_food_set
元素下的.upload_pic_wrap
元素下的input[name=pic]
元素。当这个元素的change
事件触发时,回调函数中的代码$(".wrap_food_set .upload_pic_wrap").submit();
会将.wrap_food_set .upload_pic_wrap
元素的表单提交。
问题1: hidden的作用是什么?
hidden
属性用于隐藏一个元素。当一个元素被设置为hidden
时,它将不会在页面上显示,但仍然存在于DOM中。这意味着该元素仍然可以通过JavaScript或其他方式进行访问和操作,但用户将无法看到它。
在这个特定的代码段中,hidden
属性被应用于iframe
元素,该元素用于上传文件。通过将iframe
设置为hidden
,可以隐藏上传文件的过程,使用户无法直接看到上传的文件或上传的进度。
这样可以提供更好的用户体验,同时保护用户的隐私。
相关文章:
Flask 菜品管理
common/libs/Helper.py getDictFilterField() 方法 用于在web/templates/food/index.html中展示菜品分类 如何能够通过food里面的cat_id获取分类信息呢?只能通过for循环,这样会很麻烦,所以定义了这个方法。 这个方法可以的查询返回结果…...
亚马逊实时 AI 编程助手 CodeWhisperer使用体验
文章目录 1:什么是CodeWhisperer ?2:试用3:上手体验 1:什么是CodeWhisperer ? 最近ChatGPT展现出强大AI能力给我们带来了深刻的影响,AI现在不是一个概念,基于AI的产品一定在各行各业…...
[机缘参悟-123] :实修 - 东西方各种思想流派实修的要旨与比较?
目录 前言: 一、东方各种思想流派实修的要旨? 1.1 儒、释、道、法的主要思想 1.2 儒、释、道、法各种追求的目标 1.3 儒、释、道、法各自修行的法门或修行的途径 二、西方灵修的各种派别的要旨? 2.0 西方灵修的各种派别 2.1 玛雅星系…...
基于51单片机的数字时钟系统设计
标题:基于51单片机的数字时钟系统设计与实现 摘要: 本文详细介绍了基于STC89C51单片机设计一款具有精确计时功能的数字时钟系统的全过程。该系统利用了单片机内部的定时器/计数器资源,结合液晶显示屏和按键输入模块,实现了时间显…...
《每天十分钟》-红宝书第4版-基本引用类型
引用值(或者对象)是某个特定引用类型的实例。在 ECMAScript 中,引用类型是把数据和功能组织到一起的结构,经常被人错误地称作“类”。虽然从技术上JavaScript 是一门面向对象语言,但ECMAScript 缺少传统的面向对象编程…...
【EAI 005】EmbodiedGPT:通过具身思维链进行视觉语言预训练的具身智能大模型
论文描述:EmbodiedGPT: Vision-Language Pre-Training via Embodied Chain of Thought 论文作者:Yao Mu, Qinglong Zhang, Mengkang Hu, Wenhai Wang, Mingyu Ding, Jun Jin, Bin Wang, Jifeng Dai, Yu Qiao, Ping Luo 作者单位:The Universi…...
一文读懂「Chain of Thought,CoT」思维链
前言: 思维链,在人工智能领域,是一个非常非常新的概念。强大的逻辑推理是大语言模型“智能涌现”出的核心能力之一,好像AI有了人的意识一样。而推理能力的关键在于——思维链(Chain of Thought,CoT)。 相关概念: 语言智能可以被理解为“使用基于自然语言的概念对经验事…...
杨中科 ASP.NET Core 中的依赖注入的使用
ASP.NET CORE中服务注入的地方 1、在ASP.NET Core项目中一般不需要自己创建ServiceCollection、IServiceProvider。在Program.cs的builder.Build()之前向builderServices中注入 2、在Controller中可以通过构造方法注入服 务。 3、演示 新建一个calculator类 注入 新建TestC…...
Spring Boot 和 Spring 有什么区别
Spring Boot 和 Spring 是两个不同的概念,它们服务于不同的目的,但它们之间有着紧密的联系。下面是它们之间的主要区别: 目的和定位: Spring:Spring 是一个开源的 Java 平台,它最初由 Rod Johnson 创建&am…...
Linux——以太网
一、Linux下的以太网架构 1、Linux 系统网络协议层架构 PHY 驱动的功能处于链路层: 2、以太网物理层与硬件连接 我们重点关注以下两点: (1)与 MAC 设备的接口,即是 gmii 还是 rgmii。 (2) Phy…...
HTTP 代理原理及实现(二)
在上篇《HTTP 代理原理及实现(一)》里,我介绍了 HTTP 代理的两种形式,并用 Node.js 实现了一个可用的普通 / 隧道代理。普通代理可以用来承载 HTTP 流量;隧道代理可以用来承载任何 TCP 流量,包括 HTTP 和 H…...
JavaScript 地址信息与页面跳转
在JavaScript中,你可以使用各种方法来处理地址信息并进行页面跳转。以下是一些常见的方法: 1.使用window.location对象: window.location对象包含了当前窗口的URL信息,并且可以用来进行页面跳转。 * 获取URL的某一部分…...
力扣(leetcode)第383题赎金信(Python)
383.赎金信 题目链接:383.赎金信 给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以,返回 true ;否则返回 false 。 magazine 中的每个字符只能在 ransomNote…...
提升网络安全重要要素IP地址
在数字化时代,网络安全已经成为人们关注的焦点。本文将深入探讨网络安全与IP地址之间的紧密联系,以及IP地址在构建数字世界的前沿堡垒中的关键作用。 网络安全是当今数字社会中不可忽视的挑战之一。而IP地址,作为互联网通信的基础协议&#…...
解析c++空指针解引用奔溃
空指针解引用引起程序奔溃是c/c中最常见的稳定性错误之一。 显然并非所有使用空指针的语句都会导致奔溃,那什么情况下使用空指针才会引起程序奔溃呢?有一个判断标准:判断空指针是否会导致访问非法内存的情况,如果会导致访问非法内…...
Oracle START WITH 递归语句的使用方法及示例
Oracle数据库中的START WITH语句经常与CONNECT BY子句一起使用,以实现对层次型数据的查询。这种查询模式非常适用于处理具有父子关系的数据,如组织结构、分类信息等。 理解START WITH和CONNECT BY 在层次型查询中,START WITH定义了层次结构…...
使用Windbg动态调试目标进程的一般步骤详解
目录 1、概述 2、将Windbg附加到已经启动起来的目标进程上,或者用Windbg启动目标程序 2.1、将Windbg附加到已经启动起来的目标进程上 2.2、用Windbg启动目标程序 2.3、Windbg关联到目标进程上会中断下来,输入g命令将该中断跳过去 3、分析实例说明 …...
Linux驱动学习—输入子系统
1、什么是输入子系统? 输入子系统是Linux专门做的一套框架来处理输入事件的,像鼠标,键盘,触摸屏这些都是输入设备,但是这邪恶输入设备的类型又都不是一样的,所以为了统一这些输入设备驱动标准应运而生的。…...
计算机网络(2)
计算机网络(2) 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU) 计算机网络和因特网(2)分组交换网中的时延、丢包和吞吐量时延丢包吞吐量总结 协议层次及其服务模型模型类型OSI模型分析TCP/IP模型分析 追溯历史 小程一言 我…...
什么是预训练Pre-training—— AIGC必备知识点,您get了吗?
Look!👀我们的大模型商业化落地产品📖更多AI资讯请👉🏾关注Free三天集训营助教在线为您火热答疑👩🏼🏫 随着人工智能(AI)不断重塑我们的世界,其发展的一个关键方面已经…...
bat脚本sqlserver 不同数据库同步
如果你想使用批处理脚本(.bat)在 SQL Server 中同步不同数据库的数据,你可以考虑以下步骤: 设置环境变量: 确保你的系统环境变量中已经设置了 SQLCMD 和 BCP 的路径。 编写批处理脚本: 使用 sqlcmd 来执行…...
阶段十-分布式-Redis02
第一章 Redis 事务 1.1 节 数据库事务复习 数据库事务的四大特性 A:Atomic ,原子性,将所以SQL作为原子工作单元执行,要么全部执行,要么全部不执行;C:Consistent,一致性࿰…...
微信小程序实战-02翻页时钟-2
微信小程序实战系列 《微信小程序实战-01翻页时钟-1》 文章目录 微信小程序实战系列前言计时功能实现clock.wxmlclock.wxssclock.js 运行效果总结 前言 接着《微信小程序实战-01翻页时钟-1》,继续完成“6个页面的静态渲染和计时”功能。 计时功能实现 clock.wxm…...
每天刷两道题——第十一天
1.1滑动窗口最大值 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值 。 输入:nums [1,3,-1,-3,5,3,6,7], k 3 输出&…...
Git提交规范
一. 修改类型 每个类型值都表示了不同的含义,类型值必须是以下的其中一个: feat:提交新功能fix:修复了bugdocs:只修改了文档style:调整代码格式,未修改代码逻辑(比如修改空格、格式…...
apache2的虚拟主机的配置
APACHE2的虚拟主机配置 本章中心概括: 虚拟web主机的初步认识,在redhat系列系统中如何配置,在Debian系列系统中如何配置。 什么是apache2虚拟主机: 简单点讲,就是在同一个物理机中配置多个虚拟主机,从而达…...
Provide/Inject 依赖注入(未完待续)
父组件传递给子组件数据,通过props,但是需要逐层传递 provide/Inject 的推出就是为了解决这个问题,它提供了一种组件之间共享此类值的方式,不必通过组件树每层级显示地传递props 目的是为了共享那些被 认为对于一个组件树而言是全局的数据 p…...
力扣173. 二叉搜索树迭代器
深度优先搜索 思路: 遍历二叉搜索树,左子树总比根节点小,右子树总比根节点大;先深度遍历左子树,然后返回其父节点,然后遍历其右子树节点;使用栈数据结构存储节点数据,借用其“后进先…...
电脑找不到d3dcompiler43.dll怎么修复,教你5个可靠的方法
d3dcompiler43.dll是Windows操作系统中的一个重要动态链接库文件,主要负责Direct3D编译器的相关功能。如果“d3dcompiler43.dll丢失”通常会导致游戏无法正常运行或者程序崩溃。为了解决这个问题,我整理了以下五个解决方法,希望能帮助到遇到相…...
5.3 Android BCC环境搭建(eadb版 上)
写在前面 eadb即eBPF Android Debug Bridge,它是基于adeb的重构。后者曾随aosp 10发布在platform/external目录下。 一,root权限 这里再HighLight下,当前整个专栏都是基于开发环境来展开的,也就是Android设备需要具有root权限。因此该专栏下每一篇博客都是默认了当前开发…...
龙岩网站推广/aso应用商店优化
命令: g/pattern/d 如,删除包含字母 hell 的行 g/hell/d 删除 不 匹配指定字符的行(未验证,有需要的朋友可以试一下) v/pattern/d g!/pattern/d...
struts动态网站开发综合实训总结/百度网盘app下载安装电脑版
1. 自动增长的数据类型处理MYSQL有自动增长的数据类型,插入记录时不用操作此字段,会自动获得数据值。ORACLE没有自动增长的数据类型,需要建立一个自动增长的序列号,插入记录时要把序列号的下一个值赋于此字段。CREATE SEQUENCE序列…...
cms 网站后台/搜索引擎营销的概念
物联网(Internet of Things,简称IoT)是指通过各种信息传感器、射频识别技术、全球定位系统、红外感应器、激光扫描器等各种装置与技术,实时采集任何需要监控、 连接、互动的物体或过程,采集其声、光、热、电、力学、化…...
js做示爱网站例子/营销必备十大软件
一、成员变量与成员函数分开存储 1、在C中,类内的成员变量和成员函数分开存储 首先,对于一个空对象,占用内存空间为1 class person {};void test01() {person p;cout << sizeof(p) << endl; } 因为C编译器给每个空对象分配1个字…...
2323wan网页游戏/河北seo人员
HighDPI是windows7提出的一种新特性,使应用程序更加容易地为视觉障碍者服务,使程序随着窗口一样自动缩放, 展示完美的效果,下面我们来实践一下,如何开发基于HIGHDPI的应用程序 首先体验下,在英文windows7…...
内网怎么做网站服务器/论坛seo教程
1、HTTP标头值 Content-type application/x-www-form-urlencoded ,post参数值需要URL编码 2、测试接口签约短信验证码为固定值:111111 3、参数不能有空值 4、测试支付接口规则:金额为奇数交易成功,偶数时会交易失败。 ࿰…...