I have two controller in my application events and registrations. I want to send event_id of current event to registrations controller after form submission in event show template
show.html.erb
<%= form_for #registrations do |f| %>
<%= f.submit %>
<% end %>
registrations_controller
class RegistrationsController < ApplicationController
def create
#event = Event.find(params[:event_id])
end
end
but it gives me following error
cannot find event with id =
What i think that since event and registrations are not related in any way.So its giving this error.
How to solve this error
Without knowing more about the model associations and also what the #registrations object is, this is the easiest solution:
Add a hidden field to your form:
<%= f.hidden_field( :event_id, #event.id ) %>
# assumes #event exists in the show action somewhere
and in your controller:
def create
#event = Event.find(params[:registrations][:event_id])
end
If your models are associated you can do something like:
<%= form_for #event.registrations.new %>
but it's hard to say if that will work in your app based on the code provided.
Related
I'm implementing a classified website where I want to let user to create classified even if he is not registered. In case he is not registered I take his name, email and phone number. Its like those form on website where one can still create record without registering by only giving his email and name because asking new user to create account would turn away potential customers I am using devise and rails 4 and was wondering how can I implement this ideally.
Class User < ActiveRecord::Base
has_many :classifieds
end
class Classified < ActiveRecord::Base
belongs_to :user
end
I'm wondering if I should create a guest user as an instance of User or need to create new model to store classified poster that is not registered.
How you do this will depend mainly on how you're going to implement the user authentication
I see you've got devise as a tag - so I'll give you some ideas for it!
--
Devise
Devise has a helper called user_signed_in? - basically tells you if your current_user object is defined (and thus that your user is logged in).
In views, you can use this helper to determine how things work. A good example is in navigation:
#app/views/elements/nav.html.erb
<% if user_signed_in? %>
<%= link_to "Logout", user_session_destroy_path, method: :delete %>
<% else %>
<%= link_to "Login", new_user_session_path %>
<% end %>
In the same way, you can use the conditional nature of user_signed_in? for your form
--
Form
You'll basically need to ensure that you're able to process the form regardless of whether the user is signed in or not (I.E handle the credentials), but you could also use a conditional statement to determine which attributes to use:
<%= form_for #classified do |f| $>
<%= f.text_field :title %>
<%= f.text_field :description %>
<% if user_signed_in? %>
<%= f.fields_for :user do |user| %>
<%= user.text_field :name %>
<%= user.text_field :email %>
<%= user.text_field :phone_number %>
<% end %>
<% end %>
<% end %>
--
Controller
When you get the controller, you'll then need to consider whether the data has been submitted as form data or not. This is where user_signed_in? will again come in handy:
#app/controllers/classifieds_controller.rb
Class ClassifiedsController < ApplicationController
def new
#classified = Classified.new
#classified.build_user unless user_signed_in?
end
def create
#classified = Classified.new(classified_params)
#classified.user = current_user if user_signed_in?
end
private
def classified_params
params.require(:classified).permit(:title, :body, user_attributes(:name, :email, :phone)
end
end
What you can do is inside your create method for classifieds, you can look for a user and if he/she doesn't exist then create a dummy user with some random password and build classifieds for that dummy user. This will also keep your associations intact. Inside your create method you can do something like this:
def create
#user = User.find(params[:user_id])
if !#user
#user = User.create(attributes = {}, ...)
end
#classified = #user.classifieds.build(attributes = {}, ...)
if #classified.save
redirect_to your_path
else
render action: "new"
end
end
You can clean it more by using rails find_or_create_by method
I want people to be able to submit their email on the home page and then get redirected to the views/pages/about With the following code when they click submit on their email it takes them to http://localhost:3000/premails and gives the following error:
Routing Error
uninitialized constant PremailsController
I've tried adding code to my controller like this:
if #premail.save
redirect_to :action => :about
end
and other variations but they all give me a bunch of other problems and I haven't been able to figure out the cleanest simple way to do this
This is my pages_controller.rb
class PagesController < ApplicationController
def home
#premail = Premail.new
end
def about
end
end
This is the form in my views/pages/home.html.erb
<div class="container-form">
<%= form_for #premail, html: {class: "form-inline", role: "form"} do |f| %>
<% if #premail.errors.any? %>
<h2><%= pluralize(#premail.errors.count, "error") %> prohibited this link from being saved:</h2>
<ul>
<% #premail.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<div class="form-group signup-field">
<%= f.label :email, class:"sr-only" %>
<%= f.email_field :email, class:"form-control signup-input", placeholder:"Enter email" %>
</div>
<div>
<%= f.submit "Get Early Access", class:"btn btn-default signup-button" %>
</div>
<% end %>
</div>
This is my routing:
root 'pages#home'
resources :pages
resources :premails
This is my premail model
class Premail < ActiveRecord::Base
end
This is my migration:
class CreatePremails < ActiveRecord::Migration
def change
create_table :premails do |t|
t.text :email
t.timestamps
end
end
end
What would you change to re-route to the views/pages/about page and be able to retain the email(premail) in your database? Thanks!
you need the premails_controller.rb to make the view interact with the premail model.
Now when you have the premails controller.
resources :premails
will work and form_for #premail will create a form for an individual Premail model object.
you will have to now make the #premail instance variable available here :
views/premails/new.html.erb
by using this :
class PremailsController < ApplicationController
def new
#premail = Premail.new
end
def create
#premail = Premail.create(premail_params)
if #premail.save
redirect_to :action => :about
end
end
def about
render template: "premails/about"
end
private
def premail_params
params.require(:premail).permit(:email)
end
end
make sure you have the about.html.erb page there inside the premails path
The problem is in what your form_for #premails does when submitting it.
Clicking the submit button on a form_for object will try to send the object either to the 'create' or 'update' action it's own controller depending on whether it's a new resource or not.
as the error message shows here you do not have a Premails controller. You either need to create a Premails controller with a create an update action or you need to change where the form sends the submit button to.
If you create the controller than in the create action you can redirect_to the about_page_path like this:
def create
#premail = Premail.new(premail_params)
if #premail.save
redirect_to about_page_path
else
redirect_to :back
end
end
Or if you dont want to create an entire premail controller you can change what the f.submit button does in your form to direct to an action in your pages controller. You would have to do something like
button_tag type: "submit"
The preferred method would be to create a premails controller.
the problem is in what your form for premails does sumbitting it. Clicking the submit button on a form_for object will try to send the object either to the create or update action in your controller depending on whether it's a new resource or not.
as the error message shows here you do not have a premails controller. You either need to create a premails controller with a create an update action or you need to change where the form sends the submit button to.
you can do this by changing that f.submit to a button_tag type submit.
I have created a simple form to create an instance of a modle and for some reason it is not calling the create method in the controller. Here is the form code:
<% #house.mates.each do |mate| %>
<p><%= mate.name %></p>
<% end %>
<h2>Add a new mate:</h2>
<%= form_for #mate do |f| %>
<p><%= f.label "Name" %>
<%= f.text_field :name %>
<%= f.hidden_field :house_id %>
</p>
<%= f.submit "Submit", :action => :create %>
<% end %>
Here is the controller code:
class MatesController < ApplicationController
def new
#mate = Mate.new
end
def create
#mate = Mate.new(params[:mate])
#mate.save
redirect_to house_path(current_house)
end
end
There is a many to one relationship between the Mate model and the House model... I am fairly new to rails but I have made other apps with similar forms, and I have never had this problem before. I can create and save Mate objects in the console, and I am not getting any errors, so it seem that somehow the controller method is not being called. Any help is much appreciated!
In fact, if other things have no problem, your #mate object should be created. You just can't see it in house page because you have not associated #mate with house in your code.
In your form you referred :house_id, but this attribute is nil when you rendering the form.
The reason is you have not assigned it in controller.
In controller you need to initialize #mate from house object to have house_id inside it
def new
#house = something
#mate = #house.mates.new # Instead of Mate.new
end
I have a very small application I ma building in rails. It is a simple weight tracker app. I have created a User model which has a sign up page. Once the user logs in they are redirected to the user#show view. Here is the user controller so far:
class UsersController < ApplicationController
before_filter :authenticate_user!
def show
#user = current_user
end
end
I have 2 other models one is a Weight model and the other a Goal model, I would like it so what when a user signs up they are presented with a screen asking them to type in the current weight and goal weight this information will then be store in the Weight and Goal models respectively along with the logged in users ID.
So far I have been able to add a form to the user show.html.erb template :
<%= form_for #user do |f| %>
<%= f.fields_for :weight do |builder| %>
<fieldset>
<%= f.label :value, "Current weight" %><br />
<%= f.text_field :value %><br />
</fieldset>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Which renders the form correctly but when I then click on the submit button it simply goes to an error page saying Unknown action- The action 'update' could not be found for UsersController. Im assuming iM doing something wrong as it should try and send to the create action.
Is there anyone out there who could help me back on the right path, Im very much a noob at rails.
Well this has nothing to do with all your models. This pertains to the fact you have not defined an update method in your controller.
When you have done that look into accepts_nested_attributes_for if you want to nest models.
Besides all that, a show page usually shows a read only for of the object. An edit page has the editable form of the object.
You are using the form_for Rails helper and passing #user to it, because #user is a persistent model (saved in the db) then the generated form will have the action to /users/:id with PUT method so the request will be sent to an action named update in your UsersController, it seems that you don't have that action defined in your UsersController
it should be somthing like the following:
def update
#user = Users.find(params[:id])
if #user.update_attributes(params[:user])
# do something if saving succeeds
else
# do something if saving fails
end
end
I believe, after searching for this question having similar issues, that it is not update that is missing but edit.
I know this is an old thread, and you have probably solved the issue, but if not try adding this:
def edit
#user = User.find(params[:id])
end
My question is: why doesn't .becomes pass errors over to the new object? Isn't this the expected behaviour?
I have the following single table inheritance classes in a rails app:
class Document < ActiveRecord::Base
validates :title, :presence => true
end
class LegalDocument < Document
end
class MarketingDocument < Document
end
I want to use the same controller and set of views to edit both LegalDocuments and MarketingDocuments, so I am using DocumentsController < ApplicationController with the following edit and update actions:
def edit
#document = Document.find(params[:id])
end
def update
#document = Document.find(params[:id])
if #document.update_attributes(params[:document])
redirect_to documents_path, :notice => "#{t(:Document)} was successfully updated."
else
render :action => "edit"
end
end
and the following in my edit view:
<%= form_for #document.becomes(Document) do |f| %>
<% if f.object.errors.present? %>
<div class="error_message">
<h4><%= pluralize(f.object.errors.count, 'error') %> occurred</h4>
</div>
<% end %>
<div>
<%= f.label :title %>
<%= f.text_field :title, :class => "inputText" %>
</div>
<%= f.submit %>
<% end %>
If title is filled in, the documents update correctly.
If title is left blank, I am returned to the edit view BUT the error is not shown.
From debugging, I know it's not showing because f.object.errors is nil. However, from debugging, I also know #document.errors is NOT nil, as expected.
My question is: why doesn't .becomes pass errors over to the new object? Isn't this the expected behaviour?
Yes, I noticed that too.
Just change f.object.errors.present? by #document.errors.any? ( or #document.errors.present?).
If you really want to use f.object.errors.present?, write becomes in the controller (both edit and update actions) instead of in the view:
def edit
#document = Document.find(params[:id]).becomes(Document)
end
def update
#document = Document.find(params[:id]).becomes(Document)
# ....
end
And then in the view:
<%= form_for #document do |f| %>
<% if f.object.errors.present? %>
<p>Errrorsss....</p>
<% end %>
#.....
It happens because the url of the form is build according to #document.becomes(Document) (=> PUT document/:id) but #document is created according to its "true" class (a subclass of Document).
If you have pry (highly recommended), write:
def update
#document = Document.find(params[:id])
binding.pry
# ...
end
And then inspect #document. You will see that #document is an instance of LegalDocument or the other subclass even though you called #document.becomes(Document) in your form.
So in final f.object and #document are not the same.
This explains why you can't see f.object.errors when validation fails.
Edit
The 'best way' to deal with STI and form is NOT to use becomes:
<= form_for #document, url: { controller: 'documents', action: 'update' }, as: :document do |f| %>
<% if #document.errors.any? %>
# or if f.object.errors.any?
# handle validation errors
<% end %>
# your form...
<% end %>
This enables you:
to have only one controller (documents_controller)
to have only one resource (resources :documents)
it keeps trace of your subclasses: a LegalDocument will be store as a LegalDocument. No conversion: You don't have to store its class before the conversion to Document and then reassign it later.
Plus, your subclass is available in your form, so you can (let's imagine) build a select for the type.
your controller looks cleaner: #document = Document.find params[:id] nothing more. Just like a classic resource.
If you want to share this form across different actions(typically edit and new):
<%= form_for #document, url: { controller: 'media_files', action: action }, as: :media_file do |f| %>%>
# edit.html.erb
<%= render 'form', action: 'update' %>
# new.html.erb
<%= render 'form', action: 'create' %>
Pretty much it is a bug and it should work as you initially expected. The following patch to address the issue looks like it was pulled back in October
https://github.com/lazyatom/rails/commit/73cb0f98289923c8fa0287bf1cc8857664078d43