I have a project that involved genealogy. It starts by asking you questions you (current user), then the same questions about your father, then exactly the same questions about your mother and goes on until grandparents of both sides.
Unfortunately, I have tried 3 different approaches and implementations, each time correcting past mistakes as I am pretty new to rails.
I would like to get a recommendation/guidance from this community regarding the best design approach that I should follow having design problems.
So I think that the best way is to come up with different tables to benefit later when I need to put this info in a single family tree. So, one table for the current user, one for the father, mother, etc having a model for each table BUT using a single controller because I have exactly the same html.erb form and each time changes the headers to adapt the sibling question.
I had successfully created the flow and actions such as new, create, show etc BUT my problem is the following:
The flow of my questions is consecutive, and at the very end shows a family tree. Therefore when the user clicks continue, the data are saved in the equivalent table of my database and the flow continues for the next table.
I am stuck for more than 8 hours on how to make the create method change from father to mother having this code:
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
where users is the name of Users_Controller and 'new_father' is a view in the same controller (new_father.html.erb). Other views exist like current_user, new_mother etc.
So, the first redirection is achieved successfully as data are stored in the database (first redirection = going from current user to his father), but then I can't manage to go from father to mother in the same controller and the form stays stacked in the new_father.html.erb view having the following code:
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
But then I need more controllers to perform this or a different action in the same controller called create_mother. But I tried everything with not success.
Can someone please help me (firstly) on the redirect_to method best practise, particularly if I need more controllers with same methods or the same controller with different methods, or the same controller same functions (I tried this and I am getting the error of "more redirections or renderings in a single function") and secondly if what is the best design to follow for this particular situation where I need exactly the same fields and actions but in different tables having different redirections each time.
My routes are:
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
The other actions are: (basically I had made new controllers for each relation)
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
I don't have to make relations etc, the important thing is to come up with a really simple tree to show the data of the user and to learn rails therefore relations one to one, one to many, many to many are not important.
Thank you in advance.
You have two create
actions which, following your description, reside in different controllers. Both try to redirect to the UserController
though which is inconsistent with the previous. You should add your controllers and routes code to your question to remove ambiguity.
The new_father
and new_mother
are not the views of your controller but the actions. When you do something like this at the end of an action:
redirect_to :controller => 'users', :action => 'new_mother'
The browser gets a redirect status with the url it should visit next, something like this:
Location: http://localhost:3000/users/new_mother
the browser then makes a new request at that url, meaning that if you have your routes in place, your UserController
's new_mother
action will get executed. Your new_mother
and new_father
should be actions within your UserController
unless you do some routing hacks. To reiterate, just in case, this happens as a completely new request, unrelated to the first one that returned the redirection status.
When you call redirect_to
or render
, Rails does not yet send the response to the browser, instead it marks its internal response structure with some data and goes on with the rest of your action's code. It is only when the complete action code has been executed, the response is sent back. By its design Rails complains if you call those methods more than once within your action as it treats this as a potential mistake in the action code.
On the design:
There are persons and relations in your domain. Persons have the same attributes, be it a user, father or mother or any other relative. Your domain does not dictate that persons need to be distributed among different tables. In fact, what you are mainly after is the relation type between family members. Mother, father, sister, nephew, etc. are relations between a pair of people. It would then make sense to present these relations as a model:
# simplistic representation
create_table "relations" do |t|
t.integer "to_person_id"
t.integer "is_person_id"
t.string "relation_type"
end
relation_type
field would carry the kinship type as a string, e.g. 'father'.
Although this would be one proper way to build a full-scale genealogy tree application, it is also more complex. It would involve single-table has-many associations and recursive SQL queries, both of which are rather advanced topics.
There are existing gems, handling this kind of schema. Some of them are ancestry, acts_as_tree, acts-as-dag.