#ruby-on-rails #ruby-on-rails-3 #activerecord #associations
#ruby-on-rails #ruby-on-rails-3 #activerecord #ассоциации
Вопрос:
Мне это кажется ошибкой в Rails, но, вероятно, я мало что могу с этим поделать. Итак, как я могу выполнить ожидаемое поведение?
Предположим, у нас есть:
class User < ActiveRecord::Base
has_many :awesome_friends, :class_name => "Friend", :conditions => {:awesome => true}
end
И выполнить код:
>> my_user.awesome_friends << Friend.new(:name=>'jim')
Впоследствии, когда я проверяю этот объект friend, я вижу, что поле user_id заполнено. Но я бы также ожидал увидеть, что для столбца «awesome» установлено значение «true», которым оно не является.
Кроме того, если я выполню следующее с консоли:
>> my_user.awesome_friends << Friend.new(:name=>'jim')
>> my_user.awesome_friends
= [#<Friend id:1, name:"jim", awesome:nil>]
# Quit and restart the console
>> my_user.awesome_friends
= []
Есть мысли по этому поводу? Я полагаю, что хэш условий может быть произвольно сложным, что делает невозможной интеграцию в установщик. Но в некотором смысле кажется, что по умолчанию мы передаем условие «:user_id => self.id «, и это устанавливается, так что не должны ли другие?
Спасибо, Майк
Редактировать:
Я обнаружил, что для has_many существуют обратные вызовы, поэтому я думаю, что мог бы определить взаимосвязь следующим образом:
has_many :awesome_friends,
:class_name => "Friend",
:conditions => {:awesome => true},
:before_add => Proc.new{|p,c| c.awesome = true},
:before_remove => Proc.new{|p,c| c.awesome = false}
Хотя, начинает казаться, что, возможно, я просто реализую какой-то другой, существующий шаблон проектирования. Может быть, мне следует создать подкласс AwesomeFriend < Друг? В конечном счете, мне нужна пара таких отношений has_many, а при создании подкласса get все дополнительные файлы запутаны..
РЕДАКТИРОВАТЬ 2:
Хорошо, спасибо всем, кто прокомментировал! В конечном итоге я превратил описанный выше метод в приятное маленькое расширение ActiveRecord ‘has_many_of_type’. Который работает следующим образом:
has_many_of_type :awesome_friends, :class_name => "Friend", :type=>:awesome
Который просто преобразуется в has_many с соответствующими условиями, параметрами before_add и before_remove (и это предполагает существование столбца с именем friend_type).
Ответ №1:
Вам нужно использовать:
my_user.awesome_friends.create(:name=>'jim')
или my_user.awesome_friends.build(:name=>'jim')
В документации: has_many (:conditions)
Область создания записей из ассоциации ограничена, если используется хэш. has_many :posts, :conditions => {:published => true} создаст опубликованные сообщения с помощью @blog.posts.create или @blog.posts.build.
Комментарии:
1. Приятно! Я забыл об этом. Хотя это не решает проблему добавления / удаления существующего друга. Хотя, спасибо, я, вероятно, использую это для своей реализации.
2. Да. Я не думаю, что он когда-либо изменит существующий объект при добавлении его в коллекцию, кроме как для установки внешнего ключа.
3. Принял это, потому что это было лучшее решение проблемы, которую я поставил. Однако окончательную реализацию смотрите в изменениях в вопросе.
4. Я бы создал методы add_to_awesome_friends и remove_from_awesome_friends и внедрил в него логику с использованием существующего объекта «Friend».
Ответ №2:
Во-первых, это :class_name
скорее, чем :class
.
Ответ №3:
Я не думаю, что это ошибка. Хэш :conditions определяет только то, как вы запрашиваете объекты. Но я не думаю, что рационально просто предполагать, что любой объект, который вы помещаете в коллекцию, может быть создан в соответствии с условиями.
В вашем простом примере это имеет смысл, но вы также могли бы добавить туда более сложную логику.
Документация также кажется довольно ясной по этому поводу:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
:conditions
Укажите условия, которым должен соответствовать связанный объект, чтобы быть включенным в качестве
WHERE
фрагмента SQL, напримерauthorized = 1
.
Комментарии:
1. Я согласен, документация довольно ясна, это было первое, что я проверил. Но на практике это действительно не имеет смысла для меня. Я думаю, мне просто нужно быть осторожным, чтобы установить соответствующие поля в моем объекте «Friend» перед добавлением в коллекцию.
2. В этом случае я бы предпочел создать метод add_friend, который выполняет логику за вас.