I am new to coding (never taken a CS class and have a very very minimalist understanding of the MVC architecture) and working on a test project to learn it :-) I want to setup some tables tables:
Table 1:
Table_1 ID | Name | Attribute_1 | Attribute_2 | Attribute_3 | etc
1 | Blah | attribute 1 | attribute 2 | attribute 3 |
Table 2:
Table_2 ID | Skill |
1 | nun_chucks |
2 | bow |
3 | arrow |
Table 3:
Table_3 ID | Primary_1 ID | Primary_2 ID |
1 | 1 | 1 |
2 | 1 | 2 |
3 | 1 | 3 |
And then be able to visualize the data so it reads like:
Name | nunchucks, bow, arrow | attribute 1 | attribute 2 | attribute 3 |
I have used Scaffold to create the MVC for Table 1 & 2 and have the #page for each one respectively. I am not sure how to combine them to create table 3 and then make the data show as I am asking.
Thanks!
This is a good reference for what you're doing: http://guides.rubyonrails.org/association_basics.html
INNER JOIN is what you want which is a database operation.
Take a look in your database servers manual and read up on it.
Essentially it takes two tables, joins them by a given column (for instance, Table_2.ID and Table_3.ID) and spits out a summary of both tables joined leaving out columns that don't match across the tables.
Given your example,
and the following contoller in Rails 3
class SomeController < ActionController
def show
#table_1 = Table1.find(params[:id],:include => :table2)
# Table1 and Table2 are your models
# where table2 would be your has_many_and_belongs_to_many assoc in Table1
end
end
you could write this up in show.html.erb like
<%= #table_1.name %>
<%= #table_1.table_2.map(&:skill) %>
<%= #table_1.attribute_1 %>
<%= #table_1.attribute_2 %>
<%= #table_1.attribute_3 %>
Related
I have 2 tables for example:
user_places
----------------
| id | place_id|
----------------
| 1 | 1 |
----------------
| 1 | 5 |
----------------
| 1 | 6 |
----------------
| 2 | 8 |
And a places table
--------------------------------------------
| id | title | description | image_url |
--------------------------------------------
| 1 | 1 | description1 | image1 |
--------------------------------------------
| 2 | 5 | description2 | image2 |
--------------------------------------------
| 3 | 6 | description3 | image3 |
--------------------------------------------
| ...| ... | description4 | image4 |
How to make the association in both models so I can get all places of user_places's id = 1 in rails console?
You'll want to look up foreign_keys, especially pertaining to Rails.
Rails is basically a way for you to interact with a relational database. As such, if you know how to correctly structure a relational DB, you'll be able to better understand Rails' ActiveRecord associations, and how they fit into applications.
--
How to make the association in both models so I can get all places of user_places's id = 1
You'd do this:
#app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :places
end
#app/models/places.rb
class Place < ActiveRecord::Base
has_and_belongs_to_many :users
end
This means you have to change your user_places table to the following:
#places_users
#user_id | place_id
This will allow you to call:
$ #user = User.find "1"
$ #user.places #-> 1,5,6
--
The other answer recommended a has_many :through relationship. Whilst this allows you to add other data into your join model, it means you have to include a user_places model for no real reason (at this stage).
I would recommend using has_and_belongs_to_many for the moment. This limits you to only having references in your join table, but makes the entire association much simpler:
The big caveat here is that you'll need to change your user_places table to have an alphabetical nameflow (places_users), and make sure you only have the two foreign_keys as columns: user_id | place_id
I'm trying to create an inbox for messaging between users.
Here are the following tables:
Messsages
Id | Message_from | message_to | message
1 | 2 | 1 | Hi
2 | 2 | 1 | How are you
3 | 1 | 3 | Hola
4 | 4 | 1 | Whats up
5 | 1 | 4 | Just Chilling
6 | 5 | 1 | Bonjour
Users
Id | Name
1 | Paul
2 | John
3 | Tim
4 | Rob
5 | Sarah
6 | Jeff
I'd like to display an inbox showing the list of users that the person has communicated and the last_message from either users
Paul's Inbox:
Name | user_id | last_message
Sarah| 5 | bonjour
Rob | 4 | Just Chilling
Tim | 3 | Hola
John | 2 | How are you
How do I do this with Active Records?
This should be rather efficient:
SELECT u.name, sub.*
FROM (
SELECT DISTINCT ON (1)
m.message_from AS user_id
, m.message AS last_message
FROM users u
JOIN messages m ON m.message_to = u.id
WHERE u.name = 'Paul' -- must be unique
ORDER BY 1, m.id DESC
) sub
JOIN users u ON sub.user_id = u.id;
Compute all users with the latest message in the subquery sub using DISTINCT ON. Then join to
table users a second time to resolve the name.
Details for DISTINCT ON:
Select first row in each GROUP BY group?
Aside: Using "id" and "name" as column names is not a very helpful naming convention.
How about this:
#received_messages = current_user.messages_to.order(created_at: :desc).uniq
If you want to include messages from the user as well, you might have to do a union query, or two queries, then merge and join them. I'm just guessing with some pseudocode, here, but this should set you on your way.
received_messages = current_user.messages_to
sent_messages = current_user.messages_from
(received_messages + sent_messages).sort_by { |message| message[:created_at] }.reverse
This type of logic is belongs to a model, not the controller, so perhaps you can add this to the message model.
scope :ids_of_latest_per_user, -> { pluck('MAX(id)').group(:user_id) }
scope :latest_per_user, -> { where(:id => Message.latest_by_user) }
Message.latest_per_user
First, I'd like to mention I'm COMPLETELY new to Ruby and Rails, I'm on my very first days of learning, so I apologize if I seem a bit unclear or too broad with my questions.
I'm trying to do something simple (I think?), which is to pivot a table.
I have a table that looks like this:
----------------------------------
| Name | Product ID | Amount |
|----------|----------------------
| Robert | P1 | 2 |
| Michael | P2 | 1 |
| Leonard | P2 | 1 |
| Robert | P2 | 4 |
| Robert | P3 | 2 |
| Michael | P3 | 1 |
----------------------------------
... and I'd like to to turn it into something like this:
---------------------------
| Name | P1 | P2 | P3 |
---------------------------
| Robert | 2 | 4 | 2 |
| Michael | - | 1 | 1 |
| Leonard | - | 1 | - |
---------------------------
I'm not too sure how to achieve that. I've looked around and haven't found anything specific to my question.
I found a gem called pivot_table, which can be found here: https://github.com/edjames/pivot_table but I have no clue how to exactly use it. It has a small guide in it, but I don't know where to place the code.
Any help is greatly appreciated.
Thank you.
Looking at your table and the results you're looking for, I would do it like this ( I assume it's an orders table ?)
result = []
Order.all.group_by(&:name).each do |name, orders|
record = {}
record["name"] = name
orders.each do |order|
record[order.product_id] = order.amount
end
result.append(record)
end
I hope this will give you a good starting point !
First install the gem
# In your Gemfile
gem 'pivot_table'
Then in your terminal, run
bundle install
Say the model represented by your first table is Sale.
sales = Sale.all
grid = PivotTable::Grid.new do |g|
g.source_data = sales
g.column_name = :product_id
g.row_name = :name
end
Then you can use the other methods listed in the docs. For example
g.column_headers # ['P1', 'P2', 'P3']
Note: this is just from reading the GitHub page you linked. I've never used the gem.
Edit:
You can put the code in a module:
# lib/PivotTable
module PivotTable
def grid(data, options = {})
grid = PivotTable::Grid.new do |g|
g.source_data = data
g.column_name = options[:column_name]
g.row_name = options[:row_name]
end
end
end
Then you'd call it from somewhere else with
include PivotTable
def my_method
sales = Sale.all
grid = grid(sales, { :row_name => :name, :column_name => :product_id })
# do stuff
end
This way you can reuse the grid-generating code and call it with arbitrary parameters.
This is a general solution, that I implemented in application_helper.rb
def pivot_table row_model, column_model, pivot_model, pivot_attribute
row_model.all.map do |r|
column_model.select(:id).sort.map do |c|
pivot_model.find_by(
row_model.to_s.downcase => r,
column_model.to_s.downcase => c
)&.public_send(pivot_attribute) || 0
end
end
end
The first three parameters are capital-letter ActiveRecord subclass names, and the pivot_attribute (which is an attribute of pivot_model) can be given as a symbol or a string. This assumes pivot_model is a many-to-many relation that references row_model and column_model as foreign keys. If there isn't a many-many model, I'm guessing some models could be repeated among the parameters, but I haven't kitchen tested it for that.
The return value is an array of arrays.
The project:
I'm creating a dynamic reporting system.
Metrics define the purpose of the reported data. For example:
"13 house fires."
The admin will define "house fires" as a metric through one form, the reporters will simply add the "13" through a different form.
However, there's another level: I want the reporting to be verbose across connected data points:
"13 house fires during January affecting 42 individuals (or 16 families) "
The verbage is stored in a table "metrics", the data is stored in a table "metrics_data"
Here's the metrics table from the sample above:
metric_id | parentID | childID | prefix | suffix | program_id
1 | 1 | 1 | | house fires | 1
2 | 1 | 2 | during | | 1
3 | 1 | 3 | affecting | individuals | 1
4 | 1 | 4 | (or | families | 1
The key for the sentence-based organization is the parentID - childID relationship.
Here's the metrics_data table:
metric_id | value | date
1 | 13 | 01/01/12
2 | nil | 01/01/12
3 | 42 | 01/01/12
4 | 16 | 01/01/12
The goal:
I want to organize the view (looping through the parentID) to show the metric verbosely:
Program #1
(parentID: 1): "# house fires during (date) affecting # individuals (or # families) "
(parentID: 2): "# shelters during (date) providing # overnight stays (for # individuals) "
Program #2
(parentID: 1): "# new volunteers recruited during (date) "
(parentID: 2): "# volunteers served # hours during (date) "
The code:
class Program < ActiveRecord::Base
has_many :programs_metrics
has_many :metrics, through: :programs_metrics
end
class Metric < ActiveRecord::Base
has_many :programs_metrics
has_many :metrics, through: :programs_metrics
end
Partial _program.html.erb (for programs/index.html.erb):
<% program.metrics.each do |metrics| %>
<div class="row offset1">
<% program.metrics.each do |pid| %> #how do I loop here based on parentID, sorted by childID?
<%= pid.prefix %>
#
<%= pid.suffix %>
<% end %>
</div>
<% end %>
I know I could split this out into a separate table and define the relationship between parentID and childID between two tables, but it seems overly complex to add another relationship layer.
You should probably treat the "parent" and "child" elements like attributes to Metrics, rather than trying to reuse the same model. An alternative would be to create another model called "Events" or whatever, and these Events are related to programs, and Metrics are related to Events. I would probably create a "Metric Type" as well instead of having a prefix or suffix associated directly with each record.
I think that trying to treat all the Metrics at the same level, and trying to loop through them with parentID and childID to create a sentence is infinitely more complicated than just adding another layer to the relationship.
Hopefully this is enough of a push...
I have a question about joins (and has_many belongs) ...
I have 3 tables in the database.
Advertisers
id
name
Categories
Linked with Advertisers (gem awesome_nested_set)
Comments
id
advertiser_id
comment
recommend
I can not see how many comments there are on the advertiser, and how many of these comments are recommended.
Advertisers Data
1 | abc
Comments Data
id | a_id | text | rec
---+------+---------+-----
1 | 1 | blabla | 1
2 | 1 | blablab | 1
3 | 1 | blablac | 1
4 | 1 | blablad | 0
In this case there are four comments and three of them are recommended.
In my view, I need to retrieve the following
Listing Category abapai
Advertiser name: abc
There are 3 comments recommended
a total of 4
Could someone help me?
In the comments model you can use scope method to filter for recommended comments. Like this:
scope :recommended_comments, where("recommended = '1'")
Then in the Advertiser view you could use it like this (I gussed some attribute names):
Listing Category <%= #advertiser.category.name %>
Advertiser name: <%= #advertiser.name %>
There are <%= #advertiser.comments.recommended_comments.count %> comments recommended
a total of <%= #advertiser.comments.count %>