Сигнализация проверки ошибок при назначении виртуального атрибута? - PullRequest
3 голосов
/ 27 ноября 2008

Это вопрос Rails / ActiveRecord.

У меня есть модель, которая в основном должна представлять события или выступления. Каждое событие имеет много атрибуций : атрибуция - это что-то вроде "В этом случае у Лица X была роль Y".

Я пришел к выводу, что лучший способ разрешить пользователю редактировать эти данные - предоставить поле с произвольным текстом, которое ожидает структурированный формат, который я назову строку роли :

singer: Elvis Costello, songwriter: Paul McCartney, ...

где я использую автозаполнение, чтобы завершить как имена ролей (певец, автор песен ...), так и имена людей. И роли, и люди хранятся в базе данных.

Чтобы реализовать это, я создал виртуальный атрибут в модели событий:

def role_string
    # assemble a role string from the associations in the model
end

def role_string=(s)
    # parse a string in the above role string format,
    # look up the People and Events mentioned, and update
    # what's in the database
end

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

Но что, если строка роли деформирована? Хорошо, я полагаю, я могу просто использовать регулярное выражение вместе со стандартной проверкой для проверки формата:

validates_format_of :role_string, :with => /(\w+:\s*\w+)(,\s*\w+:\s*\w+)*/

Но что, если ассоциации, подразумеваемые строкой роли, недействительны? Например, что произойдет, если я приведу приведенную выше строку роли, а Elvis Costello не ссылается на действующего человека?

Я подумал, ну, я мог бы использовать validates_each для атрибута :role_string, чтобы найти ассоциации и выдать ошибку, если, например, одно из приведенных имен не совпадает ни с чем.

У меня два вопроса: во-первых, мне не нравится этот подход, так как для проверки ассоциаций мне нужно было бы проанализировать строку и найти их, что дублирует то, что я буду делать в самом role_string=, кроме для фактического сохранения ассоциаций в базе данных.

Во-вторых, ... как бы я указал, что при присвоении этого виртуального атрибута произошла ошибка?

1 Ответ

3 голосов
/ 01 декабря 2008

Прежде всего, вы неправильно приписываете Человека к Событию. Вместо этого вы должны передать идентификатор человека на событие, а не строку с именем человека. Например, что если человек с идентификатором 230404 и именем «Элвис Костелло» изменит свое имя на «Бритни Спирс»? Что ж, если это произойдет, идентификатор останется прежним, но имя изменится. Однако вы не сможете больше ссылаться на этого человека.

Вам следует настроить ассоциации так, чтобы внешний ключ ссылался на людей в нескольких случаях:

has_one :singer, :class_name => "Person", :foreign_key => "singer_id"
has_one :songwriter, :class_name => "Person", :foreign_key => "songwriter_id"

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

Event.first.singer.name   # => "Elvis Costello"
Event.first.songwriter.name   # => "Britney Spears"

Вы можете изучить доступные проверки для ассоциаций (validates_associated), а также узнать, присутствует ли идентификатор в форме (validates_presence_of). Я бы порекомендовал создать свою собственную пользовательскую проверку, чтобы убедиться, что Персона действительна перед сохранением. Что-то вроде (не проверено):

def before_save
  unless Person.exists?(self.songwriter_id)
    self.errors.add_to_base("Invalid songwriter. Please try again!")
    return false
  end
end

Кроме того, я заметил, что вы ищете для пользователей способ выбрать пользователя, которого следует использовать для ролей в вашем мероприятии. Вот что вы можете сделать в частичной форме:

select(:event, :singer_id, Person.find(:all).collect {|p| [ p.name, p.id ] }, { :include_blank => 'None' })

Надеюсь, это поможет!

...