【问题标题】:What is the best design practise to create tables from same controller using same actions使用相同操作从相同控制器创建表的最佳设计实践是什么
【发布时间】:2016-11-29 02:56:32
【问题描述】:

我有一个涉及家谱的项目。首先是问你(当前用户)的问题,然后是关于你父亲的相同问题,然后是关于你母亲的完全相同的问题,一直持续到双方的祖父母。

不幸的是,我尝试了 3 种不同的方法和实现,每次都纠正过去的错误,因为我对 Rails 还很陌生。

我想从这个社区获得关于我在遇到设计问题时应该遵循的最佳设计方法的建议/指导。

所以我认为最好的方法是在我需要将这些信息放在一个单一的家谱中时提出不同的表格以便以后受益。因此,一张表用于当前用户,一张用于父亲、母亲等,每个表都有一个模型,但使用单个控制器,因为我有完全相同的 html.erb 表单,并且每次都会更改标题以适应兄弟问题。

我已成功创建流程和操作,例如新建、创建、显示等,但我的问题如下:

我的问题是连续的,最后显示的是家谱。因此,当用户单击“继续”时,数据将保存在我的数据库的等效表中,并继续流向下一个表。

我被困了 8 个多小时,关于如何使 create 方法从父亲变为母亲,并拥有此代码:

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to :controller => 'users', :action => 'new_father'
    else
      render 'new'
    end
  end

  def user_params
    params.require(:user).permit(:name, :surname, :dob, :location)
  end

其中 users 是 Users_Controller 的名称,'new_father' 是同一控制器 (new_father.html.erb) 中的视图。存在其他视图,例如 current_user、new_mother 等。

因此,第一次重定向成功实现,因为数据存储在数据库中(第一次重定向 = 从当前用户转到他的父亲),但是我无法在同一个控制器中从父亲转到母亲,并且表单保持堆叠在 new_father.html.erb 视图中,代码如下:

def create
        @user = User.new(user_params)
        if @user.save
          redirect_to :controller => 'users', :action => 'new_mother'
        else
          render 'new'
        end
      end

  def user_params
    params.require(:user).permit(:name, :surname, :dob, :location)
  end

但是我需要更多的控制器来执行此操作或在名为 create_mother 的同一控制器中执行不同的操作。但是我尝试了一切都没有成功。

有人可以帮助我(首先)关于redirect_to方法的最佳实践,特别是如果我需要更多具有相同方法的控制器或具有不同方法的相同控制器,或者相同的控制器具有相同的功能(我试过这个,我得到了“在单个函数中进行更多重定向或渲染”的错误),其次,对于这种特殊情况,我需要完全相同的字段和操作,但在不同的表中每次都有不同的重定向,最好的设计是什么。

我的路线是:

Rails.application.routes.draw do

  # The first page providing information about the current user (novice genealogist).
  get 'users/new'

  # The rest of the pages asking information to form the tree.
  get 'fathers/new_father'
  get 'mothers/new_mother'

  # TODO
  # get 'users/new_grandfather_m'
  # get 'users/new_grandmother_m'

  # The input windows that the user lands on need to create a new record in the database.
  get  '/signup',              to: 'users#new'
  get  '/new_father',          to: 'fathers#new_father'
  get  '/new_mother',          to: 'mothers#new_mother'

  # TODO
  # get  '/new_grandfather_m',   to: 'users#new'
  # get  '/new_grandfather_m',   to: 'users#new'

  # This page will serve as the tree showing information from the above input.
  get  '/tree'  ,              to: 'users#show'

  # Used to update the database by creating records with the above info.
  post '/signup',              to: 'users#create'
  post '/new_father',          to: 'fathers#create_father'
  post '/new_mother',          to: 'mothers#create_mother'

  # TODO
  # post '/new_grandfather_m',   to: 'users#create'
  # post '/new_grandmother_m',   to: 'users#create'

  # The database of our system.
  resources :users

  # The homepage.
  root 'users#new'

end

其他操作是:(基本上我已经为每个关系创建了新的控制器)

class FatherController < ApplicationController

  # Creates a new instance of the user object (not a record).

  def new_father
    @father = User.new
  end


  # Creates a new record in the database by filling the fields of the new object instance
  # with data.

  def create_father
    @father = User.new(father_params)
    if @father.save
      #redirect_to @user
      redirect_to :controller => 'mothers', :action => 'new_mother'
    else
      render 'new_father'
    end
  end


  # A private method to pass the parameters in the new object instance.

  private

  def father_params
    params.require(:user).permit(:name, :surname, :dob, :location)
  end


  # Extracts information from the database.

  def show_father
    @father = User.find(params[:id])
  end

end
  • 注意:

我不必建立关系等,重要的是想出一个非常简单的树来显示用户的数据并学习rails,因此关系是一对一、一对多、多对多不重要。

提前谢谢你。

【问题讨论】:

  • 请将您的new_mothernew_father 操作代码添加到您的问题中。
  • 请同时添加您的路线。
  • 感谢您的回复。我已经用您提出的代码更新了问题,但包含关系并不重要,因为这是一个简单的应用程序,主要目标是存在简单树的最终视图。

标签: ruby-on-rails ruby-on-rails-4 model-view-controller controllers


【解决方案1】:

您有两个create 操作,根据您的描述,它们驻留在不同的控制器中。两者都尝试重定向到UserController,但这与之前的不一致。您应该将控制器和路由代码添加到您的问题中以消除歧义。

new_fathernew_mother 不是控制器的视图,而是操作。当你在动作结束时做这样的事情时:

redirect_to :controller => 'users', :action => 'new_mother'

浏览器通过它接下来应该访问的 url 获得重定向状态,如下所示:

Location: http://localhost:3000/users/new_mother

然后浏览器在该 url 发出一个新请求,这意味着如果你有你的路由,你的UserControllernew_mother 动作将被执行。你的 new_mothernew_father 应该是你的 UserController 中的操作,除非你做了一些路由黑客。重申一下,以防万一,这是一个全新的请求,与返回重定向状态的第一个请求无关。

当您调用redirect_torender 时,Rails 尚未将响应发送到浏览器,而是用一些数据标记其内部响应结构并继续执行您操作的其余代码。只有当完整的动作代码执行完毕后,才会发回响应。根据其设计,如果您在操作中多次调用这些方法,Rails 会报错,因为它会将这视为操作代码中的潜在错误。


关于设计:

您的域中有人员和关系。人具有相同的属性,无论是用户、父亲或母亲或任何其他亲属。您的域并未规定人员需要分布在不同的表中。其实你主要关注的是家庭成员之间的关系类型。母亲、父亲、姐妹、侄子等是一对人之间的关系。然后将这些关系呈现为模型是有意义的:

# simplistic representation
create_table "relations" do |t|
  t.integer  "to_person_id"
  t.integer  "is_person_id"
  t.string  "relation_type"
end

relation_type 字段会将亲属关系类型作为字符串携带,例如“父亲”。

虽然这是构建完整的家谱树应用程序的一种正确方法,但它也更复杂。它将涉及单表多关联和递归 SQL 查询,这两个都是相当高级的主题。

现有的 gems 处理这种模式。其中一些是ancestryacts_as_treeacts-as-dag

【讨论】:

  • 我更喜欢一个方便而清晰的解决方案,用尽可能少的魔法来理解这个案例的 MVC,甚至每个人都有一张桌子(我不会再去找祖父母了)并告诉我它是否最好为每个表和不同的控制器使用不同的模型,并使用以下方法在创建函数中进行重定向:redirect_to :controller => 'mothers', :action => 'new_mother'
  • 我还用路由和用户控制器中的代码更新了问题
  • 为了简单起见和学习目的,您的方法是完全有效的。 fathersmothers 有不同的模型和控制器很好,特别是因为你不打算超越祖父母。在这方面,您的控制器和路由看起来不错。
  • 感谢您对 Nic 的反馈,希望有一天能达到您的水平。
【解决方案2】:

对相同的数据使用不同的模型/表并不是最好的方法。我认为您应该只需要 2 张桌子来处理所有这些(当然,一张桌子供 questions 使用)。 一张表应该是users,所有用户都将存储在其中。每个用户都会引用它的 motherfather 它可以通过自加入/继承来实现。 然后,您需要在此表中为answers 提供一个表,所有用户的所有答案都将被存储。所有答案,无论是mother,还是father,还是grand parents。 您只需要一个额外的列来区分答案的类型,无论是母亲/他自己/父亲还是祖父母。

每个answer 都将属于给出该答案的user,并将属于question

所以基本上这里是这个 schmea 的原始实现。

class User < ActiveRecord::Base
has_many :answers
belongs_to :mother , class_name: 'User' , foreign_key: 'mother_id' #This is telling the user belongs to a mother and it's stored in same table. And mother reference will be stored in mother_id column. So user table should have a column mother_id

belongs_to: father , class_name: 'User', foreign_key: 'father_id' #This is telling the user belongs to a father and it's stored in same table. And father reference will be stored in father_id column. So user table should have a column father_id

has_many :children ,->(user){ where("users.father_id ? or users.mother_id = ?",user.id)} #Every user can have many children. Whether it's a mother or a father 

def grand_father_from_father_side
    father && father.father 
end

def grand_father_from_mother_side
    mother && mother.father
end

#Similary you can get grammy like mother.mother
end


class Question < ActiceRecord::Base
has_many :answers
end

class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user

#There will be a question_for column in answer table which will tell you this answer was posted for which relation i-e Mother , Father or User Himself

end

现在在用户控制器中它非常简单。您只需要一个视图,无论是针对用户、他的父母还是他的祖父,每种类型的答案都会调用该视图。只需在表格中放置一个隐藏字段,它会告诉您答案的类型,并将其存储在答案表中。

因此,如果用户回答她的母亲,则其值为“母亲”,依此类推。

然后你可以用'过滤所有用户的答案

user.answers.where(answer_type: 'Mother')这将返回他为他母亲回答的用户的所有答案。

【讨论】:

  • 感谢您的回复。不需要包含关系,我是 Rails 的新手,这是我的第一个后端框架,我有这个项目,目的是展示具有这些关系的树。我更喜欢一个方便而清晰的解决方案,尽可能少的魔法来理解这个案例的 MVC,甚至每个人都有一张桌子(我不会再去找祖父母了),并告诉我有一个不同的模型是否好每个表和一个不同的控制器,并使用以下方法在创建函数中进行重定向:redirect_to :controller => 'mothers', :action => 'new_mother'
  • 我还用路由和用户控制器中的代码更新了问题
  • 正如我之前提到的,做你想要完成的事情并不是最佳实践。但是当你学习 Rails 时,拥有多个模型和控制器并使用它们是可以的。完成此任务后,您自己就会知道为什么这样做不是一个好主意
  • 感谢您对 Qaisar 的反馈,希望有一天能达到您的水平。
猜你喜欢
  • 2018-03-20
  • 2013-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-11
  • 1970-01-01
  • 2015-03-26
  • 1970-01-01
相关资源
最近更新 更多