I have an model that belongs to different models (game belongs to field and organiser), but when I fill the form to create the game, my creation method is not catching up the field reference
class Game < ApplicationRecord
belongs_to :organiser
belongs_to :field
end
class Organiser < ApplicationRecord
has_many :games, dependent: :destroy
end
class Field < ApplicationRecord
has_many :games, dependent: :destroy
end
Controller
class GamesController < ApplicationController
before_action :set_game, only: [:show, :edit, :update, :destroy]
def new
#organiser = Organiser.find(params[:organiser_id])
#game = Game.new
end
def create
#game = Game.new(game_params)
organiser_id = current_organiser.id
#organiser = Organiser.find(organiser_id)
#game.organiser = #organiser
#game.save
redirect_to organiser_games_path(#organiser)
end
def edit
organiser_id = current_organiser.id
#organiser = Organiser.find(params[:organiser_id])
end
def update
organiser_id = current_organiser.id
#organiser = Organiser.find(organiser_id)
#game.update(game_params)
redirect_to organiser_games_path(#organiser)
end
private
def game_params
params.require(:game).permit(:field_id, :total_players)
end
def set_game
#game = Game.find(params[:id])
end
_form parcel for new and edit view
<%= simple_form_for [#organiser, #game] do |f| %>
<div class="form-inputs">
<%= f.input :field_id, as: :select, collection: Field.all.collect(&:location) %>
<%= f.input :total_players %>
</div>
<div class="form-actions">
<%= f.button :submit, "Create", class: "btn btn-primary" %>
</div>
<% end %>
I am a beginner, and if you guys can help me with basic solutions that will help me to understand the complex bit, I will appreciate
I received no message, it looked like it went trough but it did not
collection: Field.all.collect(&:location)
Your collection has to be an array with the relation's id and the displayed value. Here, you only collect the value of location for each Field.
this code should do the trick:
<%= f.input :field_id, as: :select, collection: Field.all.map { |field| [field.id, field.location] } %>
field.id will be the value of your select option (currently, there's no value so it can't works), and field.location will be the text of your select option. :)
Related
I have two models (teams and students) and when creating a team I want to be able to add a student to the team using their email. I can do this in the rails console by doing team.students << student but I am unsure how to translate that functionality in the controller and view.
Team controller:
def new
#team = Team.new
end
def add_student
#team = Team.find(params[:id])
#team.students << Student.find(params[:student_email])
end
def create
#team = Team.new(team_params)
if #team.save
redirect_to teams_path
else
render 'new'
end
end
private
def team_params
params.require(:team).permit(:name, student_attributes:[])
end
def current_team
team = Team.find(params[:id])
end
end
Team view:
<%= form_with(model: #team, local: true) do |f| %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= fields_for :student do |s|%>
<%= s.label :email%>
<%= s.text_field :email, class: 'form-control' %>
<% end %>
<%= f.submit "Create Team", class: "btn btn-primary" %>
<% end %>
Thank you for your help
You can do a lot better then just using HABTM:
class Team < ApplicationRecord
has_many :memberships
has_many :students, through: :memberships
end
class Student < ApplicationRecord
has_many :memberships
has_many :teams, through: :memberships
end
# rails g model team student:belongs team:belongs_to
class Membership < ApplicationRecord
belongs_to :student
belongs_to :team
validates_uniqueness_of :student_id, scope: :team_id
end
This also creates a many to many assocation but it gives you an actual model so you can access additional columns on the table (like for example if you want to add roles or keep track of who added the student to the team) and its actually a real entity in your buisness logic instead of just a peice of plumbing.
HABTM is actually quite useless.
To add/remove members from a team you create and destroy memberships.
resources :teams do
resources :memberships,
only: [:new, :create, :destroy]
shallow: true
end
class MembershipsController < ApplicationController
before_action :set_team, only: [:new, :index, :create]
# GET /teams/1/memberships/new
def new
#students = Student.where.not(id: #team.students)
#membership = #team.memberships.new
end
# POST /teams/1/memberships
def create
#membership = #team.memberships.new(membership_params)
if #membership.save
redirect_to #team, notice: "Student added to team"
else
#students = Student.where.not(id: #team.students)
render :new
end
end
# DELETE /memberships/1
def destroy
#membership.destroy
redirect_to #membership.team, notice: "Student removed from team"
end
private
def set_team
#team = Team.find(params[:team_id])
end
def set_membership
#membership = Membership.find(params[:id])
end
def membership_params
params.require(:membership)
.permit(:student_id)
end
end
<%= form_with(model: #membership, local: true) do |f| %>
<div class="field">
<%= f.label :student_id %>
<%= f.collection_select :student_ids, #students, :id, :email %>
</div>
<%= f.submit %>
<% end %>
As a rule of thumb if you're creating a method on your controller thats not one of the standard CRUD methods and it contains a synonym to of one of them (add, remove, etc) you're almost certainly doing it wrong and should treat it as separate RESTful resource.
On my form on has_one association the fields do not appear for a singular form.
<%= f.fields_for :pack_social_media_sur_mesure, #commande.pack_social_media_sur_mesure do |ff| %>
remains empty
i think i missed something...
My Models :
Commande model :
class Commande < ApplicationRecord
belongs_to :user
validates_presence_of :user
has_one :pack_social_media_sur_mesure, dependent: :destroy
accepts_nested_attributes_for :pack_social_media_sur_mesure, allow_destroy: true
end
PackSocialMediaSurMesure model :
class PackSocialMediaSurMesure < ApplicationRecord
belongs_to :commande
...
end
My controller :
class CommandeStepsController < ApplicationController
...
def update
#user_id = current_user.id
#user = current_user
#commande = #user.commande
#commande.update(commande_params)
end
...
def commande_params
params.require(:commande).permit(:id,..., pack_social_media_sur_mesure_attributes: [:id, ...])
end
end
My form :
<%= form_for #commande, url: wizard_path, html: { class: "pack-slide" }, method: :put do |f| %>
<%= f.fields_for :pack_social_media_sur_mesure, #commande.pack_social_media_sur_mesure do |ff| %>
<%= ff.select :question1 %>
...
<% end %>
<%= f.submit %>
<% end %>
PS : I use wicked gem for this form, this why wizard_path.
Thx,
Théo
Ok I find.
The reason why the fields do not appear is that I had already created an 'commande' but not a 'pack_social_media_sur_mesure'. Or the "wicked gem" takes us to the edit path but we cannot edit it if it does not exist.
The solution was to create the pack_social_media_sur_mesure when creating the 'order' as bellow :
class CommandesController < ApplicationController
before_action :set_commande, only: [:show, :edit, :update, :destroy]
...
def create
#commande = current_user.create_commande(commande_params)
if #commande.save
if #commande.pack_social_media_sur_mesure == nil
#commande.create_pack_social_media_sur_mesure(...)
end
redirect_to ...
else
render :new
end
end
...
def set_commande
#commande = current_user.commande
end
Problem
I'm trying to create a middle table called category_profiles, is a intermediate table to assign favorite categories to my profiles, but I can't access to the category_ids, that I put in my form, always I got the same validation, Category doesn't exist:
Code:
class CategoryProfile < ApplicationRecord
belongs_to :profile
belongs_to :category
end
class Category < ApplicationRecord
has_many :category_profiles
has_many :profiles, through: :category_profiles
class Profile < ApplicationRecord
has_many :category_profiles
has_many :categories, through: :category_profiles
When I'm doing the create action, my controller can't find my category. How do I fix it?
My create action never find the ids of my categories to assign to the category_profiles. It has many through relation:
Module Account
class FavoritesController < Account::ApplicationController
before_action :set_category_profile
def index
#favorites = #profile.categories
end
def new
#categories = Category.all
#category_profile = CategoryProfile.new
end
def create
#category_profile = #profile.category_profiles.new(category_profile_params)
if #category_profile.save
flash[:success] = t('controller.create.success',
resource: CategoryProfile.model_name.human)
redirect_to account_favorites_url
else
flash[:warning] = #category_profile.errors.full_messages.to_sentence
redirect_to account_favorites_url
end
end
def destroy
end
private
def set_category_profile
#category_profile = CategoryProfile.find_by(params[:id])
end
def category_profile_params
params.permit(:profile_id,
category_ids:[])
end
end
end
Form
<%= bootstrap_form_with(model: #category,method: :post , local: true, html: { novalidate: true, class: 'needs-validation' }) do |f| %>
<div class="form-group">
<%= collection_check_boxes(:category_ids, :id, Category.all.kept.children.order(name: :asc), :id, :name, {}, { :multiple => true} ) do |b| %>
<%= b.label class: 'w-1/6 mr-4' %>
<%= b.check_box class: 'w-1/7 mr-4' %>
<%end %>
</div>
<div class="md:flex justify-center">
<%= f.submit 'Guardar categoría favorita', class: 'btn btn-primary' %>
</div>
<% end %>
Seems like you just want to update intermediate table. So you can do it like this.
def create
begin
#profile.categories << Category.find(params[:category_ids])
Or
params[:category_ids].each do |category_id|
#profile.category_profiles.create(category_id: category_id)
end
flash[:success] = t('controller.create.success',
resource: CategoryProfile.model_name.human)
redirect_to account_favorites_url
rescue
flash[:warning] = #category_profile.errors.full_messages.to_sentence
redirect_to account_favorites_url
end
end
Need to find other better way for error handling using either transaction block or something.
I have three models: User, Publisher and Interest all with many to many relationships linked through three join models but only 2 out of 3 join models record the id's of their 2 parent models. my UsersPublisher model does not link User to Publisher.
My Interestscontroller proccesses a form (see code) through which I ask the user to provide Interest and Publisher. The latter gets processed via the fields_for method which allows you to pass Publisher attributes via the InterestsController. the UsersPublisher join model records the user_id but the publisher_id is nil.
I've tried putting #users_publishers in both the new and create methods of Publishers- and InterestsController. My latest attempt of using after_action in the InterestsController (see code) has also failed. I've also tried the after_action way in the PublishersController
Your helped is highly appreciated!
The UsersPublisher join model
class UsersPublisher < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
end
InterestsController
class InterestsController < ApplicationController
before_action :find_user
after_action :upublisher, only: [:new]
def index
#interests = policy_scope(Interest)
end
def show
#interest = Interest.find(params[:id])
end
def new
#interest = Interest.new
#interest.publishers.build
authorize #interest
end
def create
#interest = Interest.new(interest_params)
#users_interests = UsersInterest.create(user: current_user, interest: #interest)
authorize #interest
if #interest.save
respond_to do |format|
format.js
format.html {redirect_to root_path}
end
flash[:notice] = 'Thank you, we will be in touch soon'
else
respond_to do |format|
format.js { render }
format.html { render :new }
end
end
end
def edit
#interest = Interest.find(params[:id])
authorize #interest
end
def update
#interest = Interest.find(params[:id])
#interest.update(interest_params)
if #interest.save
flash[:notice] = 'Your interest has been added'
else
flash[:notice] = 'Oops something went wrong'
end
end
private
def interest_params
params.require(:interest).permit(:name, publishers_attributes: [:publisher,:id, :feed])
end
def find_user
#user = current_user
end
def upublisher
#users_publishers = UsersPublisher.create(publisher: #publisher, user: current_user)
end
end
Form
<%= form_for [#user, #interest] do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :publishers do |ff| %>
<%= ff.label :publisher %>
<%= ff.text_field :publisher %>
<%= ff.label :feed %>
<%= ff.text_field :feed %>
<%end%>
<%= f.submit "Submit" %>
<%end%>
Since you're using fields_for, you'll want to make sure you have accepts_nested_attributes_for:
class UsersPublisher < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
accepts_nested_attributes_for :publisher
end
This should fix your issue (if it's as you outlined).
Your question is pretty broad, so I don't know whether the above will work. Below are my notes...
From the looks of it, your structure is very complicated; you should work to make it as simple as possible. In the case of creating "interests", you may wish to get rid of the form completely:
#config/routes.rb
resources :publishers do
resources :interests, path: "interest", only: [:create, :destroy] #-> url.com/publishers/:publisher_id/interest
end
#app/controllers/interests_controller.rb
class InterestsController < ApplicationController
before_action :set_publisher
def create
current_user.interests.create publisher: #publisher
end
def destroy
#interest = current_user.interests.find_by publisher_id: #publisher.id
current_user.interests.delete #interest
end
private
def set_publisher
#publisher = UserPublisher.find params[:publisher_id]
end
end
You'd be able to use the above as follows:
<%= link_to "Add Interest", publisher_interest_path(#publisher), method: :post %>
<%= link_to "Remove Interest", publisher_interest_path(#publisher), method: :delete %>
Thinking about it properly, you've got a pretty bad structure.
I'd do something like this:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :interests
has_many :publishers, through: :interests
end
#app/models/interest.rb
class Interest < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
accepts_nested_attributes_for :publisher
end
#app/models/publisher.rb
class Publisher < ActiveRecord::Base
has_many :interests,
has_many :users, through: :interests
end
This should give you the ability to create interests for any number of users and publishers. If you create a publisher for a specific user, you can use accepts_nested_attributes_for to pass the appropriate data:
#config/routes.rb
resources :users do
resources :interest, only: [:new, :create, :destroy] #-> url.com/users/:user_id/interests/new
end
#app/controllers/interests_controller.rb
class InterestsController < ApplicationController
def new
#user = User.find params[:user_id]
#interest = #user.interests.new
#interest.publisher.build
end
def create
#user = User.find params[:user_id]
#interest = #user.interests.new interest_params
end
private
def interest_params
params.require(:interest).permit(:user, :publisher)
end
end
#app/views/interests/new.html.erb
<%= form_for [#user, #interest] do |f| %>
<%= f.fields_for :publisher do |p| %>
<%= p.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
I've been having trouble setting up the form for a polymorphic "department" post in the department view. I followed the rails-cast tutorial for polymorphic associations here
Models:
class Course < ActiveRecord::Base
belongs_to :department, inverse_of: :courses
has_and_belongs_to_many :users, -> { uniq }
has_many :posts, as: :postable #allows polymorphic posts
end
class Department < ActiveRecord::Base
has_many :courses, inverse_of: :department
has_many :posts, as: :postable #allows polymorphic posts
has_and_belongs_to_many :users, -> {uniq}
end
class Post < ActiveRecord::Base
belongs_to :user, touch: true #updates the updated_at timestamp whenever post is saved
belongs_to :postable, polymorphic: true #http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
belongs_to :department, counter_cache: true #for counting number of posts in department
belongs_to :course, counter_cache: true
validates :department_id, :course_id, presence: true
end
config/routes
devise_for :users
devise_scope :users do
match '/users/:id', to: "users#show", via: 'get'
end
resources :departments do
resources :courses
resources :posts
end
resources :courses do
resources :posts
end
views/departments/show.html.erb
<div class="tab-pane" id="posts"><br>
<center><h3>Posts:</h3></center>
<%= render "posts/form", postable: #department %>
</div>
views/posts/_form.html.erb
<%= render "posts/wysihtml5" %>
<center><h3>Create New Post:</h3></center>
<%= form_for [#postable, Post.new] do |f| %>
<%= f.label :title %>
<%= f.text_field :title, class: "form-control" %>
<%= f.label :description %>
<%= f.text_area :description, :rows => 3, class: "form-control" %>
<%= f.text_area :content, :rows => 5, placeholder: 'Enter Content Here', class: "wysihtml5" %>
<span class="pull-left"><%= f.submit "Create Post", class: "btn btn-medium btn-primary" %></span>
<% end %>
controllers/post_controller.rb
class PostsController < ApplicationController
before_filter :find_postable
load_and_authorize_resource
def new
#postable = find_postable
#post = #postable.posts.new
end
def create
#postable = find_postable
#post = #postable.posts.build(post_params)
if #post.save
flash[:success] = "#{#post.title} was sucessfully created!"
redirect_to department_post_path#id: nil #redirects back to the current index action
else
render action: 'new'
end
end
def show
#post = Post.find(params[:id])
end
def index
#postable = find_postable
#posts = #postable.posts
end
...
private
def post_params
params.require(:post).permit(:title, :description, :content)
end
def find_postable #gets the type of post to create
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
controllers/departments_controller.rb
def show
id = params[:id]
#department = Department.find(id)
#course = Course.new
#course.department_id = #department
end
The error is "undefined method `posts_path' for #<#:0x0000010d1dab10>"
I think the error has something to do with the path in the form, but I don't know what. I've tried [#postable, #postable.posts.build] as well but that just gives me undefined method: PostsController.
Anybody know what's going on and how I can fix it?
#department is passed into the form partial as a local variable, but the form calls an instance variable:
# views/departments/show.html.erb
<%= render "posts/form", postable: #department %> # <------ postable
# views/posts/_form.html.erb
<%= form_for [#postable, Post.new] do |f| %> # <------ #postable
Thus, the namespaced route is not properly determined
[#postable, Post.new] # => departments_posts_path
[ nil , Post.new] # => posts_path
Checking your routes, posts are only accessible via nested routes. posts_path is not a valid route, it's method does not exist, and the error is correct: undefined method `posts_path'
Fix:
Set a #postable instance variable in the departments controller so that the form helper can use it:
def show
id = params[:id]
#postable, #department = Department.find(id) # <-- add #postable
#course = Course.new
#course.department_id = #department
end
Then you can simply call render in the view:
<%= render "posts/form" %>