2016-02-21 2 views
1

Я пытаюсь сохранить атрибуты связанной модели через метод setter.Rails: как получить объект в методе setter?

Мой код:

class Task < ActiveRecord::Base 
    belongs_to :project 
    belongs_to :employee 

    attr_accessor :company_id 
    attr_accessor :employee_name 

    def employee_name 
     employee.try(:name) 
    end 

    def employee_name=(name) 
     self.employee = Employee.find_or_create_by(name: name, 
     company_id: company_id.to_i) if name.present? 
    end 
end 

Это, однако, это сохраняет атрибут как nil.

Почему атрибут company_id становится nil при использовании внутри метода сеттера?

Его значение доступно в модели вне метода сеттера.

validate :is_company_id_available 

def is_company_id_available 
    if company_id != nil 
      errors.add(:task, "#{company_id.inspect}") 
     end 
end 

возвращает сообщение company_id в сообщении об ошибке.

Кроме того, вручную определение company_id = 1 внутри метода setter экономит только штраф.

Как правильно установить атрибут внутри метода setter?

EDIT:

Возможно ли, что атрибут company_id каким-то образом должен быть установлен в качестве аргумента этого метода?

Возможно, я смогу обновить company_id после создания записи employee.

+0

Я не вижу employee_company_id определяется где-нибудь – Vic

+0

@Vic, извиняться. Я передаю атрибут непосредственно из формы с помощью 'attr_accessor'. Это не лучшее решение, но я пытаюсь решить одну проблему за раз. – Matthias

ответ

0

В Rails путь является использование accepts_nested_attributes_for:

class Employee 
    has_many :tasks 
    accepts_nested_attributes_for :tasks 
end 

Employee.create(name: 'max', tasks_attributes: [{ description: 'Fetchez La Vache' }]) 

Конечно, Вы можете также сделать обратное:

class Task 
    belongs_to :employee 
    accepts_nested_attributes_for :employee 
end 
Task.create(description: 'Fetchez La Vache', employee_attributes: { name: 'max' }) 

Если вложенные атрибуты содержат идентификатор это обновление вложенную запись ,

@task = @employee.tasks.create(description: "Dot the T's") 
@employee.update(tasks_attributes: [{ id: @task.id, description: "Cross the T's" }]) 
@task.reload.description == "Cross the T's" # true 

Следует проявлять осторожность, чтобы избежать многих слоев вниз. Вы попадаете в ужасные наддутые контроллеры и очень сложную логику.

Хотя возможно было бы настроить делегирование, поскольку вы пытались его довольно беспорядочно, и вы должны помнить, что родительская запись должна быть вставлена ​​в БД до того, как детская запись сможет получить parent_id.


Многоуровневая пример

class Project 
    has_many :projects 
end 

class Task 
    belongs_to :employee 
    accepts_nested_attributes_for :employee, reject_if: :employee_exists? 

    def employee_exists?(attrs) 
    return true if employee.any? # its set already 
    e = Employee.find_by(name: attrs[:name] company_id: attrs[:company_id]) 
    employee = e if e 
    e.any? 
    end 
end 

class Employee 
    has_many :tasks 
end 

<%= form_for(@project) do |p| %> 
    <%= fields_for(:tasks) do |t| %> 
    <p>Select an employee</p> 
    <%= f.collection_select(:employee_id, Employee.all, :id, :name, prompt: true) %> 
    <p>Or create a new one</p> 
    <%= fields_for(:employee) do |e| %> 
     <%= f.text_field(:name) %> 
     <%= f.collection_select(:company_id, Company.all, :id, :name, prompt: true) %> 
    <% end %> 
    <% end %> 
<% end %> 

Вы обычно сочетают это с немного JavaScript, который серость из полей сотрудников, когда пользователь выбирает существующую запись. Однако, если у вас есть опция, лучше использовать AJAX для создания вложенных записей.

Это позволяет создавать пользовательские функции как ряд атомных транзакций, а не одну мегаформу или умирать от представления.

+0

Привет @Max, спасибо за ваше предложение. Я боюсь, что это не сработает. Форма создается из @projects. Я уже использую 'accepts_nested_attributes' в моей вложенной модели' Task'. Каждый новый 'project' имеет много« задач », которые должны найти или создать одного сотрудника, следовательно, виртуальные атрибуты. Любые предложения о том, как поместить переменную 'company_id' в метод setter? Еще раз спасибо! – Matthias

+0

Я предполагаю, что он не работает, так как ваш сеттер не будет запускаться перед установщиком 'company_id ='. Я бы попытался заставить его работать с accepts_nested_attributes или попробовать другое решение в целом, например, отдельные формы. Как вы можете все время и время решения проблемы не очень легко получить право. – max

+0

* Как видите, – max

0

Ответ на макс - это релятивистский способ сделать это.

Но есть attr_accessor :company_id действительно необходимо? Попробуйте удалить его или просто используйте вместо этого read_attribute(:company_id).to_i или self[:company_id].to_i.

[Редактировать]

Я думаю, что ваш PARAMS должен иметь {task_attributes: {company_id: company_id}}

или это проблема, которая возникает только тогда, когда attributes= используется. Вы можете переопределить attributes= и играть вокруг него

def attributes=(task_attributes) 
    binding.pry 
end 

[Edit 2]

Также убедитесь, что task_attributes: [:company_id] разрешается при сильных параметров

+0

Привет @jmmaniego, спасибо за ваш ответ. 'company_id' разрешено в сильных параметрах, следовательно, его доступность в модели' Task'. 'self.company_id' дает мне правильный идентификатор, но' read_attribute (: company_id) 'или' self [: company_id] 'оба являются« nil »в моей модели. Все - 'nil' внутри метода setter. Не уверен в 'def attributes =', я это рассмотрю! Возможно ли, что атрибут 'company_id' каким-то образом должен быть установлен как аргумент метода setter? – Matthias

Смежные вопросы