2

У меня есть Rails 4.1.7 приложения, где мы имеем users и themes:Rails4 метод коррекции has_many геттер

class User < ActiveRecord::Base 
    has_many :themes 
end 

class Theme < ActiveRecord::Base 
    belongs_to :user 
end 

Теперь пользователь может создавать некоторые собственные темы (то эти темы будут иметь их user_id набор для пользователя id), или может использовать любого из предустановленных тем (имеющая user_id набор для null)

что я хотел бы сделать это следующим образом: каким-то образом изменить has_many ассоциации так, что, когда я называю @user.themes, это принесет мне го e предустановленные темы, а также темы пользователя.

То, что я пробовал:

1) определить метод экземпляра вместо has_many ассоциации:

class User < ActiveRecord::Base 
    def themes 
    Theme.where user_id: [id, nil] 
    end 
end 

, но так как я хотел бы хотелось нагружать темы с пользователем (s) (includes(:themes)), что на самом деле не будет.

2) использовать некоторый объем (has_many :themes, -> { where user_id: nil }), но он дает запросы mySql, такие как ... WHERE user_id = 123 AND user_id IS NULL, который возвращает пустой. Я думаю, что с Rails5 я мог бы сделать это с чем-то вроде has_many :themes, -> { or.where user_id: nil }, но изменение версии Rails сейчас не вариант.

+0

Я думаю, что это плохая идея переписать ассоциацию, потому что 'Тема 'без какого-либо пользователя, указанного на самом деле, принадлежит каждому пользователю (многие пользователи), это не отношение' has_one-has_many'. Я бы рекомендовал использовать другой метод для достижения вашей цели. – Ilya

+0

Да, это то, что я сейчас использую. Только, было бы намного приятнее загружать его иногда ... – Misu

ответ

-1

Так разместить свой вопрос, я пытался много вещей, чтобы достичь своей цели. Один из них был интересный и, я думаю, стоит отметить:

Я попытался unscope в has_many assiciation использованием unscope или rewhere, и это выглядело так:

has_many :themes, -> user = self { # EDIT: `= self` can even be omitted 
    u_id = user.respond_to?(:id) ? user.id : user.ids 

    unscope(where: :user_id).where(user_id: [u_id, nil]) 
    # or the same but with other syntax: 
    rewhere(user_id: [u_id, nil]) 
} 

Когда я попытался @user.themes, она работала как Интересно, и дал следующую строку: MYSQL

SELECT `themes`.* FROM `themes` 
     WHERE ((`themes`.`user_id` = 123 OR `themes`.`user_id` IS NULL)) 

Но когда я попытался загрузить его нетерпеливое (почему я начал свое исследование после того, как все), он просто отказался unscope запроса и дал то же самое старые user_id = 123 AND user_id = NULL линия.

В конце концов, @ комментарий Ильи убедили меня вместе с this ответ, что это одна вещь, чтобы использовать has_many для выполнения запросов, но есть и другие стороны, как назначение, например, и опрокинув его ради одного может испортить многое другое вещи.

Так что я решил остаться с моим красивым способом, только я дал ему более конкретное название, чтобы в будущем избежать путаницы:

def available_themes 
    Theme.where user_id: [id, nil] 
end 

Что касается @ ответ AndreyDeineko в - так как он постоянно отказывается отвечать на мой вопрос, всегда отвечая на то, о чем никогда не спрашивали, - я все еще не понимаю, почему его метод (с теми же результатами, что и мой available_themes, но с использованием 3 дополнительных запросов) будет лучшим решением.

+0

Почему именно downvote? – Misu

1

Вы можете использовать super (который возвращает связанные темы) в сочетании с теми предопределёнными (где user_id является nil):

class User < ActiveRecord::Base 
    def themes 
    Theme.where(id: super.ids + Theme.where(user_id: nil).ids) 
    end 
end 
+0

Я верну массив вместо отношения. – Ilya

+0

@ Илья хороший момент, спасибо! Отредактировано –

+0

Я действительно не понимаю, почему это лучшее решение, тогда 'Theme.where (user_id: [id, nil])' ... Я имею в виду, что он использует 3 дополнительных запроса db, получает тот же результат и не хочет загружаемый. – Misu