原本的系统中,不支持按topic筛选:

希望希望加上:
按照topic筛选
而发现新建剧本页面中有类似的功能:

所以参考:
src/routes/Script/ScriptCreate.js
最后整理过来代码:
<code>
export default class ScriptList extends PureComponent {
state = {
formValues: {},
currentPage: 1,
pageSize: 20,
first_level_topic: [],
second_level_topic: [],
};
componentDidMount() {
const { dispatch } = this.props;
const params = {
page_size: 1000,
};
dispatch({
type: 'topic/fetch',
payload: params,
});
}
const topicList = this.props.topic.topics;
const firstLevelOptions = Object.keys(topicList).map(first => <Option key={first}>{first}</Option>);
const secondLevelOptions = this.state.first_level_topic.map(second => <Option key={second}>{second}</Option>);
const selectFormItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7 },
},
wrapperCol: {
xs: { span: 36 },
sm: { span: 18 },
md: { span: 16 },
},
};
<Col md={12} sm={18}>
<FormItem {...selectFormItemLayout} label="Topic" style={{ marginBottom: 5 }} >
{getFieldDecorator('topic', {
rules: [{ required: false, message: '请选择Topic'}],
})(
<Select
showSearch
optionFilterProp="children"
filterOption={(input, option) => option.props.children.indexOf(input) >= 0}
style={{ width: '46%' }}
onChange={this.handleFirstOptionChange.bind(this, topicList)}
placeholder="一级Topic"
>
{firstLevelOptions}
</Select>
)}
<span style={{ marginLeft: 5 }} className="ant-form-text"> - </span>
{getFieldDecorator('second_level_topic', {
rules: [{ required: false, message: '请选择Topic'}],
})(
<Select
showSearch
optionFilterProp="children"
filterOption={(input, option) => option.props.children.indexOf(input) >= 0}
style={{ width: '46%' }}
onChange={this.onSecondOptionChange.bind(this, topicList)}
placeholder="二级Topic"
>
{secondLevelOptions}
</Select>
)}
</FormItem>
</Col>
</code>结果运行出错:
<code>TypeError: Cannot read property 'topics' of undefined
ScriptList.renderSimpleForm
src/routes/Script/ScriptList.js:196
193 | renderSimpleForm() {
194 | const { getFieldDecorator } = this.props.form;
195 |
> 196 | const topicList = this.props.topic.topics;
</code>
自己查找对比了后,最终找到是,在connect中,加上映射,即可获得对应参数:
<code>@connect(({ script, topic, loading }) => ({
script,
topic,
loading: loading.models.script,
}))
@Form.create()
export default class ScriptList extends PureComponent {
</code>
界面上至少出来了一级和二级的topic的筛选下拉框了:

然后再去研究,如何后端加上支持这个筛选的条件的查询:
先去看前端调用的api接口:

是:
<code>http://localhost:xxx/api/v1/scripts/?topic=daily%20routine&second_level_topic=celebrations </code>
然后再去找server端代码
找到了:
apps/script/views.py
加上log日志看看:
<code>class ScriptViewSet(mixins.ListModelMixin,
def list(self, request, *args, **kwargs):
"""
获取 script list,取 author=request.user。按历史记录 version 最新的一个script,
组成列表
"""
search = request.query_params.get('search', '')
publish_status = request.query_params.get('publish_status', '')
#/api/v1/scripts/?topic=Animal&second_level_topic=farm%20animal&publish_status=1
topic = request.query_params.get('topic', '')
second_level_topic = request.query_params.get('second_level_topic', '')
logger.info("search=%s,publish_status=%s,topic=%s,second_level_topic=%s",
search, publish_status, topic, second_level_topic)
</code>果然看到相关的调试期间的输出了:

<code>INFO|20180718 09:42:49|views:list:66|search=,publish_status=,topic=daily routine,second_level_topic=celebrations </code>
然后再去看看如何加进去query的参数:
【已解决】Django项目中的Q中的xxx__icontains是什么意思
然后发现逻辑上有问题,当一级topic和二级topic,是逻辑与的关系,不是或:
<code>filter_condition = filter_condition | Q(topic__name__icontains=topic) </code>
所以要去找如何改为逻辑与
https://docs.djangoproject.com/zh-hans/2.0/topics/db/queries/
搜:|
找到:
“Q objects can be combined using the & and | operators. When an operator is used on two Q objects, it yields a new Q object.”
改为:
<code>filter_condition = filter_condition & Q(topic__name__icontains=topic) </code>
去试试结果是可以的。
然后再去优化:
此处希望二级topic可选,默认不选择
而现在问题是:
最初始化时,一级和二级topic都是默认没选择的:

但是当选择了一级topic,而二级topic默认就会选择(第一个):

希望即使切换选择了一级topic,二级也默认不选择(只是内部列表刷新)
然后用户根据自己需要,决定是否再去选择二级topic
然后改为:
<code> handleFirstOptionChange(topicList, value) {
const { form } = this.props;
form.setFieldsValue({
topic: value,
// second_level_topic: topicList[value][0] || '',
second_level_topic: '', // when first topic change, clear second topic
});
this.setState({
first_level_topic: topicList[value],
// second_level_topic: topicList[value][0] || '',
second_level_topic: '', // when first topic change, clear second topic
});
}
</code>就可以了:

然后再去修复逻辑错误:
之前的查询query的条件,都是逻辑或的,实际上应该是逻辑与
否则就会出现:条件越多,反而查询出来的结果越多了:


然后改为:
<code> filter_condition = Q()
if search:
filter_condition = filter_condition | Q(place__icontains=search) | Q(title__icontains=search)
logger.info("after search: filter_condition=%s", filter_condition)
if publish_status:
if publish_status != '3':
# filter_condition = filter_condition | Q(publish_status=publish_status)
filter_condition = filter_condition & Q(publish_status=publish_status)
logger.info("after publish_status: filter_condition=%s", filter_condition)
if topic:
# filter_condition = filter_condition | Q(topic__icontains=topic)
# filter_condition = filter_condition | Q(topic__name__icontains=topic)
filter_condition = filter_condition & Q(topic__name__icontains=topic)
if second_level_topic:
# filter_condition = filter_condition | Q(second_level_topic__icontains=second_level_topic)
# filter_condition = filter_condition | Q(second_level_topic__name__icontains=second_level_topic)
filter_condition = filter_condition & Q(second_level_topic__name__icontains=second_level_topic)
logger.info("finnal: filter_condition=%s", filter_condition)
</code>才实现了正确的逻辑:
当title或place输入了值后,查询title或place包含该字符的内容,整个作为Q的查询条件和后续的逻辑与&
而publish_status,topic和second_level_topic分别都是逻辑与&
这样才能实现准确的查询
【总结】
此处,为了给剧本列表中添加支持topic搜索,完整的改动是:
web端:
src/common/router.js
<code> '/script/script-detail': {
// component: dynamicWrapper(app, ['script'], () => import('../routes/Script/ScriptDetail')),
component: dynamicWrapper(app, ['script', 'topic'], () => import('../routes/Script/ScriptDetail')),
},
</code>src/routes/Script/ScriptList.js
<code>@connect(({ script, topic, loading }) => ({
script,
topic,
loading: loading.models.script,
}))
@Form.create()
export default class ScriptList extends PureComponent {
state = {
formValues: {},
currentPage: 1,
pageSize: 20,
first_level_topic: [],
second_level_topic: [],
};
componentDidMount() {
const { dispatch } = this.props;
dispatch({
type: 'script/fetch',
});
// topic select 取所有的 topic
const params = {
page_size: 1000,
};
dispatch({
type: 'topic/fetch',
payload: params,
});
}
handleFirstOptionChange(topicList, value) {
const { form } = this.props;
form.setFieldsValue({
topic: value,
// second_level_topic: topicList[value][0] || '',
second_level_topic: '', // when first topic change, clear second topic
});
this.setState({
first_level_topic: topicList[value],
// second_level_topic: topicList[value][0] || '',
second_level_topic: '', // when first topic change, clear second topic
});
}
onSecondOptionChange(topicList, value) {
const { form } = this.props;
form.setFieldsValue({
second_level_topic: value || '',
});
this.setState({
second_level_topic: value || '',
});
}
renderSimpleForm() {
const { getFieldDecorator } = this.props.form;
const topicList = this.props.topic.topics;
const firstLevelOptions = Object.keys(topicList).map(first => <Option key={first}>{first}</Option>);
const secondLevelOptions = this.state.first_level_topic.map(second => <Option key={second}>{second}</Option>);
const selectFormItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7 },
},
wrapperCol: {
xs: { span: 36 },
sm: { span: 18 },
md: { span: 16 },
},
};
return (
<Form onSubmit={this.handleSearch} layout="inline">
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={6} sm={18}>
<FormItem label="Place/Title">
{getFieldDecorator('search')(<Input placeholder="Place/Title" />)}
</FormItem>
</Col>
<Col md={12} sm={18}>
<FormItem {...selectFormItemLayout} label="Topic" style={{ marginBottom: 5 }} >
{getFieldDecorator('topic', {
rules: [{ required: false, message: '请选择Topic'}],
})(
<Select
showSearch
optionFilterProp="children"
filterOption={(input, option) => option.props.children.indexOf(input) >= 0}
style={{ width: '46%' }}
onChange={this.handleFirstOptionChange.bind(this, topicList)}
placeholder="一级Topic"
>
{firstLevelOptions}
</Select>
)}
<span style={{ marginLeft: 5 }} className="ant-form-text"> - </span>
{getFieldDecorator('second_level_topic', {
rules: [{ required: false, message: '请选择Topic'}],
})(
<Select
showSearch
optionFilterProp="children"
filterOption={(input, option) => option.props.children.indexOf(input) >= 0}
style={{ width: '46%' }}
onChange={this.onSecondOptionChange.bind(this, topicList)}
placeholder="二级Topic"
>
{secondLevelOptions}
</Select>
)}
</FormItem>
</Col>
</code>server端:
/apps/script/views.py
<code>class ScriptViewSet(mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
PutOnlyUpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
queryset = Script.objects.all()
def list(self, request, *args, **kwargs):
#/api/v1/scripts/?topic=Animal&second_level_topic=farm%20animal&publish_status=1
topic = request.query_params.get('topic', '')
second_level_topic = request.query_params.get('second_level_topic', '')
logger.info("search=%s,publish_status=%s,topic=%s,second_level_topic=%s",
search, publish_status, topic, second_level_topic)
filter_condition = Q()
if search:
filter_condition = filter_condition | Q(place__icontains=search) | Q(title__icontains=search)
logger.info("after search: filter_condition=%s", filter_condition)
if publish_status:
if publish_status != '3':
# filter_condition = filter_condition | Q(publish_status=publish_status)
filter_condition = filter_condition & Q(publish_status=publish_status)
logger.info("after publish_status: filter_condition=%s", filter_condition)
if topic:
# filter_condition = filter_condition | Q(topic__icontains=topic)
# filter_condition = filter_condition | Q(topic__name__icontains=topic)
filter_condition = filter_condition & Q(topic__name__icontains=topic)
if second_level_topic:
# filter_condition = filter_condition | Q(second_level_topic__icontains=second_level_topic)
# filter_condition = filter_condition | Q(second_level_topic__name__icontains=second_level_topic)
filter_condition = filter_condition & Q(second_level_topic__name__icontains=second_level_topic)
logger.info("finnal: filter_condition=%s", filter_condition)
</code>效果是:

转载请注明:在路上 » 【已解决】给内容管理系统中添加按Topic去筛选