Чи можуть помічники для маршрутизації Rails (тобто mymodel_path (модель)) використовуватися в моделях?


368

Скажіть, у мене є модель рейки під назвою Thing. У Thing є атрибут url, який за бажанням можна встановити за URL-адресою десь в Інтернеті. Для перегляду коду, мені потрібна логіка, яка робить наступне:

<% if thing.url.blank? %>
<%= link_to('Text', thing_path(thing)) %>
<% else %>
<%= link_to('Text', thing.url) %>
<% end %>

Ця умовна логіка в погляді потворна. Звичайно, я міг би побудувати функцію помічника, яка змінила б погляд на це:

<%= thing_link('Text', thing) %>

Це вирішує проблему багатозначності, але я б волію функціонувати в самій моделі. У такому випадку код перегляду буде таким:

<%= link_to('Text', thing.link) %>

Це, очевидно, вимагало б методу зв'язку на моделі. Ось що воно повинно містити:

def link
  (self.url.blank?) ? thing_path(self) : self.url
end

До питання, thing_path () - це не визначений метод всередині коду Model. Я припускаю, що можна «втягнути» деякі допоміжні методи в модель, але як? І чи є реальна причина, що маршрутизація працює лише на контролері та переглядає шари програми? Я можу придумати безліч випадків, коли модельний код, можливо, повинен мати справу з URL-адресами (інтеграція із зовнішніми системами тощо).


Випадком використання буде: генерування скороченого URL-адреси з goo.gl в післясмак,
lulalala

2
Вам, мабуть, слід загорнути модель у презентатор, якщо ви хочете додати логіку перегляду, це дозволить розділити шари MVC. Дивіться Draper ( github.com/jcasimir/draper ).
Крис

2
Дивіться також розділ "Генерація URL-адрес для названих маршрутів" у документації за адресою api.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html
Backo

Відповіді:


698

У рейках 3, 4 і 5 ви можете використовувати:

Rails.application.routes.url_helpers

напр

Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")

14
Ypu може включити це в будь-який модуль і після нього ви можете отримати доступ до URL-помічників там. Thx
В’ячеслав Молоков

41
Врятуйте себе від копіювання вашої :hostопції скрізь та встановіть її один раз у конфігураційні файли оточуючого середовища:Rails.application.routes.default_url_options[:host] = 'localhost:3000'
Ендрю

5
include Rails.application.routes.url_helpersпрацює для мене в Rails 4.1
січня

1
Якщо вам просто потрібен шлях, ви можете використовувати: only_path => true як параметр, не передаючи параметр хоста.
trueinViso

1
це здається хорошим варіантом: hawkins.io/2012/03/…
Renars Sirotins

182

Я знайшов відповідь щодо того, як це зробити сам. Всередині коду моделі просто покладіть:

Для рейок <= 2:

include ActionController::UrlWriter

Для рейок 3:

include Rails.application.routes.url_helpers

Це магічно змушує thing_path(self)повернути URL-адресу для поточної речі або other_model_path(self.association_to_other_model)повернути якусь іншу URL-адресу.


27
Просто оновлення, оскільки вищезазначене застаріло. Це поточний:include Rails.application.routes.url_helpers
yalestar

2
Я отримую NoMethodError (не визначений метод `optimize_routes_generation? 'Для # <ActionView :: База: 0x007fe8c0eecbd0>), коли я спробую це
moger777

118

Ви також можете знайти наступний підхід чистішим, ніж включення кожного методу:

class Thing
  delegate :url_helpers, to: 'Rails.application.routes' 

  def url
    url_helpers.thing_path(self)
  end
end

7
Документацію щодо цього здавалося важко знайти, тому ось гідне посилання щодо використання делегата в ActiveSupport. simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate
tlbrack

2
Я отримую undefined local variable or method 'url_helpers' for Event:Classпомилку ... :(
Августин Рідінгер

Я отримую undefined method url_helpers. Що я буду робити?
asiniy

@asiniy, ти впевнений, що ти використовував опцію delegate для url_helpers, яка написана після оголошення класу?
Кшиштоф Вітчак

Не працює, але, можливо, це працює лише в тому випадку, якщо ви створили свій власний class, як показано у відповіді. Якщо ваш клас моделі вже розширено, < ApplicationRecordце не працюватиме?
skplunkerin

13

Будь-яка логіка, пов’язана з тим, що відображається у представленні, має бути делегована допоміжним методом, оскільки методи в моделі суворо застосовуються для обробки даних.

Ось що ви могли зробити:

# In the helper...

def link_to_thing(text, thing)
  (thing.url?) ? link_to(text, thing_path(thing)) : link_to(text, thing.url)
end

# In the view...

<%= link_to_thing("text", @thing) %>

1
Що мені в цьому випадку не подобається у допоміжних методах. Коли я дивлюся на link_to_thing (), я повинен вирішити, чи є це помічником для конкретних речей або помічником для всіх програм (це легко може бути будь-яким). Мені потрібно розглянути можливість перевірки 2-х файлів на джерело. thing.link не залишає сумнівів щодо вихідного файлу.
Аарон Лонгвелл

Крім того, що, якщо мені потрібно використовувати цю функціональність у задачі Rake (можливо, експортувати файл CSV з URL-адресами речей) ... перехід безпосередньо до моделі було б набагато кращим і в цьому випадку.
Аарон Лонгвелл

Але різниця полягає в тому, що link_to не буде доступним у моделі, оскільки це помічник ActionView. Отже, це б не спрацювало. Ви можете зламати деякі параметри за замовчуванням атрибутів у моделі, тож якщо він не встановлений, він дефолтує щось, але це залежить від вас.
Джош Дельсман

12
По мірі того, як API API веб-служб зростає, часто моделям потрібно звертатися до зовнішніх ресурсів із власними даними та надавати URL-адреси зворотного дзвінка. Наприклад, фотооб'єкт повинен опублікувати себе в Socialmod, який буде передзвонити до URL-адреси цієї фотографії, коли виконується модерація. Гак after_create () має сенс опублікувати в Socialmod, але як ви знаєте URL зворотного дзвінка? Я думаю, що власна відповідь автора має сенс.
JasonSmith

3
Хоча ви маєте рацію в суто технічному сенсі, бувають випадки, коли людям просто потрібні речі для роботи, без того, щоб голити кожен як, є уникнути порушення будь-якого священного дизайнерського шаблону чи що у вас є, можливо, їм потрібно отримати URL-адресу і фактично зберігати це в базі даних було б зручно тоді, коли модель просто знає, як це зробити, а не передавати речі туди-сюди, іноді правила можуть бути порушені.
nitecoder


1

Хоча може існувати спосіб, який би я схиляв не допускати такої логіки від Моделі. Я погоджуюсь, що ви не повинні ставити це на вигляд ( тримати його худим ), але, якщо модель не повертає URL-адресу як частину даних до контролера, дані про маршрутизацію повинні бути в контролері.


4
Re: "Якщо модель не повертає URL-адресу як фрагмент даних". Ось саме це і відбувається тут ... Модель "володіє" цією частиною даних, яка є або посиланням на URL-адресу на сайті, або за його межами. У деяких випадках URL генерується за допомогою маршрутизації Rails. В інших випадках URL - це дані, що надаються користувачем.
Аарон Лонгвелл

0

(Редагувати: забути мою попередню лайку ...)

Гаразд, можуть бути ситуації, коли ви переходите або до моделі, або до якоїсь іншої URL-адреси ... Але я не думаю, що це належить до моделі, погляд (або, можливо, модель) звучить більш доречно.

Щодо маршрутів, наскільки я знаю, маршрути стосуються дій у контролерах (які зазвичай "магічно" використовують вид), а не безпосередньо для переглядів. Контролер повинен обробляти всі запити, представлення має представляти результати, а модель повинна обробляти дані та подавати їх перегляду чи контролеру. Тут я чув багато людей, які розмовляють про маршрути до моделей (до тих пір, як я все це починаю обробляти), але, як я розумію, маршрути переходять до контролерів. Звичайно, багато контролерів є контролерами для однієї моделі і їх часто називають <modelname>sController(наприклад, "UsersController" є контролером моделі "Користувач").

Якщо вам здається писати неприємну кількість логіки в погляді, спробуйте перемістити логіку кудись більш підходящим; Логіка запиту і внутрішня комунікація, ймовірно, належить до контролера, логіка, пов'язана з даними, може бути розміщена у моделі (але не логіка відображення, яка включає теги посилань тощо), а логіка, яка суто пов'язана з відображенням, буде розміщена в помічнику.


Скажімо, у вас є модель зображення. Якщо Зображення має з ним пов’язану зовнішню URL-адресу, то вкажіть на цю URL-адресу. В іншому випадку вкажіть на сторінку показу зображень, щоб завантажити зображення? Просто ідея.
Джош Дельсман

Як щодо цього менш загального прикладу: link_to "Зображення в повному розмірі", image.link Метод посилання в моделі буде або посилатися на URL-адресу сайту (image_path), або на, скажімо, URL-адресу Flickr, якщо користувач надав та .url було встановлено на зображенні.
Аарон Лонгвелл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.