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 %}

这段代码是一个网页模板,使用了模板引擎语法。下面我将逐段解析代码的功能和作用:

  1. <input type="text" name="name" class="form-control" placeholder="请输入分类名称~~" value="{{ info.name }}">:这行代码定义了一个文本输入框元素,其中包含了一个名为"name"的属性和一个名为"info.name"的变量。

  2. <div class="hr-line-dashed"></div>:这行代码定义了一个带有样式类名的div元素,用于创建一条虚线。

  3. <input type="text" name="weight" class="form-control" placeholder="请输入分类名称~~" value="{% if info and info.weight > 0 %}{{ info.weight }}{% else %}1{% endif%}">:这行代码定义了一个文本输入框元素,其中包含了一个名为"weight"的属性和一个名为"info.weight"的变量。

  4. <div class="hr-line-dashed"></div>:这行代码定义了一个带有样式类名的div元素,用于创建一条虚线。

  5. <div class="col-lg-4 col-lg-offset-2">:这行代码定义了一个带有样式类名的div元素,并设置了偏移量。

  6. <input type="hidden" name="id" value="{{ info.id }}">:这行代码定义了一个隐藏的输入框元素,其中包含了一个名为"id"的属性和一个名为"info.id"的变量。

  7. <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请求。下面是对代码的解释:

  1. 引用中的代码是一个HTML模板文件,其中使用了{%block js %}和{% endblock %}来引入一个JavaScript文件。这个JavaScript文件的路径是通过buildStaticUrl函数生成的。

  2. 引用中的代码是一个Python蓝图(Blueprint)中的一个路由函数,用于处理/cat-set接口的请求。这个函数首先判断请求的方法是GET还是POST,然后根据不同的方法执行不同的逻辑。

  3. 如果请求的方法是GET,那么函数会获取请求参数中的id,并根据id查询数据库中对应的FoodCat对象。然后将查询到的对象和当前的cat(可能是一个全局变量)放入resp_data字典中。

  4. 最后,函数会调用ops_render函数渲染一个HTML模板文件(food/cat_set.html),并将resp_data作为参数传入。

  5. 在JavaScript代码中,food_cat_set_ops是一个对象,其中包含了一个init方法和一个eventBind方法。init方法用于初始化页面,eventBind方法用于绑定事件。

这段代码是一个jQuery的事件处理函数,当点击".wrap_cat_set .save"元素时触发。下面是对代码各部分的解释:

  1. var btn_target = $(this);:将当前点击的元素保存到变量btn_target中。

  2. if( btn_target.hasClass("disabled") ){...}:判断btn_target元素是否有类名为"disabled",如果有,则弹出提示信息并返回。

  3. var name_target = $(".wrap_cat_set input[name=name]");:选取类名为"wrap_cat_set"的元素下的name属性为"name"的input元素,并将其保存到变量name_target中。

  4. var name = name_target.val();:获取name_target元素的值,并将其保存到变量name中。

  5. var weight_target = $(".wrap_cat_set input[name=weight]");:选取类名为"wrap_cat_set"的元素下的name属性为"weight"的input元素,并将其保存到变量weight_target中。

  6. var weight = weight_target.val();:获取weight_target元素的值,并将其保存到变量weight中。

  7. if( name.length < 1 ){...}:判断name的长度是否小于1,如果是,则弹出提示信息并返回。

  8. if( parseInt( weight ) < 1 ){...}:将weight转换为整数,并判断是否小于1,如果是,则弹出提示信息并返回。

  9. btn_target.addClass("disabled");:给btn_target元素添加类名"disabled"。

  10. 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。

通过返回结果集,我们可以方便地获取函数执行后的输出,并在程序中进行进一步的处理。

封面图部分代码:

这段代码是一个网页表单,用于上传图片并显示已上传的图片。具体作用如下:

  1. 这段代码包含了一个表单,其中包含了一个用于上传图片的文件输入框。用户可以选择图片文件并将其上传到服务器。上传的目标URL是{{ buildUrl('/upload/pic') }},该URL由服务器端生成。上传的图片文件将被命名为pic,并且只接受image/pngimage/jpegimage/jpgimage/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){   }

这段代码是一个成功上传文件后的回调函数。当文件上传成功后,会执行这段代码。代码的功能是将上传成功的文件显示在页面上,并提供删除按钮。具体解释如下:

  1. 首先,判断上传成功后返回的文件key是否为空,如果为空则直接返回,不执行后续代码。

  2. 创建一个变量html,用于存储要显示在页面上的图片和删除按钮的HTML代码。代码中使用了common_ops.buildPicUrl(file_key)函数来构建图片的URL,这个函数可能是用来拼接图片的完整URL地址。

  3. 判断页面上是否已经存在图片,

                如果存在则将新的图片替换原有的图片,jQuery 设置内容和属性 | 菜鸟教程

                如果不存在则将新的图片添加到页面上。 jQuery 添加元素 | 菜鸟教程

  1. 最后,调用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路由函数,用于处理上传图片的请求。下面是对各部分代码的解析:

  1. @route_upload.route("/ueditor",methods = [ "GET","POST" ])

    • 这是一个装饰器,将函数ueditor()绑定到路由/ueditor上,并指定支持的请求方法为GET和POST。
  2. root_path = app.root_path

    • 这行代码获取Flask应用程序的根目录路径
  3. 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格式的配置文件。下面是对该段代码的详细解析:

  1. 首先,代码中使用了json.loads()函数来解析JSON格式的数据。json.loads()函数将JSON字符串转换为Python对象。
  2. re.sub()函数用于替换字符串中的匹配项。在这里,使用正则表达式r'\/\*.*\*/'匹配注释块,并将其替换为空字符串
  3. fp.read()读取文件内容,并将其作为参数传递给re.sub()函数进行处理。
  4. try关键字表示尝试执行下面的代码块。
  5. 如果解析成功,将解析结果赋值给config_data变量。
  6. 如果解析失败,即出现异常,except关键字后面的代码块将被执行。
  7. 在这里,如果解析失败,将config_data赋值为空字典{}
  8. 无论解析是否成功,最后都会执行下面的代码。
知识点3:{}在python中是对象还是字典还是什么

在Python中,{}可以表示多种数据结构,包括字典和集合。具体取决于{}的上下文使用方式。

  1. 如果{}中包含键值对,例如{“name”: “zhangsan”, “age”: 26},那么它表示一个字典。字典是一种无序的键值对集合,通过键来索引对应的值。在字典中,键必须是唯一的,而值可以重复。

  2. 如果{}中没有键值对,例如{1, 2, 3},那么它表示一个集合。集合是一种无序且不重复的元素集合。集合可以用于去除重复元素、判断元素是否存在等操作。

所以,{}在Python中既可以表示字典,也可以表示集合,具体取决于上下文的使用方式。


@route_upload.route("/pic", methods=["GET","POST"])

def uploadPic()

这段代码是一个Python的路由函数,用于处理上传图片的请求。下面是对代码的解析:

  1. file_target = request.filesrequest是一个全局对象,用于获取当前请求的信息。request.files是一个字典,包含了上传的文件对象。

  2. upfile = file_target['pic'] if 'pic' in file_target else None:这行代码用于获取上传的文件对象。如果file_target字典中存在键为'pic'的项,则将其赋值给upfile变量,否则将upfile赋值为None

  3. 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 中的函数,它是在父窗口中定义的。通过调用这个函数,可以在上传成功后执行一些操作,比如更新页面内容或显示上传成功的提示信息。

  4. return "<script type='text/javascript'>{0}.error('{1}')</script>".format( callback_target,"上传失败" ):返回一个包含JavaScript代码的字符串,该代码会调用callback_targeterror方法,并传入字符串"上传失败"作为参数。

该段代码是使用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()方法,并将"上传失败"作为参数传递给该方法。


  1. ret = UploadService.uploadByFile(upfile):调用UploadServiceuploadByFile方法,将upfile作为参数传入,并将返回值赋值给ret变量。

  2. return "<script type='text/javascript'>{0}.error('{1}')</script>".format(callback_target, "上传失败:" + ret['msg']):返回一个包含JavaScript代码的字符串,该代码会调用callback_targeterror方法,并传入字符串"上传失败:" + ret['msg']作为参数。

  3. return "<script type='text/javascript'>{0}.success('{1}')</script>".format(callback_target,ret['data']['file_key'] ):返回一个包含JavaScript代码的字符串,该代码会调用callback_targetsuccess方法,并传入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的作用是什么?

starttotal变量的作用如下:

  1. startstart变量用于指定从数据库中获取照片数据的起始位置。它是一个整数值,表示上一次请求返回的最后一张照片的ID。通过将start值传递给数据库查询,可以确保每次请求返回的照片数据不会重复。

  2. totaltotal变量用于记录满足查询条件的照片总数。它是一个整数值,表示数据库中满足查询条件的照片总数。通过将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小于startImage对象。

总结起来,这段代码的作用是创建一个查询对象,并根据条件过滤出符合条件的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_fdfollow_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 ) )

这段代码是用来生成一个唯一的文件名并保存文件的。具体的步骤如下:

  1. 使用uuid.uuid4()函数生成一个UUID(通用唯一标识符)。
  2. 使用str()函数将UUID转换为字符串。
  3. 使用replace()函数将字符串中的"-“替换为空字符串,以去除UUID中的”-"。
  4. 使用字符串拼接的方式将生成的文件名与文件扩展名拼接在一起,得到完整的文件名。
  5. 使用file.save()函数将文件保存到指定的目录中。

{0}/{1} 是一个字符串格式化的语法,用于将两个变量的值插入到字符串中的占位符位置。其中,{0}{1} 分别表示第一个和第二个变量。

在给定的代码中,{0}/{1} 表示一个字符串,其中 {0}{1} 分别会被 save_dirfile_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. 判断是否传入了标签,如果没有传入或者标签长度小于1,则返回错误信息。
  2. 根据传入的id查询食品信息,如果存在则更新该食品的信息,如果不存在则创建一个新的食品。
  3. 更新食品的分类id、名称、价格、主图、摘要、库存和标签,并更新更新时间。
  4. 将食品信息添加到数据库中,并提交事务。
  5. 调用FoodService的setStockChangeLog方法,记录库存变动日志。
  6. 返回处理结果。

这段代码的主要作用是处理食品信息的增加或修改,并记录库存变动日志。在前端接收到支付链接后,可以调用这个接口来更新食品信息。

问题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'))
  1. Decimal(price):将变量price转换为Decimal类型。Decimal是Python中用于处理精确小数运算的模块。
  2. .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中进行一些自定义修改。

  1. 在ueditor\ueditor.all.js文件中,找到editorui[“simpleupload”] = function (editor){}这一行代码后面,添加你想要的自定义代码。例如:
    editorui["simpleupload"] = function (editor){// 自定义代码console.log("这是我的自定义代码");
    }

  2. 在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,可以隐藏上传文件的过程,使用户无法直接看到上传的文件或上传的进度。

这样可以提供更好的用户体验,同时保护用户的隐私。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/611513.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux 01 centos镜像下载,服务器,vmware模拟服务器

https://www.bilibili.com/video/BV1pz4y1D73n?p3&vd_source4ba64cb9b5f8c56f1545096dfddf8822 01.使用的版本 国内主要使用的版本是centos 02.centos镜像下载 这里的是centos7 一.阿里云官网地址&#xff1a;https://www.aliyun.com/ 二. -----【文档与社区】 —【…

CSND修改付费专栏价格

人工客服在个人中心右下角可以找到 客服回复已订阅专栏不支持修改价格

师傅带练|在线实习项目,提供实习证明

八大项目&#xff1a;某实习网站招聘信息采集与分析&#xff08;Python数据采集与分析&#xff09; 股票价格形态聚类与收益分析&#xff08;Python金融分析&#xff09; 某平台网络入侵用户自动识别&#xff08;Python机器学习&#xff09; 某平台广东省区采购数据分析&…

Vue项目nginx部署到线上,访问时加前缀解决方案

一、业务场景&#xff1a; 最近项目开发完了&#xff0c;需要部署一个测试版本和正式版本到线上&#xff0c;测试版本前面需要加一个dev前缀&#xff0c;遇到了一些坑&#xff0c;分享给大家 二、目前效果 三、具体实现步骤&#xff1a; &#xff08;1&#xff09;实现静态文…

构建安全可靠的系统:第二十一章到附录 A

第二十一章&#xff1a;建立安全和可靠性文化 原文&#xff1a;21. Building a Culture of Security and Reliability 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 作者&#xff1a;Heather Adkins 与 Peter Valchev&#xff0c;Felix Grbert&#xff0c;Ana Oprea…

艾思控AQMD6008BLS-TE无刷电机驱动使用笔记(配合STM32)

一、介绍 本驱动器使用的电机电流精确检测技术、有感无刷电机自测速、有感无刷电机转动位置检测、再生电流恒电流制动&#xff08;或称刹车&#xff09;技术和强大的PID调节技术可地控制电机平稳正反转、换向及制动&#xff0c;输出电流实时调控防止过流&#xff0c;精准控制电…

GC2804——GC2803 的升级,12V H桥驱动,8 个NPN达林顿晶体管,采用SSOP24 封装,应用于安防步进电机驱动产品上

GC2804 是 GC2803 的升级&#xff0c;在内部集成了 8 个 NPN 达林顿晶体管连接阵列的基础上&#xff0c;又集成了一路 12V 的 H 桥驱动。8 个 NPN 达林顿阵列用来驱动高压的步进电机&#xff0c;H 桥驱动可以用来驱动 IR-CUT&#xff0c;此芯片专用于安防摄像头系统方案。该 GC…

十分钟部署清华 ChatGLM-6B,实测效果超预期(Linux版)

前段时间&#xff0c;清华公布了中英双语对话模型 ChatGLM-6B&#xff0c;具有60亿的参数&#xff0c;初具问答和对话功能。 最&#xff01;最&#xff01;最重要的是它能够支持私有化部署&#xff0c;大部分实验室的服务器基本上都能跑起来。因为条件特殊&#xff0c;实验室网…

从零学Java 集合概述

Java 集合概述 文章目录 Java 集合概述1 什么是集合?2 Collection体系集合2.1 Collection父接口2.1.1 常用方法2.1.2 Iterator 接口 1 什么是集合? 概念&#xff1a;对象的容器&#xff0c;定义了对多个对象进行操作的常用方法&#xff1b;可实现数组的功能。 和数组区别&…

Logo设计神器:适合新手的简易操作软件,快速入门!

标志设计软件在品牌营销和企业识别中发挥着重要作用。本文将对10款知名标志设计软件进行横向评价&#xff0c;从不同维度评价其功能、易用性、创意和适用性&#xff0c;帮助您选择最适合您需求的标志设计软件。 1.即时设计 推荐指数&#xff1a;★★★★★ 即时设计是一款功…

springboot 2.7 oauth server配置源码走读一

springboot 2.7 oauth server配置源码走读 入口&#xff1a; 上述截图中的方法签名和OAuth2AuthorizationServerConfiguration类中的一个方法一样&#xff0c;只不过我们自己的配置类优先级比spring中的配置类低&#xff0c;算是配置覆盖&#xff0c;看下图所示&#xff1a; …

阿里云国际服务器设置安全防护程序

阿里云云服务器&#xff08;ECS&#xff09;提供弹性、安全、高性能、高性价比的虚拟云服务器&#xff0c;满足您的所有需求。立即在这里免费注册&#xff01; 常见 Web 应用程序 请勿对 Web 服务控制台&#xff08;如 WDCP、TOMCAT、Apache、Nginx、Jekins、PHPMyAdmin、Web…

JavaScript数组sort()对负数排序的陷阱

前言 想着好久没去力扣刷题了&#xff0c;刚好手上的需求也差不多了&#xff0c;就去看了看。看到一个难度级别为困难的题&#xff0c;看到这个题想着直接使用JS现成的方法&#xff0c;先concat再sort。再取中间值不就实现了吗。是不是你们也这么想&#xff0c;哈哈哈。 就是…

11 个 Python全栈开发工具集

前言 以下是专注于全栈开发不同方面的 Python 库;有些专注于 Web 应用程序开发&#xff0c;有些专注于后端&#xff0c;而另一些则两者兼而有之。 1. Taipy Taipy 是一个开源的 Python 库&#xff0c;用于构建生产就绪的应用程序前端和后端。 它旨在加快应用程序开发&#xf…

2024--Django平台开发-Django知识点(五)

day05 django知识点 今日概要&#xff1a; 中间件 【使用】【源码】cookie 【使用】【源码 - Django底层请求本质】session【使用】【源码 - 数据库请求周期中间件】 1.中间件 1.1 使用 编写类&#xff0c;在类型定义&#xff1a;process_request、process_view、process_…

【C++】STL 算法 ⑨ ( 预定义函数对象示例 - 将容器元素从大到小排序 | sort 排序算法 | greater<T> 预定义函数对象 )

文章目录 一、预定义函数对象示例 - 将容器元素从大到小排序1、sort 排序算法2、greater<T> 预定义函数对象 二、代码示例 - 预定义函数对象1、代码示例2、执行结果 一、预定义函数对象示例 - 将容器元素从大到小排序 1、sort 排序算法 C 标准模板库 ( STL , Standard Te…

离散数学-二元关系

4.1关系的概念 1)序偶及n元有序组 由两个个体x和y&#xff0c;按照一定顺序排序成的、有序数组称为有序偶或有序对、二元有序组&#xff0c; 记作<x&#xff0c;y>&#xff0c;其中x是第一分量&#xff0c;y是第二分量。 相等有序偶&#xff1a;第一分量和第二分量分…

游戏开发中,你的游戏图片压缩格式使用ASTC了吗

文章目录 ASTC原理&#xff1a;使用要求 ASTC&#xff08;Adaptive Scalable Texture Compression&#xff0c;自适应可伸缩纹理压缩&#xff09;是一种高级的纹理压缩技术&#xff0c;由ARM公司开发并推广。它在图形处理领域中因其出色的压缩效率和灵活性而受到广泛关注。 AST…

前端国际化之痛点(二):多包多库场景下联动多语言

前言 VoerkaI18n是一款非常优秀的前端国际化解决方案&#xff0c;其开发的出发点是为了解决现存多语言的一些痛点,接下来几篇文章将分别进行分析。 前端国际化之痛点(一)&#xff1a;让人头疼的词条Key前端国际化之痛点(二)&#xff1a;多包多库场景下联动多语言前端国际化之…

Jetson Orin AGX 64GB更新 Jetpack6.0

Jetson Orin AGX 64GB更新 Jetpack6.0 注意&#xff1a; 1&#xff0c;如果你要向我一样为AGX更新Jetpack6.0的话&#xff0c;它还要求你的ubuntu版本必须是20.04 或22.04 2&#xff0c;安装完SDKmanager后&#xff0c;然后选择对应的设备&#xff0c;根据个人选择勾选是否安装…