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.query
if id_list and len( id_list ) > 0:
query = query.filter( select_filed.in_( id_list ) )
list = query.all()
if not list:
return ret
for item in list:
if not hasattr( item,key_field ):
break
ret[ getattr( item,key_field ) ] = item
return 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 + b
result = 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, request
app = 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.values
action = req['action'] if 'action' in req else ''
if action == "config":
root_path = app.root_path
config_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.files
upfile = file_target['pic'] if 'pic' in file_target else None
callback_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.files
upfile = file_target['upfile'] if 'upfile' in file_target else None
if 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.values
start = int( req['start']) if 'start' in req else 0
page_size = int( req['size']) if 'size' in req else 20
query = Image.query
if 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.id
resp['list'] = images
resp['start'] = start
resp['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 content
except 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.query
if 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_size
list = 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类:
@staticmethod
def 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'] = -1
resp['msg'] = "不允许的扩展类型文件"
return resp
root_path = app.root_path + config_upload['prefix_path'] #获取文件保存的根目录路径:
#不使用getCurrentDate创建目录,为了保证其他写的可以用,这里改掉,服务器上好像对时间不兼容
file_dir = datetime.datetime.now().strftime("%Y%m%d") #创建文件保存的目录:
save_dir = root_path + file_dir
if 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_name
model_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
...
@staticmethod
def buildImageUrl(path):
app_config = app.config['APP']
url = app_config['domain'] + app.config['UPLOAD']['prefix_url'] + path
return 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.values
page = int(req['p']) if ('p' in req and req['p']) else 1
query = Food.query
if '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'] = list
resp_data['pages'] = pages
resp_data['search_con'] = req
resp_data['status_mapping'] = app.config['STATUS_MAPPING']
resp_data['cat_mapping'] = cat_mapping
resp_data['current'] = 'index'
return ops_render( "food/index.html",resp_data )
@route_food.route( "/info" )
def info():
resp_data = {}
req = request.args
id = 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'] = info
resp_data['stock_change_list'] = stock_change_list
resp_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.args
id = 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'] = info
resp_data['cat_list'] = cat_list
resp_data['current'] = 'index'
return ops_render( "food/set.html" ,resp_data)
resp = {'code': 200, 'msg': '操作成功~~', 'data': {}}
req = request.values
id = int(req['id']) if 'id' in req and req['id'] else 0
cat_id = int(req['cat_id']) if 'cat_id' in req else 0
name = 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'] = -1
resp['msg'] = "请选择分类~~"
return jsonify(resp)
if name is None or len(name) < 1:
resp['code'] = -1
resp['msg'] = "请输入符合规范的名称~~"
return jsonify(resp)
if not price or len( price ) < 1:
resp['code'] = -1
resp['msg'] = "请输入符合规范的售卖价格~~"
return jsonify(resp)
price = Decimal(price).quantize(Decimal('0.00'))
if price <= 0:
resp['code'] = -1
resp['msg'] = "请输入符合规范的售卖价格~~"
return jsonify(resp)
if main_image is None or len(main_image) < 3:
resp['code'] = -1
resp['msg'] = "请上传封面图~~"
return jsonify(resp)
if summary is None or len(summary) < 3:
resp['code'] = -1
resp['msg'] = "请输入图书描述,并不能少于10个字符~~"
return jsonify(resp)
if stock < 1:
resp['code'] = -1
resp['msg'] = "请输入符合规范的库存量~~"
return jsonify(resp)
if tags is None or len(tags) < 1:
resp['code'] = -1
resp['msg'] = "请输入标签,便于搜索~~"
return jsonify(resp)
food_info = Food.query.filter_by(id=id).first()
before_stock = 0
if food_info:
model_food = food_info
before_stock = model_food.stock
else:
model_food = Food()
model_food.status = 1
model_food.created_time = getCurrentDate()
model_food.cat_id = cat_id
model_food.name = name
model_food.price = price
model_food.main_image = main_image
model_food.summary = summary
model_food.stock = stock
model_food.tags = tags
model_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.values
query = FoodCat.query
if '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'] = list
resp_data['search_con'] = req
resp_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.args
id = int(req.get("id", 0))
info = None
if id:
info = FoodCat.query.filter_by( id = id ).first()
resp_data['info'] = info
resp_data['current'] = 'cat'
return ops_render( "food/cat_set.html" ,resp_data )
resp = {'code': 200, 'msg': '操作成功~~', 'data': {}}
req = request.values
id = req['id'] if 'id' in req else 0
name = req['name'] if 'name' in req else ''
weight = int( req['weight'] ) if ( 'weight' in req and int( req['weight']) > 0 ) else 1
if name is None or len( name ) < 1:
resp['code'] = -1
resp['msg'] = "请输入符合规范的分类名称~~"
return jsonify( resp )
food_cat_info = FoodCat.query.filter_by( id = id ).first()
if food_cat_info:
model_food_cat = food_cat_info
else:
model_food_cat = FoodCat()
model_food_cat.created_time = getCurrentDate()
model_food_cat.name = name
model_food_cat.weight = weight
model_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.values
id = req['id'] if 'id' in req else 0
act = req['act'] if 'act' in req else ''
if not id :
resp['code'] = -1
resp['msg'] = "请选择要操作的账号~~"
return jsonify(resp)
if act not in [ 'remove','recover' ] :
resp['code'] = -1
resp['msg'] = "操作有误,请重试~~"
return jsonify(resp)
food_cat_info = FoodCat.query.filter_by( id= id ).first()
if not food_cat_info:
resp['code'] = -1
resp['msg'] = "指定分类不存在~~"
return jsonify(resp)
if act == "remove":
food_cat_info.status = 0
elif act == "recover":
food_cat_info.status = 1
food_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.values
id = req['id'] if 'id' in req else 0
act = req['act'] if 'act' in req else ''
if not id :
resp['code'] = -1
resp['msg'] = "请选择要操作的账号~~"
return jsonify(resp)
if act not in [ 'remove','recover' ]:
resp['code'] = -1
resp['msg'] = "操作有误,请重试~~"
return jsonify(resp)
food_info = Food.query.filter_by( id = id ).first()
if not food_info:
resp['code'] = -1
resp['msg'] = "指定美食不存在~~"
return jsonify(resp)
if act == "remove":
food_info.status = 0
elif act == "recover":
food_info.status = 1
food_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():
@staticmethod
def setStockChangeLog( food_id = 0,quantity = 0,note = '' ):
if food_id < 1:
return False
food_info = Food.query.filter_by( id = food_id ).first()
if not food_info:
return False
model_stock_change = FoodStockChangeLog()
model_stock_change.food_id = food_id
model_stock_change.unit = quantity
model_stock_change.total_stock = food_info.stock
model_stock_change.note = note
model_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
,可以隐藏上传文件的过程,使用户无法直接看到上传的文件或上传的进度。
这样可以提供更好的用户体验,同时保护用户的隐私。