質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.85%

  • Ruby

    6541questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • Ruby on Rails

    6373questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

[Rails5]1対1のモデルについて

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 101

marokatsu

score 10

現在、簡単なSNSアプリを作ろうと思っているのですが、モデルの取り扱いについてわからなかったため質問いたします。

ユーザー1人はUserモデルとMyintroモデルを持っております.

Userモデルはdevise認証のGemで作ったものです。
1人のユーザーは1つの自分の詳細モデルを持つ、というイメージで考えており、
Userモデル1つにつき、Myintroモデルを1つ持っているものになります。

ここで、ユーザーを一人作った後、自分の詳細モデルを作る際
Myintroコントローラーのnewアクションを行うのですが
その中で、Userが持っているMyintroが一つであればeditページへ飛ばす、としたいのです。

質問

ユーザーがMyintroを一つ以上持っているならという条件を
if current_user.Myintro.length >=1
というようなコードで書けたらいいのですが、何かいい方法はありませんでしょうか?

class MyintrosController < ApplicationController
  before_action :authenticate_user!
  def index

  end
  def show
  end
  def new
    # 現在のユーザーの紹介を作る
    @length = current_user.myintro.length
    # 現在のユーザーが詳細モデルをすでに持っていれば編集ページに遷移する
      redirect_to   edit_myintro_path(@myintro)
    end

    @myintro = current_user.build_myintro
  end

  def create
    @myintro = current_user.build_myintro(myintro_params)
    if @myintro.save
    redirect_to   edit_myintro_path(@myintro),notice:"プロフィール詳細を作成・保存しました"
    else
      redirect_to new_myintro_path,notice:"プロフィール詳細を作成できませんでした"
    end
  end
  def edit
  end

  private
  def myintro_params
    params.require(:myintro).permit(:sex, :love_target)
  end
end
class Myintro < ApplicationRecord
  belongs_to :user

  #必須項目
  validates :sex, presence: true

end
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  has_one :myintro,dependent: :destroy
end
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

ご質問拝見しました!
routes.rbのファイルも見せて頂けますでしょうか?

基本的には以下のようにnewアクションの前にbefore_actionで処理を行うのが良いのではないかと思います!

before_action :myintro_counter, only: [:new]

~~~
private
def myintro_counter
  if User.find(params[:id]).myintro.all.length == 1
    redirect_to honyarara #UserがMyintroをちょうど一つ持っている時に飛ばしたい場所を指定

  else
    #そうでない場合の処理
  end
end

ちなみに、ご質問に「UserがMyintroを一つ以上持っているならという条件を」と書かれていましたが、has_oneを使ってアソシエーションされているので、もしご質問に書かれているように1:nの対応関係にするならhas_manyにする必要があります!

私も初心者ですが以上ご回答です!

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/23 23:45

    pekapekatailさん
    返信ありがとうございます!
    コードも拝見しました。ひとつのユーザーに紐付いた自己紹介のモデルの数を取得する、ということですね。
    気づきませんでした!
    Userは自分のプロフィールであるMyintroを一つだけ持つため1対1のモデルです。
    ありがとうございました!

    routes.rb

    Rails.application.routes.draw do
    devise_for :users
    #get 'pages/index'
    root 'pages#index'
    resources :users, only: [:show]
    resources :myintros
    get 'myintros/:id/life' => 'myintros#life', as: 'manage_myintro_life'
    get 'myintros/:id/job' => 'myintros#job', as: 'manage_myintro_job'
    get 'myintros/:id/etc' => 'myintros#etc', as: 'manage_myintro_etc'
    # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
    end

    キャンセル

+1

解決おめでとうございます。表記を正しく直しました。こちらが勉強になりました(笑)。

こんにちは。詳しくはないのですが、似た問題を解決しました。書籍を参考にしました。
devise のテーブル(Userモデルに該当)
ユーザ情報入力用のテーブル(Myintroモデルに該当)----user_id 列で、user.id を参照する。

書籍によって、devise 側のテーブル、変数を user としている場合と、ユーザの情報入力用のテーブル、変数を user としている場合があり、ややこしいです。プログラムの読み違えが起きそうです。

問題は、二つ。(以下)
問題は、切り分けると、ストレスが減ります(笑)。

(1)--ユーザの情報入力用のテーブルに、まだ、レコードがない場合---を、どのように if 文に書くか?

(2)--devise 側のテーブルから id を取得して、ここで、ユーザ情報入力用のテーブルに保存しないと、参照できない。
@ユーザ情報入力用インスタンス.new
@ユーザ情報入力用インスタンス.save
どちらも、ここで行うことになる。

書かれている if current_user.Myintro.length >=1 と同じだと思いますが、私が動作確認できた方法は以下です。

# ユーザ情報入力のテーブル---deviseモデル名_id 列--に、
# 今、ログインしている人の devise の id があるか確認する。
# where を使うので、配列で受け取り。
# @はいれつ[0]---最初の要素

@はいれつ = ユーザ情報入力モデル.where('deviseモデル名_id == ?', current_deviseモデル名.id)

if @はいれつ[0] == nil then
  # まだ、ユーザ情報入力モデルのレコードがない。

  # ここで、新規入力を行う準備。new,create の url 使わない。
  @ひと = ユーザ情報入力.new 

  # 参照される、devise のテーブルの id を代入。
  @ひと.deviseモデル名_id = current_deviseモデル名.id

  # ここで、ユーザ情報入力モデルのテーブルに保存。
  @ひと.save

  # 今、保存したレコードを where の検索。
  @はいれつ = ユーザの情報入力モデル.where('deviseモデル名_id == ?', current_deviseモデル名.id)

else
  # すでにユーザの情報入力テーブルにレコードがある。
  # 過去にログインしたとき保存された。
end

# 配列の最初の1個を代入
@ユーザの情報入力用インスタンス = @はいれつ[0]

コントローラ、if 文の中で、ユーザ情報入力テーブルに保存。つまり、ユーザ情報入力テーブルへの入力は、edit, update で行う。new, create ではない。
ややこしいです。理解するのに、かなり時間がかかりました。
今、読み返すと、自分で、忘れている部分があります。(あー)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/23 23:59

    nekoyama7さん
    返信ありがとうございます!

    where文はまだ使ったことがなかったのですが、
    newアクションで
    Myintroモデルを参照し、nilであれば作成する、という流れでしょうか。。。
    MyintroコントローラーのnewアクションでdeviseのUserに紐付いたMyintroを作成し、
    updateアクションで作ったMyintroを更新していく、ということだと理解しました。
    ありがとうございました!

    キャンセル

  • 2018/04/25 20:27

    pekapekatailさんの書いていただいた、before_action を使う方法(まだ、じっくりコードを読んでませんが)で、new アクションを使うのが、流れが自然で良いと思います。参考にした本では、ユーザ情報入力のテーブル(Myintroモデル、テーブル)の1件のレコード作成を、index アクション(本来、複数のレコードを扱う)で行うという不自然なものでした。
    save で保存した瞬間から、新規レコードではなくなるので、edit, update を使うというのも不自然。(new, create ではなく。)

    キャンセル

  • 2018/04/26 00:40

    nekoyama7さん
    ありがとうございます。
    myintroのnewアクションでidを引数で受け取っているわけではなかったため

    if User.find(params[:id]).myintro.all.length == 1
    この:idでエラーが発生してしまい、色々試した結果下記の方法で何とか動きました。

    if current_user.myintro != nil

    1対1のモデルの扱いがまだはっきりとわかっていないため、また質問するかもしれません。
    お時間がありましたらまたよろしくお願い致します。

    キャンセル

  • 2018/04/26 10:38

    お疲れ様です。プログラムで、大きなトラブルを減らせるコツとして、私が心がけているのは----
    シンプルで、小さいサンプルを先に書く。---です。
    いったん、devise のモデル、テーブルからはなれて、より、シンプルな物を考えた方が、急がば回れ---で、早いかも。

    キャンセル

  • 2018/04/26 23:57

    ありがとうございます。
    自分が作っているシステムをより簡単に。。。
    わかりました。小さな機能から作って勉強します!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.85%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Ruby

    6541questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • Ruby on Rails

    6373questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。