Creating new registration and new order at the same time - ruby-on-rails

I'm trying to add an event registration to a current or new order. Question at the end of the post.
Event model: Contains the basic event information like title, date, description. This event model has many event options.
Event Option: Contains a description and a price. This event option has many registrations.
Registration: Allows the user to register and it takes the price from the event option price. This registration belong to an event option and to the order model.
Order: Calculates the total of the order based on the sum of all the registrations associated with it.
Creating a new registration:
In the event option show page, I have a form that creates a new registration using remote: true.
Here's the form:
<%= form_for(#registration, remote: true) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :lastname %><br>
<%= f.text_field :lastname %>
</div>
<div class="field">
<%= f.label :event_option_id %><br>
<%= f.text_field :event_option_id, value: #event_option.id %>
</div>
<div class="field">
<%= f.label :order_item_id %><br>
<%= f.text_field :order_item_id%>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price, value: #event_option.price%>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
When the form is submitted, it creates the event and also creates a new order if the order does not exist. To create the new order I have this helper method in the application controller:
def current_order
if !session[:order_id].nil?
Order.find(session[:order_id])
else
Order.new
end
end
Here's the create method in the registrations controller:
def create
#order = current_order
#registration = Registration.new(registration_params)
#order_id = current_order.id
respond_to do |format|
if #registration.save
format.html { redirect_to #registration, notice: 'Registration was successfully created.' }
format.json { render :show, status: :created, location: #registration }
format.js {}
#order.save
session[:order_id] = #order.id
else
format.html { render :new }
format.json { render json: #registration.errors, status: :unprocessable_entity }
end
end
end
The problem is that I'm not able add the registration to the order. I'm guessing that this is happening because the registration is created before the order. The last two line of the if #registration.save in the respond_to block are saving the order. How can I add the registration to the order? Can both, the new registration and new order be created at the same time?

A simple way to get around this is to assign the registration to the order before saving the order...
#order.registrations << #registration
#order.save
Alternatively you could create the association at the time you're creating the #registration record.
Instead of...
#registration = Registration.new(registration_params)
do....
#registration = #order.registrations.build(registration_params)

Related

First or create method , creating duplicates

Hi i'm trying to use the find or create method to update a skill if it already exists or create it if it doesn't. I can create a new skill fine but when i try update a skill that already exists it does update the skill but also creates a duplicate skill with the same data.
def create
#project = Project.find params[:project_id]
#skills_required = #project.skills_requireds.new(skills_required_params)
skills = SkillsRequired.where(skills_required_params.slice(:skill_id)).first_or_create
skills.update(skills_required_params)
respond_to do |format|
if #skills_required.save
format.html{ redirect_to #project, notice: "Skills required added for #{#project.projectName}" }
else
format.html{ redirect_to #project, notice: "Something went wrong, unable to update required skills " }
end
end
end
Form:
<div class="section">
<div class="top-border left"></div>
<div class="top-border right"></div>
<h3> Skill Required</h3>
<%= form_for([#project, SkillsRequired.new]) do |f| %>
<div class="field">
<%= f.label :skill_id %><br>
<%= f.collection_select :skill_id, Skill.all, :id, :skillType, :prompt => "Select Skill" %>
</div>
<div class="field">
<%= f.label :numWorkers %><br>
<%= f.number_field :numWorkers %>
</div>
<div class="field">
<%= f.label :skillLevel %><br>
<%= f.text_field :skillLevel %>
</div>
<%=f.submit "Add Skill" %>
<%end%>
</div>
I've tried adding a skills_required destroy to my controller but this doesn't allow me to add a new skill. Any help would be appreciated
You're approaching the problem wrong. You need a model level validation which enforces what skill requirements are valid:
# Your model names should be nouns - not adjectives
class SkillRequirement < ApplicationRecord
validates_uniqueness_of :skill_id, scope: :project_id'
end
If you want users to be able to specify the same skill but at different levels you can do it like so:
# Your model names should be nouns - not adjectives
class SkillRequirement < ApplicationRecord
validates_uniqueness_of :skill_id, scope: [:project_id, :level]
end
You should also combine this with a database index to avoid race conditions.
add_index(:skill_requirements, [:project_id, :skill_id], unique: true, name: 'by_skill_and_project')
Since updating existing skill requirements should be handled by a seperate update action you don't need all that bloat in your controller:
class SkillRequirementsController
before_action :set_project
def create
#skill_requirement = #project.skill_requirements.new(skill_requirement_params)
if #skill_requirement.save
redirect_to #project
else
render :new
end
end
def update
#skill_requirement = #project.skill_requirements.find(params[:id])
if #skill_requirement.update(skill_requirement_params)
redirect_to #project
else
render :edit
end
end
private
def set_project
#project = Project.find(params[:project_id])
end
def skill_requirement_params
params.require(:skill_requirement).permit(:)
end
end

.new is not assigning an id to the new object

My ReportNu model is tied to an existing table whose unique id column is "oid". When I try to create a new report, I get the following error:
ERROR: null value in column "oid" violates not-null constraint DETAIL: Failing row contains (null, 2, 2, 106341051, 2016, 0, 0, null, null).
I feel like there should be a way to tell rails to automatically set the oid for me, incrementing from 1 on up. How do I do that? I loaded up the rails console and ReportNu.new just creates an instance with an oid of nil.
Here are the relevant portions of my files.
report_nu.rb:
class ReportNu < ActiveRecord::Base
self.table_name = "t_report_cust"
self.primary_key = "oid"
...
end
new.html.erb
<%= form_for(#report_nu) do |f| %>
<%= f.hidden_field :cust_id, :value => current_user.id %><br>
<div class="field">
<%= f.label :spec_id %><br>
<%= f.text_field :spec_id %>
</div>
<div class="field">
<%= f.label :hid %><br>
<%= f.text_field :hid %>
</div>
<div class="field">
<%= f.label :fore_yr %><br>
<%= f.text_field :fore_yr %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
reports_controller.rb
def new
#report_nu = ReportNu.new
end
def create
#report_nu = ReportNu.new(report_nu_params)
respond_to do |format|
if #report_nu.save
format.html { redirect_to #report_nu, notice: 'Report nu was successfully created.' }
format.json { render :show, status: :created, location: #report_nu }
else
format.html { render :new }
format.json { render json: #report_nu.errors, status: :unprocessable_entity }
end
end
end
#Nithin answered this for me in his comment above. The table I was working with did not have it's primary key set to auto-increment. I didn't create the table and didn't even know to look for that. Thanks Nithin! If you post your comment as an answer I'll check it and delete my own "answer"

Make and use temporary attribute for a form

Ruby on Rails 4.1
The form has an option to select the table column name. I want to input text into the table column selected by the form. To do this I am trying to make temporary attributes that the form can use to store the value and examine in the create method. Then assign the text to the correct column, then save.
Controller:
def new
#word = Word.new
#language = Word.new(params[:language])
#translation = Word.new(params[:translation])
#language_options = Word.column_names
end
def create
#word = Word.new(word_params)
if #language == "arabic"
#word.arabic == #translation
end
respond_to do |format|
if #word.save
format.html { redirect_to #word, notice: 'Word was successfully created.' }
format.json { render :show, status: :created, location: #word }
else
format.html { render :new }
format.json { render json: #word.errors, status: :unprocessable_entity }
end
end
end
The form:
<%= simple_form_for(#word) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :name, placeholder: 'English String' %>
<%= f.input :language, collection: #language_options %>
<%= f.input :translation, placeholder: 'Translated String' %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
This is the error I get:
undefined method `language' for #<Word:0x007f6116b1bcb8>
Which is because there is not a language attribute for the form to use. So I was trying to make a temporary one in the controller new().
Is there a way to do this or do I have to make :language and :translation in a database table to reference in the form?
Virtual Attribute
You may benefit from using an attr_accessor in your model
This creates a virtual attribute which works the same as the "real" attributes in your model:
#app/models/word.rb
Class Word < ActiveRecord::Base
attr_accessor :column_name
end
This will allow you to assign values to this attribute which won't be saved into the db, which sounds like what you want:
#app/views/words/new.html.erb
<%= simple_form_for(#word) do |f| %>
<%= f.input :column_name do %>
<%= f.select :column_name, #language_options %>
<% end %>
<% end %>
When you submit this form, it will then give you the column_name attribute to edit:
#app/controllers/words_controller.rb
Class WordsController < ApplicationController
def create
# ... you'll have "column_name" attribute available
end
end

rails 4.0 undefined method

I have an Opportunity model that has Activity as a nested resource. on my opportunities/show page, I have a list of activities for that opportunity and a form to add new activities. When I click "add activity" I get:
undefined method `activities' for nil:NilClass
Here is the error source:
# POST /activities.json
def create
#activity = #opportunity.activities.new(activity_params)
if #activity.save
redirect_to #opportunity, notice: 'Activity has been added'
else
I defined my Opportunity model as having many Activities and that my Activities belongs to an Opportunity. Here are the relevant parts of my Activity controller:
def create
#activity = #opportunity.activities.new(activity_params)
if #activity.save
redirect_to #opportunity, notice: 'Activity has been added'
else
redirect_to #opportunity, alert: 'Unable to add Activty'
end
end
And here is my views/activities/new code
<%= form_for ([#opportunity, #opportunity.activities.new]) do |f| %>
<div class="field">
<%= f.label "Date Assigned" %> <br />
<%= f.text_field :date_assigned %>
</div>
<div class="field">
<%= f.label "Date Due" %> <br />
<%= f.text_field :date_due %>
</div>
<div class="field">
<%= f.label "Description" %> <br />
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.label "Status" %> <br />
<%= f.text_field :status %>
</div>
<div class="actions">
<%= f.submit 'Add' %>
</div>
<% end %>
My routes:
resources :opportunities do
resources :activities
end
thank you!!
Your #opportunity is undefined(nil) in the block.
You must get #opportunity prior to building activities on it as :
#opportunity = Opportunity.find(params[:opportunity_id])
(Reason for :opportunity_id : Since this is ActivityController and your model is nested, by conventional nested RESTful resources (as specified in your routes), the parameter is automatically assigned as model_id => opportunity_id)
Changed code:
def create
#opportunity = Opportunity.find(params[:opportunity_id])
#activity = #opportunity.activities.new(activity_params)
if #activity.save
redirect_to #opportunity, notice: 'Activity has been added'
else
Also, it is recommend to use build instead of new while building object for relations.
Try using build instead of new.
#activities = #oportunities.activities.build(activity_params)
That should work
Edit:
You didn't find for #oportunities before the build :P
def create
#oportunities.find(params[:id])
#activity = #opportunity.activities.new(activity_params)
if #activity.save
redirect_to #opportunity, notice: 'Activity has been added'
else
redirect_to #opportunity, alert: 'Unable to add Activty'
end
end

Rails: Adding to model after user submits form

In my Rails 3.2 project, I have a form to create a new post in new.html.erb in app/views/posts/
<%= form_for(#post) do |post_form| %>
...
<div class="field">
<%= post_form.label :title %><br />
<%= post_form.text_field :title %>
</div>
<div class="field">
<%= post_form.label :content %><br />
<%= post_form.text_field :content %>
</div>
<div class="actions">
<%= post_form.submit %>
</div>
<% end %>
Then the create function in posts_controller.rb
def create
#post = Post.new(params[:post])
if #post.save
format.html { redirect_to #post }
else
format.html { render action: "new" }
end
end
When the user submits a post, the title and content of the post are added to the Post model. However, I also want to add to another field of that post. For the field random_hash (which the user doesn't get to specify), I want to make it a string of 8 lowercase letters, the first 2 of which are the first 2 letters of the title, and the last 6 are random lowercase letters. How can I do that?
def create
#post = Post.new(params[:post])
#post.random_hash = generate_random_hash(params[:post][:title])
if #post.save
format.html { redirect_to #post }
else
format.html { render action: "new" }
end
end
def generate_random_hash(title)
first_two_letters = title[0..1]
next_six_letters = (0...6).map{65.+(rand(25)).chr}.join
(first_two_letters + next_six_letters).downcase
end
Put that in your controller. You obviously have to have random_hash attribute for Post model to work.
I am using Kent Fredric's solution to generate six random letters.

Resources