Rails: вызов другого действия контроллера из контроллера

118

Мне нужно вызвать действие create в контроллере A из контроллера B.

Причина в том, что мне нужно перенаправить иначе, когда я звоню с контроллера B.

Можно ли это сделать в Rails?

ddayan
источник
2
Возможный дубликат stackoverflow.com/questions/4581269/… и stackoverflow.com/questions/3370234/…
Рэйчел Шаллит
Вы говорите о действии POST или GET? Если GET, вы можете просто перенаправить на это действие. Но причина этого не совсем ясна, поскольку вы можете перенаправить с контроллера A на любой URL-адрес, который хотите.
Voldy
возможный дубликат вызова диспетчера из другого
Ciro Santilli 郝海东 冠状 病 六四 事件

Ответы:

64

Вы можете использовать перенаправление на это действие:

redirect_to your_controller_action_url

Подробнее: Руководство по Rails

Чтобы просто отобразить новое действие:

redirect_to your_controller_action_url and return
Спирос
источник
5
Извините, но вы можете сказать, в чем разница между первой и второй строками? Думаю, сначала выполню оставшиеся строки кода, а потом перенаправлю. Это правильно?
АКС
Я думаю, что, может быть, последний пример был опечаткой и должен быть renderвместо redirect_to. Что скажешь, @Spyros?
Magne
1
Привет, @spyros, redirect_toне позволяет использовать, post: :methodи это может быть полезно, в частности, для перенаправления на уже существующее createдействие другого контроллера, как @ddayan спросил в первый раз. У меня похожая ситуация, когда в какой-то ситуации я должен создать другой объект. Вызов другого createдействия может быть DRYer ..
SanjiBukai
53

Чтобы использовать один контроллер от другого, сделайте следующее:

def action_that_calls_one_from_another_controller
  controller_you_want = ControllerYouWant.new
  controller_you_want.request = request
  controller_you_want.response = response
  controller_you_want.action_you_want
end
Сэмми Ларби
источник
18
Если бы вы хотели, чтобы обратные вызовы по-прежнему выполнялись, controller_you_wantвы бы это сделалиcontroller_you_want.process(:action_you_want)
Константин Шепенс,
3
Спасибо! Это очень полезно в ситуациях, когда вы не хотите перенаправлять, вам просто нужно быстрое решение для переноса действия от одного контроллера к другому. Перенаправление - это совсем другое дело. Также: render status: :ok, json: JSON.parse(controller.render(:action_you_want).first)похоже, работает, чтобы вернуть JSON от другого контроллера
Yourpalal
1
Отличный ответ, именно то, что я искал. Один вопрос - какой формат здесь должны быть параметры запроса? Я предполагаю, что они живут, controller_you_want.requestно не смогли получить этот запуск с передачей хэша или экземпляра параметров.
SRack
Я не уверен, о чем вы спрашиваете @SRack. Они paramsстановятся доступными controller_you_want, установив requestв 3-й строке. Вы об этом спрашиваете?
Сэмми Ларби
1
пробовал в рельсах 5, для рендеринга надоrender html: controller_you_want.process(:action_you_want)
назар кулиев
41

Представленная вами логика не совместима с MVC и не с Rails.

  • Контроллер отображает представление или перенаправление

  • Метод выполняет код

Исходя из этих соображений, я советую вам создавать методы в вашем контроллере и вызывать их из вашего действия.

Пример:

 def index
   get_variable
 end

 private

 def get_variable
   @var = Var.all
 end

Тем не менее, вы можете делать то же самое через разные контроллеры и вызывать метод из контроллера A, пока вы находитесь в контроллере B.

Словарь очень важен, поэтому я очень настаиваю.

apneadiving
источник
Я знаю, что не должен этого делать, но это не часть моего приложения, а только для тестирования и оценки моего приложения.
ddayan
9
Обожаю ... отделите метод от рендеринга, а затем вызовите отдельный метод там, где это необходимо. Только один вопрос .. как get_variableтеперь можно вызвать с другого контроллера?
FloatingRock
1
@FloatingRock только что заметил ваш вопрос / комментарий: у вас есть несколько вариантов: можно определить общего предка или включить общий
миксин
люблю ответ, неуверенный в завершающем фрагменте предложения
Джерард Симпсон
30

Вы можете использовать url_forдля получения URL-адреса контроллера и действия, а затем использовать redirect_toдля перехода по этому URL-адресу.

redirect_to url_for(:controller => :controller_name, :action => :action_name)
Остин
источник
1
другой, похоже, у меня не работал, он выглядит лучше, но как передать параметры?
msanjay
3
@msanjay вы можете передать их как другие параметры в url_for. Например, redirect_to url_for(:controller => :controller_name, :action => :action_name, :param1 => :val1, :param2 => :val2)будет результат /contorller_name/action_name?param1=val1&param2=val2. См. Документацию
Ариэль Аллон
если я попытаюсь перенаправить на корневой контроллер, такой как «MyOtherController», с контроллера, такого как «Module :: MyController» .. он разрешит вызов «module / MyOtherController» .. любая идея, как я могу вызвать root?
ggez44
12

Это плохая практика - вызывать другое действие контроллера.

Вам следует

  1. продублируйте это действие в вашем контроллере B, или
  2. оберните его как модельный метод, который будет доступен всем контроллерам, или
  3. вы можете расширить это действие в контроллере A.

Мое мнение:

  1. Первый подход не является СУХИМ, но он все же лучше, чем призыв к другому действию.
  2. Второй подход хорош и гибок.
  3. Третий подход - это то, что я делал часто. Я покажу небольшой пример.

    def create
      @my_obj = MyModel.new(params[:my_model])
      if @my_obj.save
        redirect_to params[:redirect_to] || some_default_path
       end
    end

Таким образом, вы можете отправить в этот параметр действия redirect_to, который может быть любым путем.

fl00r
источник
Привет, для решения B wrap как модельного метода, как завершить, если модели нет вообще? Мы работаем над поисковой системой и хотели бы вызывать просмотры результатов поиска в поисковой системе из других систем. Модель данных для поискового действия отсутствует вообще.
user938363 06
@ user938363 - Возможно, оба действия отображают одно и то же представление (даже если эти действия находятся в разных контроллерах). Сам вызов "render" будет дублирован, но это всего лишь одна строка дублирования - не так уж и плохо. Если у вас много логики, которая подготавливает хэш параметров для передачи в вызов рендеринга, вы можете избежать дублирования этого, переместив его в отдельный файл (возможно, модель в /modelsили обычный класс или модуль в /lib). Единственная проблема заключается в том, что ваш контроллер взаимодействует с представлением через переменные экземпляра - вам придется исправить это дублирование другим способом.
антином
Что, если у вас есть UserController, который создает нового пользователя (регистрацию), и в случае успеха вы хотите вызвать SessionsController и аутентифицировать пользователя? В этом случае вы так или иначе вызываете SessionsController. Есть мысли по этому поводу?
dipole_moment
Почему это плохая практика? в чем проблема с ответом Сэмми Ламби?
vasilakisfil 03
7

Может быть, логику можно было бы извлечь в помощника? помощники доступны всем классам и не передают управление. Вы можете проверить в нем, возможно, имя контроллера, чтобы узнать, как он был вызван.

Майкл Даррант
источник
6

Композиция на помощь!

Учитывая причину, вместо того, чтобы вызывать действия между контроллерами, следует разрабатывать контроллеры для разделения общих и настраиваемых частей кода. Это поможет избежать как дублирования кода, так и нарушения паттерна MVC.

Хотя это можно сделать разными способами, использование проблем ( композиции ) является хорошей практикой.

# controllers/a_controller.rb
class AController < ApplicationController
  include Createable

  private def redirect_url
    'one/url'
  end
end

# controllers/b_controller.rb
class BController < ApplicationController
  include Createable

  private def redirect_url
    'another/url'
  end
end

# controllers/concerns/createable.rb
module Createable
  def create
    do_usefull_things
    redirect_to redirect_url
  end
end

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

Олег Афанасьев
источник
2

Вы можете вызвать другое действие внутри действия следующим образом:

redirect_to действие: 'action_name'

class MyController < ApplicationController
  def action1
   redirect_to action: 'action2'
  end

  def action2
  end
end
CodecPM
источник
-6

Отделите эти функции от контроллеров и поместите их в файл модели. Затем включите файл модели в свой контроллер.

Michale.Gaocl
источник
Плохое предложение. Запутать обязанности, указать на MVC. Проблемы с сессиями / вызовами
файлов