原本的系统中,不支持按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去筛选