Build form inside show action for outside model - ruby-on-rails

I am trying to build a form inside the show action of a controller. Here is my setup.
Category Model
class Category < ActiveRecord::Base
# Associations
has_many :feeds
has_many :subscriptions
# Nested attributes
accepts_nested_attributes_for :subscriptions
end
Subscription Model
class Subscription < ActiveRecord::Base
# Associations
belongs_to :profile
belongs_to :category
belongs_to :feed
end
Using a Subscription namespace:
# Subscriptions
namespace :subscriptions do
resources :categories
end
Inside the show action for a category (http://URL.com/subscriptions/categories/1), I want to build the subscriptions model form and list the available feeds for that category. I want the form to submit the checked off feeds to the Subscription model.
CategoriesController
def show
#feeds = Feed.where("category_id = ?", #category)
end
Trying to build the form in the Show action of the Categories view:
<%= simple_form_for [:subscriptions, #category] do |f| %>
<%= f.association :feeds, collection: #feeds, value_method: :id, as: :check_boxes %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Right now it is rendering the appropriate information, but it is rendering as an edit form, not a create form. How do I fix this?
What else do I need to add to my routes / controller to actually get the information to save to the Subscription model?
Thanks.

first thing i am not sure where you initialize the #category object in show action and in view. if you want to render a new page than it should be map to new action of the controller and you path with will be
http://URL.com/subscriptions/categories/new
the path you are using
http://URL.com/subscriptions/categories/1
will always map to the show action according to rails convention though you can override that for you accordingly but that is not a good practice. also in new action initialize the instance by
#category = Category.new
now on submit it will automatically goes to the create action. if your #category is already persisted in the database for example
#category = Category.find(1)
now if this object goes to the simple_form_for it will send the data to update action .

Related

How to create multiple children objects after the parent has been created?

This is my scenario:
I have a an Order model and a Item model. They have the following relationship:
class Order < ApplicationRecord
has_many :items
end
class Item < ApplicationRecord
belongs_to :order
end
In my project, initially, I need to create the Order without Items. After that I need to create the items related to that order.
I have already tried user nested_attributes, however, I'm gonna need to created items more than once and in the second time try the Items I have already created shows up in the form for editing.
Any suggestions on the best approach?
EDIT:
Add one more info. I need the option to create multiple items at once.
An option could be to create first your Order, and then the items.
# config/routes
...
resources :orders do
resources :items
end
# app/controllers/itesm_controller.rb
class ItemsController < ApplicationController
def new
order = Order.find(params[:order_id]
#item = order.items.new
end
def create
item.create(item_params)
redirect_to orders_path # just guessing your paths
end
protected
def item_params
params.require(:item).permit(:your, :attributes, :here)
end
end
# Assuming Rails +5.1
# app/views/items/_form.html.erb
# you can use this partial in 'new.html.erb' and 'edit.html.erb'
<%= form_view model: #item do |form| %>
<%= form.label :your_attribute %>
<%= form.text_field :your_attribute %>
<%= form.submit %>

Random Generation of Items from Existing Model

I am fairly new to RoR and trying to get a basic app to work - I have a 'books' model and a 'genre' model. I wish to create a page that randomly generates books of different genre's for a user to select.
I have created a 'random_book' controller, but am unsure on how to proceed with the random selection and display.
Any help/pointers would be appreciated.
Edit:
Here's the work I've been doing in the random_book model:
" load 'user.rb'
class random_book < ActiveRecord::Base
belongs_to :book
belongs_to :genre
def get_random_book
find(:all).sample(5)
end
"
Thank you.
Based on discussion
4 models
Book
Genre
UserBook
User
They will look something like this:
class User < ActiveRecord::Base
has_many :user_books
has_many :books, through: :user_books
end
class Genre < ActiveRecord::Base
has_many :books
def fetch_random_books(qty)
#you want to make sure you don't error out by requesting a sample of an empty list of books so check first. The qty argument lets you control the number of books for the search
unless self.books.empty?
self.books.limit(qty).sample
end
end
end
class Book < ActiveRecord::Base
belongs_to :genre
has_many :user_books
end
class UserBook < ActiveRecord::Base
belongs_to :book
end
I would most likely use a different route for the random book section, because it's not very url-friendly to say code-descriptive things like user_book.
There are 4 things to do
Create a new route to get a list of genre_ids that a user chooses.
Create an action that correlates to the route you created that renders a list of boos and adds those books to a users list
Create a form in a view (any view, like a sidebar in an existing books view, doesn't matter) this form will post the route and action you just made
Create a view to render the book list (the easy / DRY way is to add a few elements to the existing books index to let users know its a random generated list of books based on their genre pics)
Add the route
post "/random-books", to: "books#random_books", as: :random_books
Create the action in the books_controler
def random_books
if params[:genre_ids]
genres = Genre.where(id: params[:genre_ids])
#books = []
genres.each do |genre|
#books << genre.fetch_random_books(10)
end
else
#books = nil
end
respond_to do |format|
format.html { render action: :index }
end
end
Then create a form that makes a post request to the index action of the books_controller -- You can parse the form and update the UserBook model inside that action, and then display list of books all at the same time.
<%= form_tag(random_books_path, method: :post) do %>
<ul>
<% Genre.all.each do |genre| %>
<li>
<label class='genre-select'>
<%= check_box_tag 'genre_ids[]', genre.id -%>
<%= genre.name %>
</label>
</li>
<% end %>
</ul>
<%= submit_tag "Fetch Book List"%>
<% end %>
-- The last one I'm sure you can do, it returns a books object list so parse it however works best for you. In the controller action you can automatically add the ids for the books to the UserBook model by adding this inside the controller action:
#books.each{ |book| book.user_books.create(user: user)}

Nested_form has_one association

I have 2 models dog and litter_field:
class Dog < ActiveRecord::Base
belongs_to :user
has_one :litter_field
accepts_nested_attributes_for :litter_field
attr_accessible :litter_field_attributes
end
class LitterField < ActiveRecord::Base
belongs_to :dog
attr_accessible :breed_type
end
In my controller I have:
class DogsController < ApplicationController
def edit
#dog = Dog.find(params[:id])
#dog.build_litter_field
end
And in my view I have:
<%= simple_form_for #dog do |f| %>
<%= f.fields_for :litter_field do |l| %>
<div>
<%= l.label :breed_type %>
<%= l.input_field :breed_type %>
</div>
<%= f.button :submit, "Save" %>
<% end %>
I've looked at the documentation and from what I can tell this should work, however this page is not on the main edit page which I'm assuming is where the problem lies. Should I be adding what's in the edit action to a new action which displays the litter_field edit form?
EDIT:
What I am trying to do is split the edit form into separate pages, I've done this by adding additional actions that render extra pages so a user would go to dogs/settings/litter for example to see the litter_field nested form. I've tried adding #dog.build_litter_field to the litter action which displays the fields but when I try and save the form I am getting the error:
Failed to remove the existing associated litter_field. The record failed to save when after its foreign key was set to nil.
EDIT 2:
Fixed the above with adding:
has_one :litter_field, :dependent => :destroy
accepts_nested_attributes_for :litter_field, update_only: true
To dog.rb, the only problem I have now is it won't display the saved value on edit.
If you call #dog.build_litter_field in the edit action, it will build a new LitterField overtop of whatever had been saved previously. I would suggest trying something like this to see if it solves the problem you are currently seeing:
def edit
#dog = Dog.find(params[:id])
#dog.build_litter_field if #dog.litter_field.nil?
end
It can also help to checkout the debugger gem. You can use that to step through controller actions and see what the objects look like after each statement.

Rails form_for and has_many through argument error

I'm trying to build a form_for to create a join model between two other models. I have a Book model and User model, with another called Reads that is my join. Here is how I've set up the associations:
class User < ActiveRecord::Base
has_many :reads
has_many :books, :through => :reads
end
class Book < ActiveRecord::Base
has_many :reads
has_many :users, :through => :reads
end
class Read < ActiveRecord::Base
belongs_to :book
belongs_to :user
end
I've looked at the docs for form_for and watched the railscast episode on many-to-many associations, but I can't figure out why I'm getting the error when I try to render the Book#show view where I've put the form:
First argument in form cannot contain nil or be empty
Here is my form in app/views/books/show.html.erb:
<%= form_for(#read) do |f| %>
<%= f.hidden_field :book_id, value: #book.id %>
<%= button_to 'Add to Reads', {controller: 'reads', action: 'create'}, {class: 'btn'} %>
<% end %>
I think part of the problem is that I am trying to create a 'Reads' object from the Books model, but I'm not sure what I am doing wrong. I need the 'Add to Reads' button on the Book's page so that a user can select that particular book to add to their 'reads.' I'm also adding the current_user id in the controller, rather than in the view. Here is my create action from the Reads controller if that helps...
def create
#read = Read.new(read_params)
#read.user_id = current_user.id
#read.save
if #read.save
# do this
else
# do that
end
end
And I'm using strong params...
def read_params
params.require(:read).permit(:user_id, :book_id)
end
Thanks for any help.
First argument in form cannot contain nil or be empty
This means that #read in your form is nil. Since you are in the show action of your Books controller, you have to define this variable in the books controller.
def show
#read = Read.new
...
end

belongs_to parent form with has_many nested form in Rails 3

Abbreviated models:
class Event < ActiveRecord::Base
belongs_to :place
accepts_nested_attributes_for :place
end
class Place < ActiveRecord::Base
has_many :events
end
Abbreviated events controller:
def new
#event = Event.new
#event.build_place
def create
#event = Event.new(params[:event])
respond_to do |format|
if #event.save
Abbreviated view:
<%= form_for(#event) do |f| %>
<%= fields_for #event.place do |place_f| %>
Given the above...
I want a user to be able to create an event. When they create the event, they have the option of adding a place. The place may or may not exist in the database.
Right now, the place isn't associated or created on form submission, but it is definitely in the post parameters.
Any thoughts on what I'm doing wrong? This is for Rails 3.
If you dont want to let user fill place attributes, remove #event.build_place from your new action in controller like this
def new
#event = Event.new
end
and remove <%= fields_for #event.place do |place_f| %> block from your view.

Resources