I am currently learning rails and in the process of building my first rails project. I created a :restaurant model (along with other models - bookings and user) with several attributes which include :city. Here's my schema:
create_table "restaurants", force: :cascade do |t|
t.string "name"
t.string "city"
t.string "website"
t.string "phone_number"
t.integer "ratings"
t.integer "capacity"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
In my root '/' page, I am displaying the unique city values as a list with links. I was hoping that the user can browse restaurants by clicking on the city that they are in or planning to visit (ideally with a link '/restaurants/#{city}' and through that end up in a page with the list of restaurants in that city.
I have been trying to figure out how to do this currently my relevant routes look like this:
resources :restaurants do
resources :bookings
end
I tried creating :city as a nested resource but this ended up with url '/restaurants/:restaurant_id/:city' which isn't what I'm trying to achieve.
But most importantly, I cannot work out how the 'city' that the user clicked in the root page can lead to the page with all the restaurants in that city.
Any advice would be most helpful.
Thank you.
Routes are extremely flexible and give you a lot of power.
First option: I'd suggest the more traditional Rails way: Separate out your cities into their own model and relate them to a restaurant.
Something like this:
class City < ApplicationRecord
has_many :restaurants, inverse_of: :city
...
end
class Restaurant < ApplicationRecord
belongs_to: city, inverse_of: :restaurants
...
end
Then, I'd move your database around a bit:
create_table :cities do |t|
t.string :name, null: false
t.timestamps
end
create_table :restaurants do |t|
t.string :name
t.references :city
t.string :website
t.string :phone_number
t.integer :rating
t.integer :capacity
end
This will get you on the right track for nested routing like:
/cities/:city_id/restaurants
Second option is to wander off the RESTful path and play with the flexibility of routes:
(I'm suggesting getting away from /restaurants/:city and just use /:city, but the idea is the same)
# routes.rb
# warning! Put this towards the very end of your file. Even then, any URL you try to hit that fits
# this pattern will get sent to this controller action. e.g. "yoursite.com/badgers"
# you'll need to explore handling RecordNotFound and redirecting someplace else
get '/:city', to: 'restaraunts#by_city', as: 'restaurants_by_city'
Now in your Restaurants controller:
class RestaurantsController < ApplicationController
...
def by_city
city = params[:city] # this will be whatever is in the url
#restaurants = Restaurant.where(city: city)
# you'll need some error handling:
redirect to root_path if #restaurants.empty?
...
end
end
Related
I am using a drop down menu to determine the status of a Show is/was: "Watched," "Watching," or "To-Watch" I am trying to display the status on the shows page and it will only populate the status_id. This feels pretty basic, but I've tried many iterations and even did a nested attribute in my controller. The only way I can populate it is Status.last.watched, etc. Can anyone point me in the right direction?
***schema.rb***
create_table "statuses", force: :cascade do |t|
t.string "watched"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "show_id"
t.index ["show_id"], name: "index_statuses_on_show_id"
create_table "shows", force: :cascade do |t|
t.string "show_title"
t.integer "user_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "note_id"
t.integer "status_id"
end
***params in shows_controller.rb***
def show_params
params.require(:show).permit(:show_title, :status_id, status_attributes: [:watched])
end
***index.html.erb***
<% #shows.each do |s| %>
<h4><li><%= link_to s.show_title, show_path(s.id) %></h4>
<p><%= s.status_id %>
<% end %>
Unfortunately you kind of failed at the database design stage. If you have shows and users and want to keep track of which users have watched what you want to setup a join table and put the status there.
class User < ApplicationRecord
has_many :user_shows
has_many :shows, through: :user_shows
end
# rails g model UserShow user:belongs_to show:belongs_to status:integer
# adding a unique compound index on user_id and show_id is a good idea
# if you are using PG you can use a native Enum column type instead of an integer
class UserShow < ApplicationRecord
enum status: { watching: 0, watched: 1, to_watch: 2 }
belongs_to :user
belongs_to :show
validates_uniqueness_of :show, scope: :user_id
end
class Show < ApplicationRecord
has_many :user_shows
has_many :shows, through: :user_shows
end
This creates a many to many association between Users and Shows. In your code a show can only be associated with a single status. That means you would have to duplicate everything on the shows table for every user.
I have no idea what you're trying to do with t.string "watched". A boolean would be slight improvement. But an ActiveRecord::Enum would let you keep track of the status without multiple boolean columns.
To get the status string to show in your view you want to change <%= s.status_id %> to <%= s.status.watched %>.
That being said, the way you have this setup, I would make another column in the shows table, call it status, and have it be of type string. Then you can set the status as one of three you listed. You could then get rid of the status_id column in shows and the whole statuses table.
So I am new to Rails and I have been trying to build a nested form. I have been having a lot of trouble and can't seem to get it to work.. I have watched multiple videos on youtube but I can't seem to find what I am doing different. For the purpose of me trying to build one, I have a Product which has many Buyers but a Buyer belongs to only one Product. (Assume you can only buy one Product...). When I submit my form I get an error which I can see in the server log: "Unpermitted parameter: buyer" I feel like I have tried everything.. I'd be so happy if someone could maybe tell me whats going on. Thanks so much
I have followed the Rails guide and added the following to my models:
class Product < ActiveRecord::Base
has_many :orders
has_many :buyers
accepts_nested_attributes_for :buyers
end
class Buyer < ActiveRecord::Base
belongs_to :product
end
Strong Params in the Product Controller:
def product_params
params.require(:product).permit(:name, :description, :image_url, :color, :adult, buyers_attributes: [:name, :age, :product_id])
end
And the Products controller:
def new
#product = Product.new
#product.buyers.build end
Then for the form:
Form
(Sorry, was having major issues inserting the code here)
Lastly this my schema for both tables:
create_table "buyers", force: :cascade do |t|
t.string "name"
t.integer "age"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "product_id" end
`
create_table "products", force: :cascade do |t|
t.string "name"
t.text "description"
t.string "image_url"
t.string "color"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "price"
t.binary "adult"
end
Your product acceptes nested attributes for buyers but you're only adding buyer (no plural) attributes to the form. You probably need to change the nested form to
<%= f.fields_for :buyers, [#product.buyers.build] do |x| %>
I need some help with polymorphic associations. Below is my structure:
class Category < ActiveRecord::Base
has_ancestry
has_many :categorisations
end
class Categorisation < ActiveRecord::Base
belongs_to :category
belongs_to :categorisable, polymorphic: true
end
class classifiedAd < ActiveRecord::Base
belongs_to :user
has_one :categorisation, as: :categorisable
end
And here is my schema.rb
create_table "classifiedads", force: true do |t|
t.string "title"
t.text "body"
t.decimal "price"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "classifiedads", ["user_id"], name: "index_classifiedads_on_user_id", using: :btree
create_table "categories", force: true do |t|
t.string "name"
t.string "ancestry"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "categories", ["ancestry"], name: "index_categories_on_ancestry", using: :btree
create_table "categorisations", force: true do |t|
t.integer "category_id"
t.integer "categorisable_id"
t.string "categorisable_type"
t.datetime "created_at"
t.datetime "updated_at"
end
It seems like the associations is correct as when I'm in the console I can do the appropriate commands and all seems to return the right results, for example: Category.first.categorisations or ClassifedAd.first.categorisation. But what I don't understand is saving the association from the Create and editing the record via the Update actions. I'm using simple_form to create my forms and when looking at the params I get the below:
{"title"=>"Tiger",
"body"=>"Huge Helicopter",
"price"=>"550.0",
"categorisation"=>"5"}
and this fails to update or even create as I get this error : Categorisation(#70249667986640) expected, got String(#70249634794540) My controller actions code are below:
def create
#classified = Classifiedad.new(classifiedad_params)
#classified.user = current_user
#classified.save
end
def update
#classified.update(classifiedad_params)
end
def classifiedad_params
params.require(:classifiedad).permit(:title, :body, :price)
end
I think it has something to do with the params as categorisation should be within a sub hash of results, is this right? Also, I need to do something extra within the actions, but what? What the params[:categorisation] value needs to do is save the number into the Categorisations.category_id table column and also save the polymorphic association. This table will be used across other models, which will also be a has_one association, as the user will only be able to select one category for each record. I really hope someone can help me here as the more I look into it the more I get confused :S Please let me know if you ned anymore info from me.
I'm using Rails 4 and Ruby 2
EDIT 2
I managed to get something working but I'm still not sure if its right. Below is the update code for the Create and Update actions. Would be good to know if there is a better way of doing this?
def create
#classified = Classifiedad.new(classifiedad_params)
#classified.user = current_user
**** NEW
cat = Categorisation.new(category_id: params[:classified][:categorisation])
#classified.categorisation = cat
**** END NEW
#classified.save
end
def update
**** NEW
#classified.categorisation.update_attribute(:category_id, params[:classified][:categorisation])
**** END NEW
#classified.update(classifiedad_params)
end
I'm new to rails and and I'm on the urge of learning Associations.
I'm using Rails version 3.
I have a user model and post model.My need is as below:-
Models
class User < ActiveRecord::Base
has_many :post
end
class Post < ActiveRecord::Base
belongs_to :user
validates_associated :user
end
Schema
ActiveRecord::Schema.define(:version => 20101016171256) do
create_table "posts", :force => true do |t|
t.integer "sell_or_buy"
t.string "title"
t.text "body"
t.integer "user_id" <<<<<<< I thought this will help to associate to user model.
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.string "password"
t.integer "rank"
t.datetime "created_at"
t.datetime "updated_at"
end
end
I thought keeping a user_id field and the belongs_to association will do my job, but
when i tried to display all the posts belonging to a user as follows:
<%= #user.posts %>
in my show.html.erb file. But I get only the following display:-
Name: saran
Email: saran.saran007#gmail.com
Password: abcd
Rank:
Edit | Back
Posts
#<Post:0xb69f47f8>#<Post:0xb69f3024>
I want to display the associated posts "title" and "body" in a readable format.
Also I'm able to create a post with a user_id in which no user exists!. The validates_associated :user is also not working, Please help me out.
Its
class User
has_many :posts
end
Not
has_many :post
Edit and Update your results.
You are getting the posts as expected in your view... So I'm not sure I understand that part of your question. As to the other part, validates_associated just ensures that the attached object is valid itself, and not if it exists at all. For that you want validates_presence_of. See the docs.
I wrote the following partial for my purpose and it works well :).
Thanks for all your inputs.
<% for post in #user.posts do %>
<h3> <%= post.title %> </h3>
<%= post.body %>
<% end %>
I'm trying to set up a new project for a music site. I'm learning ROR and am a bit confused about how to make join models/tables. Does this look right?
I have users, playlists, songs, and comments. Users can have multiple playlists. Users can have multiple comments on their profile. Playlists can have multiple songs. Playlists can have comments. Songs can have comments.
class CreateTables < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :login
t.string :email
t.string :firstname
t.string :lastname
t.timestamps
end
create_table :playlists do |t|
t.string :title
t.text :description
t.timestamps
end
create_table :songs do |t|
t.string :title
t.string :artist
t.string :album
t.integer :duration
t.string :image
t.string :source
t.timestamps
end
create_table :comments do |t|
t.string :title
t.text :body
t.timestamps
end
create_table :users_playlists do |t|
t.integer :user_id
t.integer :playlist_id
t.timestamps
end
create_table :playlists_songs do |t|
t.integer :playlist_id
t.integer :song_id
t.integer :position
t.timestamps
end
create_table :users_comments do |t|
t.integer :user_id
t.integer :comment_id
t.timestamps
end
create_table :playlists_comments do |t|
t.integer :playlist_id
t.integer :comment_id
t.timestamps
end
create_table :songs_comments do |t|
t.integer :song_id
t.integer :comment_id
t.timestamps
end
end
def self.down
drop_table :users
drop_table :playlists
drop_table :songs
drop_table :comments
drop_table :users_playlists
drop_table :users_comments
drop_table :playlists_comments
drop_table :songs_comments
end
end
Few things:
Short version:
Join tables should be alphabetical (e.g. create_table :comments_users
This is only one half of setting up your associations. I'd like to see your model code as well
Your comments model should be setup to be a polymorphic association to the others.
Long version:
When setting up a join table (e.g. users and comments) you want to make it alphabetical. So that the users/comments table is actually create_table :comments_users. Ideally, you would want to setup a more meaningful name for this join table (since it is a model - see below) but it's your call.
Remember that you have to create models for these :has_many through associations. Most of these join tables could be created with :has_and_belongs_to_many but for future expansions, may as well use the :has_many through method. It would be wise to provide your association code in your model files as well - just as important as the creation of tables.
For comments, think about polymorphic relationships. This is because the comments are the same across all the different tables it belongs to. What this does is look for a type column in the polymorphic join table and that tells it what table the record (i.e. comment) belongs to.
Starting Resources:
Check out Ryan's brilliant (as usual) Railscast on the subject:
http://railscasts.com/episodes/47-two-many-to-many
Also, on polymorphism:
http://railscasts.com/episodes/154-polymorphic-association
http://guides.rubyonrails.org/association_basics.html
That looks ok. You seem to have the ID's in all the right place.
Couple of points though:
It's often easier to have many small migrations. I generally create each model and migration individually. You can then start building your model relationships in lock-step with your tests to ensure that your business requirements are actually being implemented.
You have some tests, right? :)