#ruby-on-rails
#ruby-on-rails
Вопрос:
Я работаю через RailsTutorial, но создаю веб-приложение «Объявления» для средней школы, в которой я преподаю (настройка данного клона Twitter).
Когда пользователи создают объявление, они используют флажки, чтобы определить, для каких оценок оно должно отображаться (1-3 оценки могут быть истинными). Это работает правильно, и я сохраняю оценки как логические значения.
create_table "announcements", :force => true do |t|
t.string "content"
t.integer "user_id"
t.boolean "grade_6"
t.boolean "grade_7"
t.boolean "grade_8"
t.date "start_date"
t.date "end_date"
t.datetime "created_at"
t.datetime "updated_at"
конец
У моих пользователей также есть поле оценки, которое является целым числом. Я хочу использовать это, чтобы на домашней странице каждого пользователя отображались объявления для их оценки.
Пример: у учителя 8-го класса оценка = 8. Когда они входят в систему, на их домашней странице должны отображаться только объявления, которые имеют grade_8 = TRUE.
Пример: директор имеет оценку = 0. Когда они входят в систему, на их домашней странице должны отображаться все объявления.
Я пытаюсь понять, как перевести целочисленное значение user.grade в логические флаги для извлечения объявлений из модели.
Код, который я пишу, работает, но невероятно неуклюж. Пожалуйста, помогите мне сделать что-то более элегантное! Я не привязан к этой модели БД, если у вас есть идея получше. (На самом деле, мне действительно не нравится эта модель БД, поскольку я жестко определяю количество оценок в нескольких местах).
# Code to pull announcements for the home page
def feed
case grade
when 6
grade_6
...
else
grade_all
end
end
# Example function to pull announcements for a grade
def grade_6
Announcement.where("grade_6 = ? AND start_date >= ? AND end_date <= ?",
TRUE, Date.current, Date.current)
Ответ №1:
правильный способ установить этот тип отношений — использовать связь «многие ко многим» через has_many через:
class Announcement < ActiveRecord::Base
has_many :announcement_grades
has_many :grades, :through => :announcement_grades
end
class AnnouncementGrades < ActiveRecord::Base
belongs_to :grade
belongs_to :announcement
end
class Grade < ActiveRecord::Base
has_many :announcement_grades
has_many :announcements, :through => :announcement_grades
end
тогда ваши миграции будут:
create_table :announcements, :force => true do |t|
t.date :start_date
t.date :end_date
t.timestamps #handy function to get created_at/updated_at
end
create_table :announcement_grades, :force => true do |t|
t.integer :grade_id
t.integer :announcement_id
t.timestamps
#start and end date might be more appropriate here so that you can control when to start and stop a particular announcement by grade rather than the whole announcement globally, depending on your needs.
end
create_table :grades, :force => true do |t|
t.timestamps
#now you have a bona-fide grade object, so you can store other attributes of the grade or create a relationship to teachers, or something like that
end
итак, теперь вы можете просто найти свою оценку, а затем вызвать объявления для фильтрации:
@grade = Grade.find(params[:id])
@announcements = @grade.announcements
итак, это правильный способ сделать это с точки зрения моделирования. есть и другие соображения по поводу этого рефакторинга, поскольку вам придется внести существенные изменения в свои формы и контроллеры для поддержки этой парадигмы, но это также обеспечит гораздо большую гибкость и надежность, если вы решите, что хотите присоединить к классу другие типы объектов, помимо просто объявлений. этот railscast демонстрирует, как управлять несколькими моделями с помощью одной формы с помощью вложенных элементов формы, это поможет вам сохранить внешний вид и ощущения после применения изменений к вашим моделям. Я надеюсь, что это поможет, дайте мне знать, если вам понадобится дополнительная помощь в этом, это будет немного работы, но в конце концов оно того стоит.
Комментарии:
1. Спасибо, Крис! К сожалению, мне не хватает достаточной кармы, чтобы проголосовать за вас, но это именно то, что я искал («rails way» против моего хакерского способа его снять).
2. нет проблем, Даниэль, не стесняйтесь обращаться ко мне напрямую, если у вас возникнут другие вопросы по ходу работы. вы можете принять ответ, за это вы получите значок, это галочка прямо под стрелками голосования
3. Привет, Крис, еще раз спасибо за вашу помощь. Есть ли у вас какие-либо советы по настройке этого как отношения has_and_belongs_to_many и отказу от таблицы annoucement_grades? Я не вижу, чтобы я что-то делал с announcement_grades (кроме объединения).
4. настроить его как habtm довольно просто, это описано здесь: api.rubyonrails.org/classes/ActiveRecord/Associations/… . Но каждое отношение habtm, которое я настроил, я в какой-то момент изменил на has_many, потому что вы не получаете гибкости с habtm. вам все равно нужно создать таблицу соединений, но для управления ею нет модели, и вы не можете хранить в ней другие столбцы. это будут только pk из двух таблиц.
Ответ №2:
Пример Криса теоретически превосходит. Однако ваша исходная схема может быть более практичной, если 1) вы знаете, что ваше приложение не станет более сложным, и 2) американская система k-12 останется (я бы поставил на это …). Если вы предпочитаете придерживаться схемы, которая у вас уже есть, вот некоторые улучшения, которые вы могли бы внести в код:
Давайте добавим область «оценки» в вашу модель объявлений
class Announcement < ActiveRecord::Base
....
scope :grade, lambda do |num|
num > 0 ? where("grade_#{num} = ?", true) : where('1=1')
end
....
end
Это позволило бы значительно упростить кодирование, например
teacher = User.find(user_id)
announcements = Announcement.grade(teacher.grade).where('start_date >= :today AND end_date <= :today', {:today => Date.today})
Комментарии:
1. Это тоже очень полезно! Я собираюсь использовать идею Криса (потому что я думаю, что многому научусь в результате рефакторинга), но мне очень нравится ваш более элегантный хак. «Where (‘1 = 1’) — забавный хак — это обычная вещь при написании кода SQL / db?
2. В крайнем случае это не неслыханно. В этом случае это было бы уместно, только если бы это был быстрый и грязный проект, который должен был быть завершен завтра. Но если вы подходите к этому как к возможности обучения, то я настоятельно рекомендую вам следовать совету Криса и делать это «правильным» способом.
3. Действительно, вам всегда нужно взвешивать архитектурные решения из-за ограничений ресурсов. Было бы правильно сделать это таким образом, просто менее гибким и простым в реализации