I'm building an employments table which contains start_date and end_date
create_table :employments do |t|
t.integer :user_id
t.integer :company_id
t.string :title
t.string :department
t.string :summary
t.date :start_date
t.date :end_date
t.timestamps
end
It's good for creating past employments, but not for current employment since the user is still employed.
How to handle when user tries to indicate he/she is currently employed, so that the user doesn't have to select the :end_date. (I know LinkedIn offers a option that allows users to click on "I'm currently working here" to avoid choosing :end_date, but in database, how is this achieved?)
I use simple_form for :employments edit and new. The date fields are date selectors.
What about a validates_presence_of with an :unless conditional?
Here's an example:
Validate presence of field only if another field is blank - Rails
Off the top of my head, why not just set the end_date field to nil and treat that value as "currently employed" in your UI?
Related
According to MySQL reference manual there is a data type as "year". I wanted to use it with my rails app but when i run rails db:migrate , it fails and says :
NoMethodError: undefined method `year' for #<ActiveRecord::ConnectionAdapters::MySQL::TableDefinition:0x000056047e3c6120>
Create_book migration file:
class CreateBooks < ActiveRecord::Migration[5.2]
def change
create_table :books do |t|
t.string :name
t.year :release
t.references :genre, foreign_key: true
t.references :author, foreign_key: true
t.timestamps
end
end
end
If you are using DateTime then t.datetime will work for you.
If you are storing year only, it can be stored in integer, use t.integer which will simply store year. Maybe mysql have datatype year but here your ruby object for mysql connection adapter do not have.
I want to implement a character limit to a data value on my PostgreSQL database for my Ruby on Rails application. Currently my database table looks like this:
create_table "applications", force: :cascade do |t|
t.string "name"
t.string "gender"
t.date "date_of_birth"
t.string "gpa"
t.text "essay"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I want to change it so the "essay" only allows 10000 characters. From my limited understanding of PostgreSQL, text is unlimited by default whereas string is for 255 characters or less. I thought about implementing a Javascript conditional to not let the user hit the submission button in the client if the text is over 1,000 characters, but of course a tech savvy user could change that. What would be the most optimal way to do this?
Use rails model validations - validates_length_of.
On the rails end you can add this in application.rb file
validates_length_of :essay, :maximum => 1000
This will ensure that max size is not exceded. Also keep the javascript validation, since its helpful for all users except the malicious once.
Also I recommend you to change the table name from applications. You can get easily confused
Given a model with multiple has_many associations like this:
class Route < ActiveRecord::Base
has_many :flights
has_many :deals
has_many :ratings
...
end
Given a scenario where not all routes actually have all 3, is there an easy way to find the number of routes that do have all 3?
Based on other SO questions I have tried this:
scope :active, -> { joins(:ratings).joins(:deals).joins(:flights). Then I call Route.active.count in my console but the process just hangs. These tables are pretty large so I'm assuming it has something to do with it. Flights has 2,037,031 rows; Deals has 659,804 rows; and Ratings has 141,879.
Is there a faster/better way to get to the number I need (number of routes that have flights, deals, and ratings)?
EDIT
Here is the relevant schema info:
create_table "routes", force: true do |t|
t.integer "from_id"
t.integer "to_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "routes", ["from_id"], name: "index_routes_on_from_id", using: :btree
add_index "routes", ["to_id"], name: "index_routes_on_to_id", using: :btree
create_table "ratings", force: true do |t|
# various columns...
t.datetime "created_at"
t.datetime "updated_at"
t.integer "route_id"
end
add_index "ratings", ["route_id"], name: "index_ratings_on_route_id", using: :btree
create_table "flights", force: true do |t|
t.integer "airline_id"
t.integer "route_id"
# various columns...
end
add_index "flights", ["airline_id"], name: "index_flights_on_airline_id", using: :btree
add_index "flights", ["route_id"], name: "index_flights_on_route_id", using: :btree
create_table "deals", force: true do |t|
t.integer "route_id"
# various columns...
end
add_index "deals", ["route_id"], name: "index_deals_on_route_id", using: :btree
EDIT
I added boolean attributes to the Routes table of flightless and ratingless to help keep easier track after an import of whether the Route has any flights or ratings. After doing that, I've tried a few different queries and get different counts:
[7] pry(main)> Route.where(flightless: false, ratingless: false).includes(:deals).count
=> 19415
[8] pry(main)> Route.where(flightless: false, ratingless: false).joins(:deals).distinct.count
=> 10243
[9] pry(main)> Route.where(flightless: false, ratingless: false).joins(:deals).count
=> 378737
The query on line 8 produces the same result as Nic's pure SQL suggestion in the first answer below. I think I understand why the query on line 9 returns way more than could possible be correct (each route is duplicated for every resource it gets joined to) but I don't understand the difference between joins and includes well enough to tell why joins...distinct produces a different answer than includes here.
Using joins as well as includes will generate a cartesian product which will require some memory and depending on available resources can make the server unresponsive. Since you're after count and not the actual records, it should be enough to check for their existence, albeit probably at the expense of more calculations:
Route.where('EXISTS (SELECT 1 FROM ratings WHERE routes.id = ratings.route_id)
AND EXISTS (SELECT 1 FROM deals WHERE routes.id = deals.route_id)
AND EXISTS (SELECT 1 FROM flights WHERE routes.id = flights.route_id)').count
Although this mostly is raw SQL, it will return a proper ActiveRecord::Relation when count is skipped. This allows using it for on-demand loading of models like in find_in_batches.
This most probably can be rewritten using Arel to be more Rails way, but at this point I'm curious whether it actually gets you the proper result.
EDIT:
Or you could use
Route.joins(:ratings).joins(:deals).joins(:flights).distinct.count
It will return the correct count but will produce an intermediary cartesian product which it will then reduce with distinct. I would be curious to know what is the performance difference between these two approaches.
I'm building a Ruby on Rails application where I have a Locomotive and a Trip model.
Each locomotive can have many trips, and each trip belongs to one locomotive. The Trip model has a "begin" and "end" attribute, which are timestamps and can be null. Trip "end" is set once a locomotive on arrival of its destination; until then its' value is null.
Troubles start whenever I want to set the "end" attribute on the last trip for a particular locomotive ( id=45, for example). To this I must search within the set of all trips for a match on "locomotive_id=45" and search for those with a null "end" attribute. Only then I may be able to set the ending time.
In order to improve performance, I'm thinking of adding circular association with both models. Add a column to my Locomotive table named "last_trip_id", which can be null, that points to the Trip table and lets me know which row on that table is the last trip this locomotive has done.
I think this idea is awesome! :D However, I haven't found any documentation or tips regarding circular association on the ruby association guides or in it's API... So I don't know if this sort of implementation is encouraged on the RoR framework or not.
Would somebody give me some tips about this issue? Is it encouraged to add this circular association in order to have a better performance within this context?
Right now, my Locomotive and Trip models looks like this:
app/models/locomotive.rb
has_many :trips, dependent: :restrict_with_error
app/models/trip.rb
validates :locomotive_id, presence: true
belongs_to :locomotive
I think my new models should look like this:
app/models/locomotive.rb
has_many :trips, dependent: :restrict_with_error
belongs_to trip
app/models/trip.rb
validates :locomotive_id, presence: true
belongs_to :locomotive
has_one :locomotive
Which methods will add rails to my new models? Would I have some troubles when I write, for example:
> L=Locomotive.first
> L.trips.locomotives.trips.....
Best regards,
Karl
EDIT 1
Here you have my locomotives and trips structure:
db/schema.rb
...
create_table "locomotives", force: true do |t|
t.string "code"
t.datetime "created_at"
t.datetime "updated_at"
end
...
create_table "trips", force: true do |t|
t.integer "locomotive_id"
t.datetime "begin"
t.datetime "end"
t.datetime "created_at"
t.datetime "updated_at"
t.string "city_origin"
t.string "city_destination"
end
I would structure the schema like so: remember that with your schema you are trying to model real world concepts as clearly as possible, so always start with real world intuitive concepts.
Station #list of stations
Locomotive #list of trains
#these are the **scheduled** journeys
Route
start_station_id
end_station_id
departure_time
arrival_time
#these are the **actual** journeys
Journey
route_id
locomotive_id
actual_departure_time
actual_arrival_time
So, Stations, Locomotives and Routes all exist in advance, and then a Journey record is created for every actual real life journey by a train. The journey route_id and locomotive_id are filled when the journey is scheduled, and actual_departure_time and actual_arrival_time are filled in when the train actually leaves and arrives.
I think the names of the classes could be a bit better, and Journey could be decomposed further so that you have a table of start & end stations, and another with instances of these at different times, but you get the idea hopefully.
Your idea is to have an extra field from locomotive to the latest trip, so performance is better. If you set the right index on the trips table, maybe you don't need it.
Nevertheless, if you want to do it, here is how. Rails has the standard to set the name of the association to be identical to the classname. But this is no law. If your model get a little more complex, this is often no longer the right thing. So you can override it.
You call the association "latest trip" (or current_trip ?) and tell rails that it is actually a Trip-object.
locomotive.rb
belongs_to :latest_trip,
class_name: "Trip",
foreign_key: "latest_trip_id"
http://guides.rubyonrails.org/association_basics.html#belongs-to-association-reference
And you need to add a field "latest_trip_id" to the locomotives database table. You only need to be careful when you create and the objects, that all fields are correctly set.
You can have even several associations to Trip, like "funniest_trip", "accident_trips",....
You could introduce a Schedule model that would hold these trips. This would allow you to sort by date range for locomotives arriving/departing.
Just take care if your modeling trips that may run 'late' or 'off' schedule.
class CreateTrainManagementSystem < ActiveRecord::Migration
def change
create_table :locomotives do |t|
t.float :speed
end
create_table :schedules do |t|
t.integer :locomotive_id
t.integer :route_id
t.datetime :departure
t.datetime :arrival
end
create_table :routes do |t|
t.integer :origin
t.integer :destination
t.float :distance
end
create_table :trips do |t|
t.integer :schedule_id
t.datetime :departure
t.datetime :arrival
end
create_table :stations do |t|
t.string :title
t.string :address
t.string :city
t.string :state
t.integer :zip
t.decimal :coord_x
t.decimal :coord_y
end
end
end
To own this solution you can't just copy and paste it. This still needs models, validations and the like. It also solves more than just the performance issue. Think about calculating ETAs for delayed trains using speed, trip log, and route distances.
#Karl Hi, just saw in your comments that imply you're either a student or recent graduate. This is great news, you have a great resource here to ask questions. If you decide this is your craft and want to create solutions like mine, read Joe Celko's book Thinking In Sets.
I got the following models. The join table references the user_id and the course_id and the combination is set unique. However, after testing my page on the server, e.g. leaving and joining courses many times,also with another current_user my join table created records where the course_id was empty. This got me errors in fetching data afterwards. So now I tried adding :false => null. Could this help? thx for your time
def self.up
create_table :course_enrollments do |t|
t.references :user, :null => false
t.references :course, :null => false
t.timestamps
end
add_index :course_enrollments, [:user_id, :course_id], :unique => true
end
Adding :null => false will cause an exception when you try to save a CourseEnrollment with a null ID. That's fine if you write the controller action to handle the exception. You could (and should) add validates_presence_of :user_id, :course_id in the CourseEnrollment model, so that instances with null IDs will not be valid, and can be handled with the normal <model>.save method.
However the bigger question is why does your app save rows with null IDs in the first place? For example, you might be creating orphaned rows (by deleting the associated Course or User). Generally it's a good idea to add :dependent => :destroy on has_many associations to prevent this.