I am trying to build a 'ajaxy' form between two models in rails. here are the relevant files and code snippets...
my models
ActiveRecord::Schema.define(:version => 20140214134314) do
create_table "group_shots", :force => true do |t|
t.integer "shot_id"
t.integer "group_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "groups", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "shots", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
end
app/views/groups/show.html.erb
<b>Name:</b>
<%= #group.name %>
<%= link_to 'New Shot', new_shot_path, id: "new_shot", remote: true %></br>
app/views/shots/new.js.erb
$('#new_shot').hide().after('<%= j render("form") %>');
app/controllers/shots_controller.rb
def create
#shot = Shot.new(params[:shot])
respond_to do |format|
if #shot.save
GroupShot.create! :shot_id => #shot.id
def new
#shot = Shot.new
end
My end goals is when a user is viewing a group (show action/view) that they can add a shot that is associated with that group, and consequently when the user adds the shot, the correct entries are made in the group_shot table.
You can see in the shots controller, when the shot is saved I am creating a new entry in the group/shots table successfully. I am getting the shot_id, but not the group_id. So that is the question, how do I get the group_id in this view/controller. Any help appreciated.
My suggestion is to pass the group_id as a parameter to the call to new_shot_path in your groups_controller#show view:
new_shot_path(group_id: #group)
then include it as a hidden field in the form you're using in your shots_controller#new view, using hidden_field_tag:
<%= hidden_field_tag 'group_id', #group.id %>
and then include it in your call to GroupShot#create in the shots_controller#create method:
GroupShot.create! :shot_id => #shot.id, :group_id => params[:group_id]
Just keep in mind that you're doing two things in shots_controller#create -- both creating the shot and associating it back to the group, which is perhaps a little unorthodox. From your code, it appears that a Shot is never created without an associated Group. If your starting point is always from viewing groups (then adding shots to a selected group), you may want to consider moving this to the groups_controller as something like #add_shot, but that's going to be depend on how the rest of your application is architected.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I have 6 modules (1. ingredients; 2. restaurants; 3. sandwich_ingredients; 4. sandwiches; 5. stocks; 6. users) and the user should be able to create/personalise a sandwich using the ingredients of that specific restaurant. Therefore, I would like to create a form on the show.html.erb of the restaurant so that the user can pick up several ingredients and create their own sandwich.
I feel comfortable working with 3 modules in a many-to-many relationship but with 6 tables I just get confused and lost.
I tried form_for, form_tag, collection_check_boxes. When I used to work with a smaller project, I had no issues but now I'm not sure what approach should I take. I tried to create the form on the user show page first and then on the sandwich show page but it didn't seem quite right.
This is the code from the restaurant show.html.erb page:
<ul>
<%= form_tag('/restaurants/create_sandwich') do %>
<%= label_tag 'Ingredients' %>
<li><%= collection_check_boxes(:ingredient, :id, #restaurant.ingredients, :id, :name) %></li>
<%= submit_tag 'Create' %>
<% end %>
</ul>
The restaurants_controller.rb (messed up):
class RestaurantsController < ApplicationController
def index
#restaurants = Restaurant.all
end
def show
#restaurant = Restaurant.find(params[:id])
#user = User.new
#sandwich = Sandwich.new
end
def new
#sandwich = Sandwich.new
# res = Restaurant.find(params[:id])
# #restaurant_ingredients = res.ingredients
end
def select_ingredients
# byebug
#
# #restaurant_ingredients = Restaurant.find(params[:id]).ingredients
# byebug
# #restaurant_ingredients = res.ingredients
# #ingredients = Ingredient.all
end
def create_sandwich
# byebug
#user = User.find_or_create_by({name: params[:user_name]})
#sandwich = Sandwich.new({name: params[:recipe_name], user_id: #user.id})
#sandwich.sandwich_ingredient_ids = params[:ingredients]
if #sandwich.save
redirect_to sandwich_path(#sandwich)
else
render :new
end
end
def edit
#code
end
def update
#code
end
def delete
#code
end
end
If it can help, these are the routes:
Rails.application.routes.draw do
# get '/restaurants/:id', to: 'restaurants#create_sandwich'
post '/restaurants/create_sandwich', to: 'restaurants#create_sandwich'
resources :stocks
resources :restaurants
resources :sandwich_ingredients
resources :ingredients
resources :sandwiches
resources :users
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
and this is my schema:
ActiveRecord::Schema.define(version: 2019_04_15_174308) do
create_table "ingredients", force: :cascade do |t|
t.string "name"
t.integer "quantity"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "restaurants", force: :cascade do |t|
t.string "name"
t.string "location"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "sandwich_ingredients", force: :cascade do |t|
t.integer "sandwich_id"
t.integer "ingredient_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "sandwiches", force: :cascade do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "stocks", force: :cascade do |t|
t.integer "restaurant_id"
t.integer "ingredient_id"
t.integer "count"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "surname"
t.string "email"
t.string "phone_number"
t.integer "age"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
What I'm trying to achieve is a form that would create the association "SandwichIngredient" and to do so, the "User" and the "Sandwich" but at this point I feel just lost.
Lots of areas to comment on here, and the question is quite vague but I'll give some tips
Your 'new' action on your restaurant controller should be for a new restaurant, not a new sandwich.
Your 'new sandwich' form should probably be directing to the create action on your sandwiches controller. You're trying to do too much on the restaurants controller.
If your ingredients belong to a restaurant, they need a restaurant_id field
You probably don't want to create a new user each time you create a sandwich, perhaps you want to add devise or a similar user management gem to your project and have them sign in, and assign the sandwich to the current user, or have a drop down select of the users in the system as part of the sandwich form
As a general tip, I would completely ignore your stocks model for now, focus on getting sandwiches working first.
Not super related but would be good to have either one 'name' field for a user or 'first_name' and 'last_name' to avoid confusion
You may need to add a hidden field in the form to contain the restaurant_id to be able to pass this value through when you submit the form
Make sure you have a field on the sandwich form for the name of the sandwich
I would probably recommend sticking with 'form_for' for #sandwich, which by default should go to the sandwiches create action as mentioned above.
Feel free to post a link to this project on github or similar if you need further help.
I am building a cinema application in Ruby on Rails and am currently working on the booking system. What I want to be able to do is select a film from a drop down menu, and then the show times for that film are displayed in a drop down menu, the user can select a show time and then the seats available are displayed in a drop down menu.
I have watched this video: https://www.youtube.com/watch?v=GYg6s-b1XGo and have looked at many other sites but what I want to do is a bit more complicated.
Ok, so my models/bookings.rb:
class Booking < ActiveRecord::Base
belongs_to :user
belongs_to :showing
belongs_to :seat
end
models/showing.rb:
class Showing < ActiveRecord::Base
belongs_to :film
has_many :bookings
belongs_to :screen
def showing_times
"#{show_date.strftime("%e %b %Y")} # #{show_time.strftime("%H:%M")}"
end
end
models/seats.rb:
class Seat < ActiveRecord::Base
belongs_to :screen
has_many :bookings
def seats_available
"#{row_letter}#{row_number}"
end
end
This is where it gets complicated, my views/bookings/_form.html.erb:
<%= form_for #booking do |f| %>
<%= f.hidden_field :user_id %>
<%= image_tag "thor_hammer.jpg",:size => "900x250" %>
<td width="300px">
<br><%= f.label :film_id, 'Film:' %>
<br><%= f.collection_select :film_id, Film.all, :id,:title_info %>
<br><%= f.label :showing_id, 'Showing:' %>
<br><%= f.collection_select :showing_id, Showing.all, :id,:showing_times %>
<br><%= f.label :seat_id, 'Seat ID:' %>
<br><%= f.collection_select :seat_id, Seat.all, :id,:seats_available %><br>
</td>
<td width="300px">
<div class="actions">
<%= f.submit %>
</div>
<br>
<%= render "/error_messages", :message_header => "Cannot save: ", :target => #booking %>
</td>
<% end %>
This is where it gets complicated because the line <%= f.collection_select :film_id, Film.all, :id,:title_info %> causes the error:
NoMethodError in Bookings#new
undefined method `film_id' for #<Booking:0x584e6b0>
Because the bookings data table does not store the film, it stores the showing time and this has the film associated with it, this is the same for the seats: the user needs to be able to select a showing to select the seat but the showings table is associated with the screens table, which is associated with the seats.
To make this clearer, here is the schema:
create_table "bookings", force: :cascade do |t|
t.integer "user_id"
t.integer "showing_id"
t.integer "seat_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "films", force: :cascade do |t|
t.string "title"
t.string "synopsis"
t.string "director"
t.string "cast1"
t.string "cast2"
t.string "cast3"
t.date "release_date"
t.string "warnings"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image_url"
t.string "certificate_id"
t.integer "category_id"
t.integer "hours"
t.integer "minutes"
t.string "video_url"
end
create_table "screens", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "seats", force: :cascade do |t|
t.string "row_letter"
t.integer "row_number"
t.integer "screen_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "showings", force: :cascade do |t|
t.date "show_date"
t.time "show_time"
t.integer "film_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "screen_id"
end
Has anyone got any suggestions?
You are trying to set the film_id of an instance of the Booking class. You are correct to notice that you do not have a film_id in the bookings table.
The easiest way to accomplish what you desire is simply to add a film_id to the bookings table and to create a has_one and belongs_to association between Booking and Film.
Another way to accomplish this, that will require you to dive deeper into rails is to use accepts_nested_attributes_for. Here's a good tutorial and here's a newer tutorial if you run into any issues with the first one. This will allow you to not have to create redundant associations.
UPDATE 1
After looking closer at what you are trying to do I realized that you are not trying to create a new resource (film) so please disregard my previous comment about nested attributes.
The solution to what you are trying to do involves a few moving parts that are a little too long to give in an answer like this. However, here is a summary that may guide you while you try to figure it out:
You need a separate form to select the film first
Based on the film selection you can use ajax to fetch all the showings that belong to that film (For this you need to read up on ajax; you also need to read up on how to expose your data via a json endpoint in rails) You will end up passing an id of the film the user has selected and you will return the result of this query: Showing.where(film_id: <id>)
After you managed to get that data you need to populate it in a second form that ends up displaying only the showings associated with that film.
This seems a bit complicated for what you are trying to do. What I would recommend is that you either:
spend some extra time on figuring out how to structure your models a little differently
or that you don't allow the users to select the film and then the showings on the same page (dynamically). Make them select the film first. After they selected the film they are redirected to a page where they can select the showings. This would be a worse experience for the user but it would enable you to understand rails a little better (ajax and javascript wouldn't be required in this scenario).
I assume you're doing this to try to get a better grasp of rails, so I hope this information helps you. Good luck!
I have a Post, Reply and a Vote model (polymorphic):
create_table "posts", :force => true do |t|
t.text "content", :limit => 255
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "title"
t.integer "replies_count", :default => 0, :null => false
t.integer "category_id"
end
create_table "replies", :force => true do |t|
t.text "content"
t.integer "post_id"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "votes", :force => true do |t|
t.integer "votable_id"
t.string "votable_type"
t.integer "user_id"
t.integer "polarity"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
I needed the total_votes of posts and replies so I created an instance method:
post.rb and reply.rb:
def total_votes
self.votes.map {|v| v.polarity }.sum
end
So I can use it to sort posts and replies:
homepage:
default_order = "created_at DESC"
params[:order_by] ||= default_order
#feed_items = #post.replies.paginate(page: params[:page],
per_page: 10).order(params[:order_by])
So now, I'm not very sure what do add after order_by in the view:
<span><%= link_to 'total_votes DESC', root_path(order_by: HERE) %></span>
I tried &:total_votes DESC and total_votes DESC but didn't work.
What's the right way of doing this? (I thought it was a bit of unnecessary to add a total_votes column to both posts and replies tables, but not sure if that is better for performance?)
I would start with using the database to do the summing for you, but this is a little bit tricky because you need to order by a value that is calculated (an "aggregate"), but let's set that aside.
Your view will display items in the order determined when the instance variable is set in the controller. So in the controller, you may detect that there's an order_by parameter (e.g. /feed?order_by=total_votes, and load the #feed_items accordingly, e.g.
def index
if params[:order_by] && params[:order_by] == 'total_votes'
sort_order = "total_votes DESC"
else
sort_order = "created_at DESC"
end
#feed_items = #post.replies.paginate(page: params[:page],
per_page: 10).order(sort_order)
end
then in your view, to change the sort order, create a link_to that adds the query string parameter (see the last example in the link_to api doc, e.g.
<%= link_to "Sort by Total Votes", root_path(:order_by => 'total_votes') %><br />
<%= link_to "Sort by Time", root_path %><br />
Your other question is about how to do the sorting by votes. This answer may provide a good start for that, and an answer to your performance question: Rails: Order by sum of two columns
Here's my schema file..
ActiveRecord::Schema.define(:version => 20120505115340) do
create_table "clients", :force => true do |t|
t.string "name"
t.string "detail"
t.string "more_detail"
t.string "more_details"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "jobs", :force => true do |t|
t.string "name"
t.integer "number"
t.string "responsible"
t.string "monthly"
t.string "quarterly"
t.string "other"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
end
And here's my migration file's..
class CreateClients < ActiveRecord::Migration
def change
create_table :clients do |t|
t.string :name
t.string :detail
t.string :more_detail
t.string :more_details
t.timestamps
end
end
end
class CreateJobs < ActiveRecord::Migration
def change
create_table :jobs do |t|
t.string :name
t.integer :number
t.string :responsible
t.string :monthly
t.string :quarterly
t.string :other
t.timestamps
end
end
end
In my view file, I have it setup so that is pulls out the client.name and shows it to the user <%= link_to client.name, client_path(client) %>.
However, all im getting back when I create a new entry is /clients/1 instead of the name that I specified in my form.
When I try to migrate the DB nothing happens and then when I try to drop he DB to start afresh it tells me that it does even exist.
If I understand you correctly, you are concerned that your view displays a link to /clients/1 for your newly created object?
This is the default path when using Ruby on Rails, and is what will be produced by the path helper object_path(object) that you are using. This can be customized (see guides on routes.rb). If this is not a problem, then your application is working as intended.
BtW, the number used in the default path refers to the id given to the object. All objects stored using ActiveRecord will automatically get a unique id which can be used to identify the object. Just as the created_at and updated_at columns in your schema, the id column will be created regardless if you explicitly define it in your schema or not.
To reset your database (drop, recreate and migrate to current schema), use the following command:
rake db:reset
EDIT:
<%= link_to client.name, client_path(client) %>
Should result in the following HTML (where CLIENT_NAME is the name attribute of the client)
CLIENT_NAME
I have two models, Zombie and Tweet. Schema below:
create_table "tweets", :force => true do |t|
t.string "status"
t.integer "zombie_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "zombies", :force => true do |t|
t.string "name"
t.string "graveyard"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
With the following associations:
class Zombie < ActiveRecord::Base
has_many :tweets
end
class Tweet < ActiveRecord::Base
belongs_to :zombie
end
In the Zombie#show view, I have added a "New Tweet" button sending it to Tweet#new (new_tweet_path). In the Tweet#new view I have a form with two fields: status and zombie_id. When I arrive at the Tweet#new page coming from a Zombie's profile, I don't want to have to fill in the zombie_id, or I'd like it to know what the id is since I just came from it's profile on the previous page.
What do I need to do to accomplish this? I'm assuming I need to send the zombie object from the Zombie#show page to the Tweet#new page, but I'm not sure what I need to do in the controller or views to handle this. Any advice?
In the Zombie#show view add zombie_id param to the new_tweet_path call like this:
new_tweet_path(zombie_id: #zombie.id)
Then in the Tweet#new create a Tweet model with already filled zombie_id, that was passed in params hash:
#tweet = Tweet.new(zombie_id: params[:zombie_id])