Rails 3.1 and Mysql Position in table - mysql

Using Rails 3.1 and mysql,I've the following issue: I've created the array #addresses:
#registry_all = Registry.all
#addresses = #registry_all.map{|reg| [ reg.id, reg.indirizzo_1, reg.indirizzo_2, reg.indirizzo_3, reg.indirizzo_4 ]}.sort
and this is model relative to Registry:
rails g model registry id:integer, created_at:datetime, updated_at:datetime, name:string, cod_fiscale:string :limit=>16, p_iva:string :limit=>11, indirizzo_1:text, indirizzo_2:text, indirizzo_3:text, indirizzo_4:text ,telefono1:string :limit=>25, telefono2:string :limit=>25
So array #addresses contains all registry's addresses and now I want to retrieve all addresses owned by a singular registry, making a finding by registry.id. I mean,I was thinking something similar to that:
#addresses.find[registry.id]
but off course doing so, (supposing that registry.id has equals to 30), I retrieve all addresses owned by 30-th element of array #addresses and not addresses owned by registry.id. This should works only if object for what I'm looking for, is 30-th element of array addresses, in other words, only if this object is 30-th inside Registry mysql table
How can I do this?

If I understood well you have all the 4 addresses [1] for a registry in a row of the registries DB table with the names indirizzo_1, indirizzo_2, indirizzo_3, indirizzo_4 and you want these addresses in an array. You could do this:
registry = Registry.find(the_registry_id)
return [registry.indirizzo_1, registry.indirizzo_2, registry.indirizzo_3, registry.indirizzo_4]
With find you get the Registry with that ID and then you use return the 4 attributes as an array.
But from a design point of view I would not add the 4 fields for the addresses but I would use a one-to-many relationship since a registry can have many addresses.
[1] "indirizzo" is "address" in italian

I created a migration somewhat like yours (minus the :limit)
class CreateRegistries < ActiveRecord::Migration
def change
create_table :registries do |t|
t.integer :id
t.datetime :created_at
t.datetime :updated_at
t.string :name
t.string :cod_fiscale
t.string :p_iva
t.text :indirizzo_1
t.text :indirizzo_2
t.text :indirizzo_3
t.text :indirizzo_4
t.string :telefono1
t.string :telefono2
t.timestamps
end
end
end
I assume you have already done:
$ bundle exec rake db:migrate
A simple way to think of this is to open a rails console:
rails c
I created 5 Registry records like this:
Registry.create(name:"name5", cod_fiscale:"cod_fiscale", p_iva:"p_iva", indirizzo_1:"txt", indirizzo_2:"txt", indirizzo_3:"txt", indirizzo_4:"txt" ,telefono1:"12345")
So now typing:
Registry.count
Gives me:
1.9.2-p180 :017 > Registry.count
(0.2ms) SELECT COUNT(*) FROM "registries"
=> 6
You can now use the .find method like this:
Registry.find(1)
where (1) is the id of one of the 6 records we have.
If using this in a Controller it will look like this:
#registry = Registry.find(params[:id])
Then you could grab the indirizzo by typing:
Registry.find(1).indirizzo_1
which gives:
1.9.2-p180 :036 > Registry.find(1).indirizzo_1
Registry Load (0.3ms) SELECT "registries".* FROM "registries" WHERE "registries"."id" = ? LIMIT 1 [["id", 1]]
=> "txt"
Let me know if you need more help and feel free to include more output.

Related

Store Join Model Data in Rails [duplicate]

This question already has answers here:
Rails HABTM setup, model object, & join_table insertion controller setup
(2 answers)
Closed 2 years ago.
I'm new to Ruby on Rails, and I'm developing a backend API.
Currently, I got 2 Active Record Models called Book and Genre.
Active Record Model
class Book < ActiveRecord::Base
has_and_belongs_to_many :genres
end
class Genre < ActiveRecord::Base
hast_and_belongs_to_many :books
end
DB Schema Model
create_table :books do |t|
t.string "title"
end
create_table :genres do |t|
t.string "genre"
end
create_join_table :books, :genres do |t|
t.index [:book_id, :genre_id]
t.index [:genre_id, :book_id]
end
REST POST Request
# POST /book
def create
book= Book.new(books_params)
if book.save
render json: {status: 'SUCCESS', message:'Book Saved', data: book},status: :ok
else
render json: {status: 'ERROR', message: 'Booknot saved', data: book.errors}, status: :unprocessable_entity
end
end
private
def books_params
params.require(:book).permit(:title)
end
QUESTION
I'd like to make an HTTP Post request to create a new book with it's genres. I've already tested the book insertion (without genres, just passing the book name), it works perfectly. However I'd also like to add some genre categories.
Both the has_many and has_and_belongs_to_many class methods create a set of _ids setters and getters:
book = Book.new
book.genre_ids = [1, 2, 3]
book.genre_ids == [1, 2, 3] # true
These setters will automatically create and delete rows in the join table. Since ActiveModel::AttributeAssignment maps the hash argument to .new, .update and .create to setters in the model all you need to do is whitelist the genre_ids parameter:
def books_params
params.require(:book).permit(:title, genre_ids: [])
end
Passing an empty array permits an array of permitted scalar values like for example numericals or strings.

Is there a way to preload an arbitrary number of parent associations in Rails?

TL;DR: I have a model that belongs_to :group, where group is another instance of the same model. That "parent" group can also have a parent, and so on up the chain. Is there a way to includes this structure as far up as it goes?
I have a Location model, which looks like this (abridged version):
create_table "locations", force: :cascade do |t|
t.string "name"
t.decimal "lat", precision: 20, scale: 15
t.decimal "long", precision: 20, scale: 15
t.bigint "group_id"
t.string "type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["group_id"], name: "index_locations_on_group_id"
end
class Location < ApplicationRecord
belongs_to :group, class_name: 'Location', required: false
has_many :locations, foreign_key: 'group_id', dependent: :destroy
end
In other words, it can optionally belong to a "parent" instance of itself, referenced as group.
That parent instance can also belong to a parent instance another level up, as can its parent, etc etc. Elephants, all the way down.
What I'd like to do is string the names of a Location and all its parent instances together, so I end up with something like "Top-level group > Mid-level group > Lowest group > Location". This is fine, and I've implemented that in the model already:
def parent_chain
Enumerator.new do |enum|
parent_group = group
while parent_group != nil
enum.yield parent_group
parent_group = parent_group.group
end
end
end
def name_chain
(parent_chain.map(&:name).reverse + [name]).join(" \u00BB ")
end
The only problem with this, however, is that it will query individually for each parent instance as it gets there (the N+1 problem). Once I'm including several Locations in a single page, this is a lot of queries slowing the load down. I'd like to preload (via includes) this structure as I would for a normal belongs_to association, but I don't know if there's a way to include an arbitrary number of parents like this.
Is there? How do I do it?
Using includes? No. Recursive preloading could be achieved this way though:
Solution #1: True recursion
class Location
belongs_to :group
# Impure method that preloads the :group association on an array of group instances.
def self.preload_group(records)
preloader = ActiveRecord::Associations::Preloader.new
preloader.preload(records, :group)
end
# Impure method that recursively preloads the :group association
# until there are no more parents.
# Will trigger an infinite loop if the hierarchy has cycles.
def self.deep_preload_group(records)
return if records.empty?
preload_group(records)
deep_preload_group(records.select(&:group).map(&:group))
end
end
locations = Location.all
Location.deep_preload_group(locations)
The number of queries will be the depth of the group hierarchy.
Solution #2: Accepting a hierarchy depth limit
class Location
# depth needs to be greather than 1
def self.deep_preload_group(records, depth=10)
to_preload = :group
(depth - 1).times { to_preload = {group: to_preload} }
preloader = ActiveRecord::Associations::Preloader.new
preloader.preload(records, to_preload)
end
end
The number of queries will be the minimum of depth and the actual depth of the hierarchy

Rails 4 .joins() issue -- not working

Model for Plugin:
class Plugin < ActiveRecord::Base
has_many :vulns
end
Model for Vuln:
class Vuln < ActiveRecord::Base
belongs_to :plugin
end
Table for Plugin:
create_table "plugins", force: true do |t|
t.string "name"
end
Table for Vuln:
create_table "vulns", force: true do |t|
t.string "title"
t.integer "plugin_id"
t.string "vulnerability_type"
t.string "fixed_in"
end
When I use rails console using rails c and enter Plugin.select("*").joins(:vulns) it only grabs data from the Plugin table
#<ActiveRecord::Relation [#<Plugin id: 1, name: "xxx">, #<Plugin id: 2, name: "xxx">
This is the query:
SELECT * FROM `plugins` INNER JOIN `vulns` ON `vulns`.`plugin_id` = `plugins`.`id`
However, when I execute this query in mysql, it shows all content from vulns and plugins like its supposed to. For every plugin there is atleast one or more vuln in the database.
So here's the question: How do I get it to fetch content from both tables (plugins and vulns) instead of just plugins?
The vulns values are there, it's just not shown because you are using Plugin model to select, i.e. Plugin.select("*").joins(:vulns).
If you did the following, you will get the value:
> plugin = Plugin.select("*").joins(:vulns)
> plugin.first.title
=> "mytitle"
Because you are querying through Plugin model, you'll see Plugin object.
Another way to test this would be by doing the following:
> plugin = Plugin.select([:name, :title]).joins(:vulns)
=> #<ActiveRecord::Relation [#<Plugin id: 1, name: "xxxx">]>
# It won't show you title even though it's there
> plugin.title
=> "mytitle"

How to save data directly from the migration file in ruby on rails

In my ROR project, I came across an issue. Previous developer of the project has save an array of data into a single column for a particular field. But now I have to get those data and save it to another separate table with the current table id. Following is my code for get the details from the db:
#ar = AResponse.select("id, selected_barriers");
#ar.each do |p|
p.selected_barriers.each do |barrier|
end
end
What I wanted is I have to save the p.id and barrier to a new table and I have to do it in a migration file. So when the migration runs, it will get all the existing details from AResponse and save it to the new one. Can I do it only with migration file? If so how can I do it?
This is my full code
I tried but its not working: Following is my code
class AnalysisBarriers < ActiveRecord::Migration
def self.up
end
def change
create_table :analysis_barriers do |t|
t.integer :analysis_response_id, :null => false
t.string :barrier
end
#analysis_response = AnalysisResponse.select("id, selected_barriers");
#analysis_response.each do |p|
p.selected_barriers.each do |barrier|
AnalysisBarriers.create(:analysis_response_id => p.id, :barrier => barrier)
end
end
end
def self.down
drop_table :analysis_barriers
end
end
Thanks
Yes you can do data changes after migrations. Whatever data changes you want to do, you can do in change function or up function. Take the following example
class Event < ActiveRecord::Migration
def change
create_table events do |t|
t.datetime :starts_at
t.datetime :ends_at
t.timestamps
end
Event.create(:starts_at => Time.now, :ends_at => Time.now+1)
end
end
Above statement will add data to your table just after its creation.

Comparing datetimes does not work

I'm creating a Rails application which uses MySQL. I have a table in my DB like this:
create_table "pastes", :force => true do |t|
t.string "title"
t.text "body"
t.string "syntax"
t.boolean "private"
t.datetime "expire"
t.string "password"
t.datetime "created_at"
t.datetime "updated_at"
end
I want to show only the non-expired pastes to people, so I do this:
#pastes = Paste.find(:all, :conditions => "expire < '#{Time.now.to_s(:db)}'")
However, even that returns ALL pastes. Not just those that are not expired yet. Can anyone help me? Thanks
Oh, changing < to > returns no pastes, not even the non-expired ones :(
I would create a named scope within your Paste model:
class Paste < ActiveRecord::Base
named_scope :expired, lambda {
{ :conditions => ["expire < ?", Time.zone.now] }
}
end
Rails 3 version:
class Paste < ActiveRecord::Base
scope :expired, lambda {
where("expire < ?", Time.zone.now)
}
end
Note that the lambda is required to delay the evaluation of Time.zone.now until when the (named) scope is actually invoked. Otherwise, the time that the class was evaluated would be used.
Now you can get all the expired pastes with a simple:
#pastes = Paste.expired
—Pretty clean, huh?
I solved it already. It had to do with timezones. This works:
#pastes = Paste.find(:all, :conditions => "expire > '#{Time.now.utc.to_s(:db)}'")
# ^^^ this does it