ActiveModel Serializers:has_many在运行时有条件吗?

时间:2017-02-15 08:33:29

标签: ruby-on-rails active-model-serializers

我使用rails(5.0.1)和active_model_serializers(0.10.2)。我想以某种方式有条件地序列化import React, {Component} from 'react'; import {reduxForm} from 'redux-form'; import {connect} from 'react-redux'; import map from 'lodash/map'; import timezones from '../../data/timezones'; import styles from '../formElements/formElements.scss'; import {registerUser} from '../../actions/actionRegister'; import {formUpdate} from '../../actions/actionForm'; import FieldGroup from '../formElements/FieldGroup'; import { Form, FormControl, Col, Checkbox, Button, FormGroup } from 'react-bootstrap'; // {... props} passing large number of props wrap in object with spread notation class SignupForm extends Component { //if component have state it needs to be class constructor(props) { super(props); this.state = { errors: { //this errors are irrelevant for now name:'', email: '', password: '', passwordConfirmation:'', timezone:'' }, }; } onChange = (event, index, value) => { this.props.onChange(event.target.name, event.target.value); }; onSave = (event) => { event.preventDefault(); this.props.onSave(this.props.values); } render() { let isLoading = this.props.isLoading; return ( // this.props.handleSubmit is created by reduxForm() // if the form is valid, it will call this.props.onSubmit <Form onSubmit={this.onSave} horizontal> <FieldGroup id="formControlsName" type="text" label="Name" name="name" placeholder="Enter Name" value={this.props.values[name]} onChange={this.onChange} help={this.state.errors.name} /> <FieldGroup id="formControlsEmail" type="text" label="Email" name="email" placeholder="Enter Email" value={this.props.values[name]} onChange={this.onChange} help={this.state.errors.email} /> <FieldGroup id="formControlsPassword" type="password" label="Password" name="password" placeholder="Enter Password" value={this.props.values[name]} onChange={this.onChange} help={this.state.errors.password} /> <FieldGroup id="formControlsPasswordConfirmation" type="password" label="Password Confirmation" name="passwordConfirmation" placeholder="Enter Password" value={this.props.values[name]} onChange={this.onChange} help={this.state.errors.passwordConfirmation} /> <FieldGroup id="formControlsTimezone" label="Time Zone" name="timezone" placeholder="Select Time Zone" componentClass="select" defaultValue="Select Your Timezone" value={this.props.values[name]} onChange={this.onChange} help={this.state.errors.timezone} > <option value="Select Your Timezone">Select Your Timezone</option> { map(timezones, (key, value) => <option key={key} value={key}>{value}</option>) } </FieldGroup> <FormGroup> <Col smOffset={4} sm={8}> <Checkbox>Remember me</Checkbox> </Col> </FormGroup> <FormGroup> <Col smOffset={4} sm={8}> <Button type="submit" disabled={isLoading} onClick={!isLoading ? isLoading : null} > { isLoading ? 'Creating...' : 'Create New Account'} </Button> </Col> </FormGroup> {this.props.errorMessage && this.props.errorMessage.register && <div className="error-container">{this.props.errorMessage.register}</div>} </Form> //this.setState({ disabled: true }); //this.props.errorMessage.register == this.props = {errorMessage :{ register: ''}} ); } } function mapStateToProps(state) { return { errorMessage: state.signup.error, isLoading: state.signup.isLoading, values: state.form.values }; } function mapDispatchToProps(dispatch) { return { onSave: (values) => dispatch(registerUser(values)), onChange: (name, value) => dispatch(formUpdate(name, value)) }; } export default connect(mapStateToProps, mapDispatchToProps)(SignupForm) 关联:

has_many

我使用jsonapi并查询class Question < ApplicationRecord has_many :responses, :inverse_of => :question end class Response < ApplicationRecord belongs_to :question, :inverse_of => :responses end class QuestionSerializer < ActiveModel::Serializer attributes :id, :title, :created_at, :updated_at has_many :responses end class ResponseSerializer < ActiveModel::Serializer attributes :id, :title end 我得到了这个回复:

响应-1

http://localhost:3000/api/questions/1

如果我从{ "data": { "id": "1", "type": "questions", "attributes": { "title": "First", "created-at": "2017-02-14T09:49:20.148Z", "updated-at": "2017-02-14T13:55:37.365Z" }, "relationships": { "responses": { "data": [ { "id": "1", "type": "responses" } ] } } } } 删除has_many :responses,我就明白了:

响应-2

QuestionSerializer

我如何有条件地在运行时获得 Response-1 Response-2 ?我尝试了所有建议 - 不适用于AMS 0.10.2。目前,条件只有这样:

{
  "data": {
    "id": "1",
    "type": "questions",
    "attributes": {
      "title": "First",
      "created-at": "2017-02-14T09:49:20.148Z",
      "updated-at": "2017-02-14T13:55:37.365Z"
    }
  }
}

OR:

class QuestionSerializer < ActiveModel::Serializer
  attributes :id, :title, :created_at, :updated_at
  has_many :responses if true
end

在这2个案例中,我真的得到 Response-1 Response-2 。但这是硬编码的,我想将一个参数传递给序列化器或做一些类似的事情。

我该怎么办?

4 个答案:

答案 0 :(得分:2)

我认为你已经回答了自己的问题。如果您查看AMS documentation for associations,它会说支持条件。

从我可以告诉你的只是一个错字

class QuestionSerializer < ActiveModel::Serializer
   has_many :responses, if: false
end

attributes方法也支持if选项,如here所述。

你的active_model_serializers版本是什么?

修改: 我的答案也有错误。我正在使用active_model_serializers (0.10.3)而我能够

class QuestionSerializer < ActiveModel::Serializer
   has_many :responses, if: -> { false }
end

if选项适用于方法,过程或字符串。我认为你可以在运行时通过提供一个方法作为条件来决定。

class QuestionSerializer < ActiveModel::Serializer
  attr_writer :should_render_association
  has_many :responses, if: -> { should_render_association }
end
# Usage: 
serializer = QuestionSerializer.new(question)
serializer.should_render_association = false
serializer.to_json
# => no "responses" key

答案 1 :(得分:2)

感谢@gkats,我找到了答案(AMS 0.10.2):

class QuestionSerializer < ActiveModel::Serializer
  attributes :id, :title, :created_at, :updated_at
  has_many :responses, if: -> { should_render_association }

  def should_render_association
    @instance_options[:show_children]
  end
end

class Api::ResponsesController < Api::ApplicationController
  def show
    render json: @response, show_children: param[:include_children]
  end
end

问题在于语法:序列化程序中的if:应该应用于块而不是函数。

答案 2 :(得分:0)

我为同一问题苦苦挣扎。这是我想出的有效解决方案。

except: [:key_one, :key_two]作为参数进行初始化。

class QuestionsController
    def index
        @questions = Question.all
        render(json: ActiveModel::ArraySerializer.new(@questions,
                                                      each_serializer: QuestionSerializer,
                                                      root: 'questions',
                                                      except: [:responses])
        )
    end

    def show 
        # you can also pass the :except arguments here
        # render(json: QuestionSerializer.new(@question, except: [:responses]).to_json)
        render(json: QuestionSerializer.new(@question).to_json)
    end
end

https://www.rubydoc.info/gems/active_model_serializers/0.9.3/ActiveModel%2FArraySerializer:initialize

https://www.rubydoc.info/gems/active_model_serializers/0.9.1/ActiveModel%2FSerializer:initialize

答案 3 :(得分:0)

这里是如何从父串行器传递参数,并在子串行器中基于这些参数显示或隐藏属性。

父级序列化器:

class LocationSharesSerializer < ActiveModel::Serializer
  attributes :id, :locations, :show_title, :show_address
     
  def locations
    ActiveModelSerializers::SerializableResource.new(object.locations, {
      each_serializer: PublicLocationSerializer,
      params: { 
        show_title: object.show_title
      },
    })
  end

end

子序列化器

class PublicLocationSerializer < ActiveModel::Serializer
  attributes :id, :latitude, :longitude, :title, :directions, :description, :address, :tags, :created_at, :updated_at, :photos

  def title
    object.title if @instance_options[:params][:show_title]
  end

end