My goal is to create a web app that show elections results from my country.
The data is the results for every candidates in every city for every election.
An election has many candidates and many cities.
A candidate has many elections and many cities.
A city has many elections and many candidates.
For the 2nd round of the last presidential election:
City
inscrits
votants
exprime
candidate1
score C1
candidate2
score C2
Dijon
129000
100000
80000
Macron
50000
Le Pen
30000
Lyon
1000000
900000
750000
Macron
450000
Le Pen
300000
How can I join those 3 tables together?
Is it possible to create a join table between the three, like this?
create_table "results", force: :cascade do |t|
t.integer "election_id", null: false
t.integer "candidate_id", null: false
t.integer "city_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["city_id"], name: "index_results_on_city_id"
t.index ["candidate_id"], name: "index_results_on_candidate_id"
t.index ["election_id"], name: "index_results_on_election_id"
end
But in this case, where can I add the city infos for election? (Column 2, 3, 4 of my data example, i.e: in this city, for this election XXX people voted, XXX didn't vote.)
I came with this database schema:
my database schema
This will not work because I will not be able to access the result of a candidate in a specific city for a specific election. It looks like there is no connection between cities and candidates.
To actually tie these models together and record the data required you need a series of tables that record the election results at each level your interested in:
# rails g model national_result candidate:belongs_to election:belongs_to votes:integer percentage:decimal
class NationalResult < ApplicationRecord
belongs_to :candidate
belongs_to :election
delegate :name, to: :candidate,
prefix: true
end
# rails g model city_result candidate:belongs_to election:belongs_to votes:integer percentage:decimal
class CityResult < ApplicationRecord
belongs_to :city
belongs_to :candidate
belongs_to :election
delegate :name, to: :candidate,
prefix: true
end
Instead of having C1 and C2 columns you should use one row per candidate instead to record their result. That will let you use the same table layout even if there are more then two candidates (like in a primary) and avoids the problem of figuring out which column a candidate is in. Use foreign keys and record the primary key instead of filling your table with duplicates of the names of the candidates which can easily become denormalized.
While you might naively think "But I don't need NationalResult, I can just sum up all the LocalResult's!" - that process would actually expose any problems in your data set and very likely be quite expensive. Get the data from a repubable source instead.
You can then create the has_many assocations on the other side:
class Canditate < ApplicationRecord
has_many :local_results
has_many :national_results
end
class Election < ApplicationRecord
has_many :local_results
has_many :national_results
end
class City < ApplicationRecord
has_many :local_results
end
Keeping track of the number of eligable voters per election/city will most likely require another table.
Related
I have following models in my rails app:
class Student < ApplicationRecord
has_many :tickets, dependent: :destroy
has_and_belongs_to_many :articles, dependent: :destroy
class Article < ApplicationRecord
has_and_belongs_to_many :students, dependent: :destroy
class Ticket < ApplicationRecord
belongs_to :student, touch: true
I need to extract all Students who has less than articles and I need to extract all Students who's last ticket title is 'Something'.
Everything I tried so far takes a lot of time. I tried mapping and looping through all Students. But I guess what I need is a joined request. I am looking for the most efficient way to do it, as database I am working with is quite large.
go with #MCI's answer for your first question. But a filter/select/find_all or whatever (although I havn't heared about filter method in ruby) through students record takes n queries where n is the number of student records (called an n+1 query).
studs = Student.find_by_sql(%{select tmp.id from (
select student_id as id from tickets where name='Something' order by tickets.created_at desc
) tmp group by tmp.id})
You asked
"I need to extract all Students who has less than articles". I'll presume you meant "I need to extract all Students who have less than X articles". In that case, you want group and having https://guides.rubyonrails.org/active_record_querying.html#group.
For example, Article.group(:student_id).having('count(articles.id) > X').pluck(:student_id).
To address your second question, you can use eager loading https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations to speed up your code.
result = students.filter do |student|
students.tickets.last.name == 'Something'
end
Here association is HABTM so below query should work
x = 10
Student.joins(:articles).group("articles_students.student_id").having("count(articles.id) < ?",x)
I have by the moment 4 tables on my web-market.
Table User: Username + password + email.
Table Serie: Serie name + price + description
Table Season: Season name + price + description + fk(Serie) //Each season belongs to a serie
Table Chapter: Chapter name + price + description + fk(Season) //Each chapter belongs to a season
The user would be able to buy either series seasons and chapters. The main idea is the following:
Table invoice: fk(user) //User have multiple invoices
Table invoice_line: fk(invoice), fk(serie|season|chapter) //Each invoice contains multiple products
How can I represent that serie|season|chapter relation easily?
You could use polymorphic association from Active Record.
It can be represented like this:
class InvoiceLine < ActiveRecord::Base
belongs_to :invoice
belongs_to :containable, polymorphic: true
end
With this definition you should have table like this:
create_table :invoice_lines do |t|
t.references :invoice,
t.references :containable, polymorphic: true
end
t.references :containable, polymorphic: true will create both integer containable_id and string containable_type columns.
I have two models, Apartments and Amenities, which are associated through ApartmentAmenities. I am trying to implement a filter where I only show apartments that have all of the amenities specified.
class Amenity < ActiveRecord::Base
has_many :apartment_amenities
has_many :apartments, through: :apartment_amenities
end
class ApartmentAmenity < ActiveRecord::Base
belongs_to :apartment
belongs_to :amenity
end
class Apartment < ActiveRecord::Base
has_many :apartment_amenities
has_many :amenities, through: :apartment_amenities
end
I've got a query working that will return all apartments that match at least one of the amenities of given set like so:
Apartment.joins(:apartment_amenities).where('apartment_amenities.amenity_id IN (?)', [1,2,3])
but this isn't quite what I'm going for.
Alright, after giving up for a few days then getting back to it, I finally found this question: How to find records, whose has_many through objects include all objects of some list?
Which led me to the answer that works properly:
def self.with_amenities(amenity_ids)
where("NOT EXISTS (SELECT * FROM amenities
WHERE NOT EXISTS (SELECT * FROM apartment_amenities
WHERE apartment_amenities.amenity_id = amenities.id
AND apartment_amenities.apartment_id = apartments.id)
AND amenities.id IN (?))", amenity_ids)
end
I have a database with 6500 players and each player has an average of 15 game results.
Use case
I want to generate a list of players, ordered by the sum of their prize money (a field in the results table).
I prefer this to be in some sort of scope, so I can also filter the list on the player's country, etc.
Performance
I have seen posts that mention a cache_counter field for performance. In my case I have thousands of result records (75.000+) so I don't want the calculations being done every time someone visits the generated listings.
Question
What is the best pattern to solve this? And how do I implement it?
Models
class Player < ActiveRecord::Base
has_many :results
end
class Result < ActiveRecord::Base
belongs_to :player
end
Schemas
create_table "players", :force => true do |t|
t.string "name"
t.string "nationality"
end
create_table "results", :force => true do |t|
t.integer "player_id"
t.date "event_date"
t.integer "place"
t.integer "prize"
end
Update
What I am trying to accomplish is getting to a point where I can use:
#players = Player.order_by_prize
and
#players = Player.filter_by_country('USA').order_by_prize('desc')
You should be able to use something like this:
class Player
scope :order_by_prize, joins(:results).select('name, sum(results.prize) as total_prize').order('total_prize desc')
Refer rails api - active record querying for details.
I have a one-tomany relationship with 2 tables as follows:
Models:
class MediaType < ActiveRecord::Base
belongs_to :media
end
class Media < ActiveRecord::Base
has_many :media_types
end
SQL for simplicity sake are:
create_table :media do |t|
t.string "name", :limit => 255
t.integer "media_type_id"
end
create_table :media_types do |t|
t.string "name", :limit => 255
end
Once I insert a Media record relating to a media_type_id, how do I pull back the media_type.name value related to the media record?
I blindly tried:
media = Media.find(1)
media.media_type_id.name
But that didn't work of course. Is my SQL not Rails standards possibly?
Appreciate any help.
If you idea: media_type has many medias, but every media has only one media_type
You need another models:
class MediaType < ActiveRecord::Base
has_many :medias
end
class Media < ActiveRecord::Base
belongs_to :media_type
end
And
media = Media.find(1)
media.media_type.name
give you name
It seems that media has_many media_types.
In that case you would create media_id column in media_types table, but you did it other way around.
You will then approach each relation by
types = Media.media_types
to get the media_types that the Media has, and
media = MediaType.media
to get the media that mediatype belongs to.