I am fairly new to ruby of rails, but I would like to build this table from my MySQL database, consisting of two tables, Parents and Children.
enter code here
Children (:id, :name, :parentId)
Parent (:id, :name, :childId)
Here is the HTML code:
<% #children.each do |child| %>
<tr>
<td><%= link_to child.name, child_path(child) %></td>
<td><%= child.ticker %></td>
<td><%= link_to child.parent, parent_path(parent) %></td>
</tr>
<% end %>
The table would be able to link to the show path of both tables, but only when child.parentId = parent.id. The table is on my table page with controller
class StaticPagesController < ApplicationController
def table
#children = Child.all.order('name ASC')
if params(:id) == child.parentId
#parent = Parent.find(params[id])
else
#parent = NULL
end
end
end
I know this is incorrect, but I am struggling to create this table. Any feedback or references are helpful!
Thank you in advance.
If you are using sqlite3, then the database for your application should already be under db/development.sqlite3.
I'm not 100% sure what you're trying to achieve here, but perhaps I can help anyway. If I get it wrong, please feel free to correct me in the comments.
First, your Parent table does not need the childId column that I can see. Rails will still be able to tell which parents have which children, and which children belong to which parents, without it. I'd suggest getting rid of it.
As for the Child table, consider replacing parentId with parent_id. Rails uses the latter by default, and it's generally easier to just go with the default unless you have a good reason.
With these considerations in mind, your migrations for these two models would probably look something like so:
db/migrations/[timestamp]_create_parents.rb
class CreateParents < ActiveRecord::Migration
def change
create_table :parents do |t|
# the id column is created automatically.
t.string :name
end
end
end
db/migrations/[timestamp]_create_children.rb
class CreateChildren < ActiveRecord::Migration
def change
create_table :children do |t|
# this creates a column `parent_id` with a foreign key for enforcing
# validity at the database level and an index for faster searching.
t.references :parent, foreign_key: true, index: true
t.string :name
end
end
end
You will want to associate these two models so that Rails can find the records that go together.
app/models/parent.rb
class Parent < ActiveRecord::Base
...
has_many :children
...
end
app/models/child.rb
class Child < ActiveRecord::Base
...
belongs_to :parent
...
end
Now, I'm not quite sure what you mean to do in your controller action. To start, you could just write:
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def table
#children = Child.all.order('name ASC')
end
end
Make sure you have a route for this controller action. Then your view (slightly corrected below) ought to work, except perhaps for this ticker attribute. Is that something you have defined in your database table?
app/views/static_pages/table.html.erb
<table>
<% #children.each do |child| %>
<tr>
<td><%= link_to child.name, child_path(child) %></td>
<td><%= child.ticker %></td> // as long as this attribute is defined, I believe it should work.
<td><%= link_to child.parent.name, parent_path(child.parent) %></td>
</tr>
<% end %>
</table>
Related
I have a Plan model that has_many Versions. I'm nesting Version inputs inside a Plan form, and even though the validations seem to be working, the form inputs don't display any errors.
I don't know if this is relevant, but because the nested inputs are scattered around the form, I open the nested input blocks with simple_fields_for :versions_attributes[0] instead of simple_fields_for :versions because I want to be specific that all the inputs around the form correspond to the same object. Otherwise the hash would be built with a different key for each different block (eg: versions_attributes: { 0: {amount: value}, 1: {another_field: another_value} } instead of versions_attributes: { 0: {amount: value, another_field: another_value}}.
plan.rb:
class Plan < ApplicationRecord
has_many :versions
accepts_nested_attributes_for :versions
validates_associated :versions
end
version.rb
class Version < ApplicationRecord
belongs_to :plan
validates :amount, presence: true
end
plans_controller.rb
class PlansController < ApplicationController
def new
#plan = current_account.plans.build
#version = #plan.versions.build
end
def create
#plan = current_account.plans.build(plan_params)
if #plan.save
redirect_to plan_path(#plan)
else
#plan = #base_plan.plans[0]
render :new
end
end
def plan_params
params[:plan][:versions_attributes]["0"]&.merge!(
account_id: current_account.id,
)
params.require(:plan).permit(
:name,
:short_description,
versions_attributes: %i[id account_id amount],
)
end
end
form.html.erb:
<%= simple_form_for [#plan] do |f| %>
<%= f.input :name %>
<%= f.input :short_description %>
<%= f.simple_fields_for "versions_attributes[0]" do |v| %>
<%= v.input :amount %>
<% end %>
<% end %>
Problem:
#version.errors contains a hash with the object's validation errors. However, the related inputs don't render validation errors nor are the form-group-invalid has-error CSS classes added (provided by the simple_form gem).
My first guess is that it's got something to do with the create action. My second guess is that it's got something to do with the way I'm opening the nested input's blocks (described above).
Either way, I'm confused because #version.errors contains the nested object's errors.
I want to make a table called subject_types and populate it with different subjects such as english, spanish, history. And assign the subjects table to each user. I want the user to be able to select multiple subjects with a checkbox and it will save those specific ones and keep checked. each user can save their own preferences/services. Im trying to figure out the best way to approach this.
here is how it would look
in a way the user is editing their settings. I want to be able to take this information and use it later to find results with only those subject types that they select. is there a tutorial or similar reference to this?
-Should I make the subject_types model and populate with these subjects or is there a better approach?
possible solution
could I have an array in a column in my user table? the array would be the ids of the subjects table. How can i do that??
Thank you
Updated:
<%= form_for(#user) do |f| %>
<%= f.collection_check_boxes(:writing_type_ids, WritingType.all, :id, :name) %>
<%= f.submit 'submit' %>
<% end %>
class WritingType < ActiveRecord::Base
has_many :user_writing_types
has_many :users, through: :user_writing_types
end
class UserWritingType < ApplicationRecord
belongs_to :user
belongs_to :writing_type
end
class User < ActiveRecord::Base
has_many :user_writing_types
has_many :writing_types, through: :user_writing_types
end
My migration for the join
create_table :user_writing_types do |t|
t.belongs_to :user, index: true
t.belongs_to :writing_type, index: true
t.boolean :active, default: false
t.timestamps
end
latest update
Im getting my last of errors! when i click submit on that page i get No route matches [PATCH] "/users/51".
I added this <%= f.collection_check_boxes(:writing_type_ids, WritingType.all, :id, :name) %> to my edit form in devise edit.html.erb. The writing_type names populate on the checkboxes, but nothing gets submitted to the database on the user_writing_type table.
Start by creating a many to many relation with a join model by using has_many :through associations:
class User < ApplicationRecord
has_many :user_subjects
has_many :subjects, through: :user_subjects
end
class Subject < ApplicationRecord
has_many :user_subjects
has_many :users, through: :user_subjects
end
# this is the join model
class UserSubject < ApplicationRecord
belongs_to :user
belongs_to :subject
end
You can then create a check box to add subjects to a user with collection_checkboxes:
<%= form_for(#user) do |f| %>
<%= f.collection_checkboxes(:subject_ids, Subject.all, :id, :name) %>
# ...
<% end %>
could I have an array in a column in my user table? the array would be
the ids of the subjects table. How can i do that??
You don't.
Even though Postgres for example allows you to create array type columns
it is not a good solution as thats not how associations work in ActiveRecord.
Its also a crappy relational database design as its messy to write queries with a join though an array column and it does not let you enforce referential integrity with foreign keys or have good indices for performance.
I'm used to MySQL but trying to use Ruby on Rails right now. In MySQL, I would have two tables, with one containing a reference to another ("posts" referring to "topic"). A MySQL query doing what I want would be similar to "SELECT * FROM Posts WHERE posts.topic="topic" ("topic" here is a variable).
However, trying to work with the Ruby model stuff has me confused. The variables being passed between the controller and view are null because they are empty tables.
In my controller:
def topic
#topic = Topic.where(params[:topic])
#posts = Post.where(topic: #topic.object_id)
end
I don't know how to select the posts which have the topic defined by the "topic" variable.
In the view:
<% #posts.each do |post| %>
<p><%= post.title %></p>
<% end %>
The migration files:
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.string :text
t.references :topic
t.timestamps
end
end
end
class CreateTopics < ActiveRecord::Migration
def change
create_table :topics do |t|
t.string :topic
t.timestamps
end
end
end
Given that Post and Topic are related, according to your migrations at least, in the models you should be stating"
class Topic
has_many :posts
and
class Post
belongs_to :topic
Given that you then have an instance of Topic, #topic, you can retrieve all the related records with:
#posts = #topic.posts
I think those methods you put in your controller are fine where they are, but keep in mind that the Rails way is "fat models, skinny controllers." If you put that logic in the model as a method, it's much easier to read in the controller. Also, you should look into scopes, as they'll help you with queries like this down the line too.
In any case, you should stick the following in your Topic model:
scope :by_name, ->(name) { where(topic: name) }
That's essentially the same as doing the following:
def self.by_name(name)
where(topic: name)
end
On your posts model, you'd be able to do the following:
scope :by_topic, ->(topic) { where(topic_id: topic) }
The other problem with what you've stuck in your controller is that when you use scopes, or a "where", it returns an array that contains all of the different records that match your query terms. So, when you call #topic = Topic.where(params[:topic]), you're getting back an array of objects. Therefore, when you do a #topic.id, you're trying to get back the id of an array instead of one object.
Based off of what I showed you before, it makes much more sense for you to do this:
def topic
#topic = Topic.by_name(params[:topic]).first #this returns the first record
#post = Post.by_topic(#topic.id)
end
That will return an array of posts that match the first topic name that you query for.
Alright, first a primer on how database design and how Rails (really, ActiveRecord) works. Basically, you should be connecting posts.topic_id = topic.id, not posts.topic = topic.topic.
Your migration is correct as is, create_table automatically includes an :id PRIMARY KEY column. That said you should know that these are all equivalent:
t.references :topic
t.belongs_to :topic
t.integer :topic_id
In your view, instead of embedding topic.topic and passing that to the controller when the form is submitted, embed topic.id (the documentation for the select helper has a good example of this) and in your controller:
#topic = Topic.find params[:id]
#posts = #topic.posts
i got 2 tables connected with each other.
device and push information are my models.
class Device < ActiveRecord::Base
has_one :pushinformation
end
class Pushinformation < ActiveRecord::Base
belongs_to :device
end
these are my 2 model files and their relationships.
and these are my migration files
class CreateDevices < ActiveRecord::Migration
def change
create_table :devices do |t|
#t.integer :id
t.string "token"
t.string "carrier"
t.string "segment"
#t.datetime :created_at
#t.datetime :updated_at
t.timestamps
end
end
end
class CreatePushinformations < ActiveRecord::Migration
def change
create_table :pushinformations do |t|
t.integer "device_id"
#t.string "token"
t.string "first_name"
t.string "last_name"
t.string "nickname"
t.timestamps
end
end
end
now the thing is , i was able to create a relationship successfully in rails console by saying
device.pushinformation=push
and they were associated.
How can i make this process automated, like when i add one device- it will have a push information table filled aswell,
i thought about having the same attribute and relating them might be the solution. In this case its token and its completely unique.
How can i create this relationships? I need to be able to know which device has what kind of first_name
i m a beginner in ruby and this is a newbie question sorry guys :)
I am not sure I understand completely what you ask but my guess is that you could use a callback on create
class Pushinformation < ActiveRecord::Base
belongs_to :device
after_create :create_push_notification
private
def create_push_notification
...
end
end
check the docs
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
xlembouras's answer is right (to a degree), but as you're new, let me explain it for you:
--
Associations
ActiveRecord associations are nothing magical, they're just a way to associate two "objects" using a relational database setup.
ActiveRecord is an ORM -- "object relationship mapper" -- which basically means it just provides a level of abstraction for your ActiveRecord objects to associate with each other
Herein lies the crux of the matter - you need to apprciate how and why your associations will work, and more importantly, how to populate them correctly:
--
Models
Firstly, you need to appreciate the object-orientated nature of Ruby (& by virtue of running on Ruby, Rails). This is where the Models of Rails play such a vital role -- they allow you to build & manage objects from your database
The ActiveRecord associations give you the ability to manage the associations those objects have - maning if you build one, you should be able to build the other:
#app/models/device.rb
Class Device < ActiveRecord::Base
has_one :push_information
before_create :build_push_information #-> creates associative object before creation
end
#app/models/push_information.rb
Class PushInformation < ActiveRecord::Base
belongs_to :device
end
You need to consider the importance of what I've written above.
What you need is to create a push_information object with the same foreign_key as the device object, which can be achieved by using the build method
This will essentially create a "blank" version of your associative object, saving it with the correct foreign key etc
--
Nesting
Further to this, you have to appreciate the idea of "nesting", especially the method accepts_nested_attributes_for
This allows you to create associative / dependent objects based on your "parent" object:
#app/models/device.rb
Class Device < ActiveRecord::Base
has_one :push_information
accepts_nested_attributes_for :push_information
end
#app/models/push_informtion.rb
Class PushInformation < ActiveRecord::Base
belongs_to :device
end
This gives you the ability to do the following:
#app/controllers/devices_controller.rb
Class DevicesController < ApplicationController
def new
#device = Device.new
#device.build_push_information
end
def create
#device = Device.new(device_params)
#device.save
end
private
def device_params
params.require(:device).permit(:your, :device, :params, push_information_attributes: [:push, :information, :attributes])
end
end
This gives you the ability to populate the devices#new form like so:
#app/views/devices/new.html.erb
<%= form_for #device do |f| %>
<%= f.text_field :your_device_attributes %>
<%= f.fields_for :push_information do |p| %>
<%= p.text_field :your_field %>
<% end %>
<%= f.submit %>
<% end %>
Add a create
method to your Devise class. Something like:
def self.create(token, carrier, segment)
device = Device.new(:token => token, :carrier => carrier, :segment => segment)
pi = Pushinformation.create(device.id,..) # other params
end
This helped for display on an individual 'items' show page
Accessing an attribute of a linked model in Rails
However I'm having trouble doing the same for an 'all items table'
...
<% #items.each do |item| %>
...
<td><%= item.room.name %></td>
...
Clearly where one room has many items.
only this works:
<td><%= item.room_id %></td>
I can't seem to use it there, gives:
undefined method `name' for nil:NilClass
Have a look at Rails' Delegate module:
class Item < ActiveRecord::Base
# ...
delegate :name, :to => :room, :allow_nil => true, :prefix => :room
end
This will add the instance method room_name to Item, which will fail more gracefully (returning nil if there is no room, instead of the NilClass error).