I've just started programming in rails 3 days ago, learned ruby by the same time, and I'm having a hard time since yesterday figuring how to with one form, delete/update some of my instances, I only use one model "Task". Here's the code:
<%= form_for #task do |f| %>
<ul>
<% #tasks.each do |task| %>
<li id="task"><%= f.check_box :done %> <%= f.label :name %> </li>
<% end %>
<button onclick="doUpdate()">Mark Selected as done </button>
<%= button_to "Delete selected", :method => :delete %>
</ul>
<% end %>
Here's the controller:
def delete
#tasks = Task.find(:all, :conditions => ["task.done = ?", true])
#tasks.each do |task|
task.delete
end
#tasks = Task.all
end
My model have only 2 parameters. name:String and done:Boolean, I wan't to delete all the selected checkboxes. But this don't work for me
Thanks in advance.
The problem is, you are doing it wrong(and I'll tell you why). I could paste the code that would make it work but I'd rather explain, as you are probably doing it to learn.
Task.find(:all, :conditions => ["done = ?", true]) will return EVERYTHING in your database where done = true. You will be erasing everything that is marked as done in the DATABASE, not what were marked on the form. Task is your model, you can access the database by using find, where and other methods from activerecord(if activerecord doesn't sound natural to you, activerecord lets you get stuff from the database without the need of writing SQL queries).
What you really need to do in your controller is:
- You have to get what was sent from the form (check the documentation/web resources for the usage of param[] ).
- For every checkbox marked true, you erase a record. (you got the each part right, this is good!)
I don't think your view is right, I advise you to first be sure that the data that you receive is right(the params[]), then proceed to try to erase the record, or do whatever you want to do with it.
To "test" if your variables and code that is inside your controllers and models, use print #variable or something else(check rails docs how to debug).
I advise you use destroy instead of delete as other fellow stackoverflowers have said. Read the docs of destroy and delete.
Keep going :)
On a first glance, try using task.destroy instead of task.delete, and done instead of task.done See delete vs. destroy.
def destroy
#tasks = Task.find(:all, :conditions => ["done = ?", true])
#tasks.each do |task|
task.destroy
end
#tasks = Task.all
end
Related
I am very novice at Ruby and Ruby on Rails.
I am trying to update a web application that has signed up volunteers that require approval before they can access full website functionality. I added a boolean field to the volunteers database model called :approved that defaults to false when a volunteer signs up, and requires a logged in administrator to update it to true.
I created an administrator class in my project that can view a list of pending volunteers from the controller:
def view_pending_volunteers
#navbar = true
#volunteers = Volunteer.where(approved: false)
end
In the administrator view I want to use checkboxes associated with volunteer, which when submitted will allow the administrator to update the :approved field in the volunteer table.
I have several issues that are not clear to accomplish this task:
In my administrator model I use has_many :volunteers with a migration that put the volunteer_id as a foreign key in the administrator database table. Is that sufficient to accomplish the task at hand, or do I need to create a join table for :approved? I can't have volunteers belong_to :administrators because they would not be able to sign up on their own, they need to be created by administrators.
I am not sure exactly how to configure the code for checkbox helpers in my administrator view. I am using form_with but I am sure my code is not correct. I would like to pass an array of parameters for each check_box associated with the :approved field for the list of pending volunteers back to the controller.
<p><b style="color:blue;font-size:24px;">
<%= form_with(model: #volunteer, local: true) do |f| %>
<% #volunteers.each do |v| %>
<li>
Volunteer: <%= x.first_name%> <%= x.last_name%> Age: <%= x.age%> Description: <%= x.description%>
<%= f.check_box (#volunteers, v.approved, options = {}, checked_value = "1", unchecked_value = "0")%>
</li>
<% end %>
<%= f.submit "Approve" %>
<% end %>
</b></p>
I am not exactly sure how to handle the array of checkbox values that get returned to the administrator controller in order to update the volunteer database table. Do I need to create a hash in the view (how to do that?) and then loop through the hash to update_attribute(:approved, true) for each checked volunteer? I have created an update_pending_volunteers method in the administrator controller for the POST operation, but am unclear on the code that should be there because I am unsure the checkbox approach.
Thanks in advance for your patience with a newbie. This seems like such a simple thing to do but I am not sure of the proper approach. I have spent quite a bit of time reviewing APIs and videos and stack overflow articles but cannot seem to piece together information that will give me confidence in a particular approach to do this correctly. Again it seems like such a simple thing to accomplish but has become a source of frustration.
See the comment from Rockwell Rice. Don't create a relationship of any kind for this functionality.
Use the URL feature of form_with, not model. You're not acting on one volunteer, you're acting on many. Create a post route (ie. approve_volunteers). In the view you would create the checkboxes like this:
<%= form_with(url: approve_volunteers_path, local: true) do |f| %>
<% #volunteers.each do |v| %>
<div class="field">
<label class="checkbox">
Volunteer: <%= x.first_name%> <%= x.last_name%> Age: <%= x.age%> Description: <%= x.description%>
<input type="checkbox" name="volunteers[]" value="<%= v.id %>">
</label>
</div>
<% end %>
<%= f.submit "Approve" %>
<% end %>
That should send params through the form like {"volunteers" => ["1", "3"]} and leave the rest empty. You might have to play around with those a little. Check your console for the params.
Then in your controller something like:
def approve_volunteers
volunteer_ids = []
volunteers = Volunteer.where(approved: false)
volunteers.each do |v|
if v.id.in?(params[:volunteers])
volunteer_ids << v.id
end
end
Volunteer.where('id IN (?)', volunteer_ids).update(approved: true)
end
See above.
The checkbox code provided by Sam worked perfectly. My url for the view is "update_pending_volunteers_path." I need to improve the formatting a little bit.
The code in the controller that worked to loop through the array of volunteer ids that was passed back into the controller is as below:
def view_pending_volunteers
#navbar = true
#volunteers = Volunteer.where(approved: false)
end
def update_pending_volunteers
#navbar = true
params[:volunteers].each do |value|
Volunteer.find(Integer(value)).update(approved: true)
end
redirect_to (administrator_dashboard_path)
end
The volunteers passed into the view have already been parsed to just those that have not been approved, so the volunteer_ids returned as checked are only from that list.
In a scenario with 1->N->N assocations. For example: Post->Comments->Votes (votes will be list of names of people who voted on the comment). To display a page the query with includes might look like:
#post = Post.where(:id => 100).includes({:comments => :votes}).first
I am starting to add caching support. Which means if the comments partial is already cached I will not need to run include the comments/votes all the time. So I wonder if there is a way to make the code appear like:
# controller
#post = Post.find(100)
# view
<% cache('comments', #post.last_comment_time do %>
<% #post.includes({:comments => :votes}).comments.each do |comment| # ???? %>
<% end %>
Running the "post-query" includes, will "fill in" the associations. So #post.comments will be populated and each comment will include all the votes. Is there a way to achieve this?
P.S. I am aware the view is not the best place to run the query, this is just an example.
in latest releases of rails, all the finder-methods return a proxy object, that will only trigger a database-call once you send it some iterator-method like all or first in your case. this is why you can chain all the calls like Post.where.order.sort.bla.
it's not possible though to load the post model and use an includes call later. includes works by using a join call on the relations that get loaded with the model instance, so that you have just one database-call instead of one for each relation.
executing active_record code in your view is also a bad practice. the data-retrieval is the responsibility of the controller, not the view.
This is a fairly old question but this can be done now like this
# controller
#post = Post.find(100)
# view
<% cache('comments', #post.last_comment_time do %>
<% ActiveRecord::Associations::Preloader.new.preload #post, comments: :votes # this will trigger one query %>
<% #post.comments.each do |comment| # this will not trigger any additional queries %>
<% end %>
Not the cleanest way but it does the job
Background:
I'm creating a dashboard as a project and I have a query that I think is going to be a big performance issue:
<% for outlet in #outlets %>
<% if Monitoring.where(:outlet_id => outlet.id).where('date(created_at) = ?', Date.today).exists? %>
<li>
<a class="done" href="<%= outlet_url(outlet) %>" rel="tooltip" title="<%= outlet.name %>"></a>
</li>
<% else %>
<li>
</li>
<% end %>
<% end %>
What I'm trying to achieve is a series of dots on a page. If the anchor tag has a class of done, it will display as green, if not it will be red (done through CSS).
Aside from the obvious DRY issues here, this query is very heavy, so I'm looking at ways to improve it.
An Outlet is Monitored at least once a day (An Outlet has_many :monitorings). For each outlet I need to check if it has been monitored on that particular day, and output the HTML accordingly.
If anyone could help me with this it would be fantastic.
(Also, any advice on caching this would be appreciated).
Cheers in advance :).
You might make a conditioned association for current monitors, then use includes to fetch the associated current monitorings on the original query.
class Outlet
has many :current_monitorings, :class_name => "Monitoring",
:conditions => proc { [ 'monitorings.created_at > ?', Time.now.midnight ] }
end
#outlets = Outlet.includes(:current_monitorings)
#outlets.each do |outlet|
if outlet.current_monitorings.empty?
# the no monitor today case
else
# it's been monitored today
end
end
At the Postgres level, you'll likely benefit from an index on monitorings(outlet_id,created_at) to support the outer join implied by #includes.
BTW, it's bad style to be executing database queries in your view. Put the domain logic in your models, and have your controller execute the query and supply the results to the presentation layer.
Maybe try:
<% #outlets.includes(:monitorings).each do |outlet| %>
<% css_class = outlet.monitorings.any? { |m| m.created_at == Date.today } ? 'done' : '' %>
<li><%= link_to '', outlet_url(outlet), :class => css_class, :rel => "tooltip", :title => outlet.name %></li>
<% end %>
It'll perform 1 big query.
A good way to cache this would be denormalizing your database with the use of callbacks. In your Outlet model, you could add a field called last_monitored_on; anytime a Monitor is saved, update the appropriate Outlet model with the date. Then, you wouldn't have to query Monitors at all.
You could also consider caching that page fragment, and let it expire daily.
In a Rails 3.2 app I have a model Project, which has many Tasks. Each Task has a :status field, which is an integer as follows
1=Normal
2=Urgent
In the Project show view, I want to display a text alert if any of the associated tasks are flagged as urgent.
If the status field was within the Project model, I would do something like this:
<% if Project.status == 2 %>
<div class="alert">URGENT TASKS!</div>
<% end %>
How can I set up a similar if statement, that will cycle through all associated Tasks, and return true if at least one task is marked as urgent?
I'm not sure what terms I should be searching on for this sort of functionality. Or maybe I'm not looking at the problem the right way. I'd be grateful for any pointers in the right direction.
Thanks
This method in Project will do it:
def urgent?
tasks.detect{|t| t.status==2}
end
Then you can do, if you have #project set to the project you're looking at:
<% if #project.urgent? %>
...whatever ...
<% end %>
This next bit was added in answer to your comment. This method in Project will return the highest priority set (lowest number in your example) for any task in a particular project:
def highest_priority
tasks.map{|t| t.status}.min
end
You can then switch between them in your view:
<% case #project.highest_priority
when 1 %>
...priority 1 stuff...
<% when 2 %>
...priority 2 stuff...
<% when 3 %>
...and so on...
<% end %>
I guess that you want to check if a project has some urgent task to be completed. If thats the case I think the best way to achieve that would be to create new method in the Project model, something like this:
def has_urgent_task?
tasks.map(&:status).include?(Task::URGENT)
end
Assuming you have defined your statuses as constants in your Task model, if not just replace Task::URGENT for 2.
So in your view you only need to do this:
<% if #project.has_urgent_task? %>
<div class="alert">URGENT TASKS!</div>
<% end %>
Hey all,(im a beginner in rails)
i've created a controller that look like that:
class HomeController < ApplicationController
def homepage
end
def showmsg
#postword = params[:p]
end
end
the showmsg view looks like that:
<%= #postword %>
and my homepage view looks like that:
<%= form_tag( {:controller => 'home', :action => 'showmsg'}, :method => "post") do %>
<%= text_field_tag(:p,#postword) %>
<%= submit_tag("post") %>
<% end %>
now i have a form that i can write something in it and it will show on the showmsg view.
i created a model with the param :posts with a :description "text" field too.
MY QUESTION is how do i implement the model in the code so any thing i write will be in a list with the things i wrote before, because now (obviously) anything if i write something its deleting the one i wrote before.
thank you all!
I would argue that you're approach is not very rail's like... so if you're learning rails... you're learning it wrong.
Make a Model. Call it "Message":
rails generate model Message content:string
remember to migrate (hopefully you have your databases setup properly):
rake db:migrate
Then in your controller, when you post, you can create message like this:
def create #instead of showmsg... 'create' is the standard name for this
Message.create(params[:message])
#messages = Message.all
end
This will create the message in the database, and then it will get all the messages out of the database and put them into #messages.
You need to edit your form so that it uses form_for. You need to pass it #message, which is an instance of Message.new that your first controller action created. You should call this new
In your create.erb.html file, you show all the messages like this:
<ul>
<% #messages.each do |message| %>
<li><%= message.content %></li>
<% end %>
</ul>
I actually wouldn't recommend showing all the messages in the create action - it should really happen in the index action and you should redirect... but we need to keep this simple. Just google this or watch some of Ryan's screencasts and you'll get it.
And you're done. This is the "Rails Way" to do things. It's best to learn it the way they want you to learn it.
I would also commend that you format your code properly by indenting, and start naming your methods to be real english. For example, showmsg is bad and show_message is a lot better.
If all of this is totally confusing, then just create a new project, and then type:
rails generate scaffold message content:string
It will basically build the application you want and a lot more. You can just read the code and see how they did it.
Hope it helps.
Your approach is not really rails like so some tweaks and fixes are needed. Suggestions: check rails approach to REST. The following code will work it is a little more rails like, but still not all the way there.
Generate a model
rails generate model Message postword:string
this will generate the model and create the migration necessary to create the table in the database.
Create the table
rake db:migrate
Define a post action
It will save the postword in the database. In your controller:
def create
#message = Message.create!(params[:message])
if #message.save
redirect_to "/home/showmsg"
else
render :action => "/home/homepage"
end
end
Create and instance of Message to use in your form
def homepage
#message = Message.new
end
Fix your form tag
<%= form_for #message, :url => "/home/create" do |f| %>
<%= f.label :postword %>
<%= f.text_field :postword %>
<%= f.submit "Create" %>
<% end %>
Now let's show the words in the showmsg page
In the controller select the postwords from the database:
def showmsg
#postwords = Message.all
end
Showing them: /showmsg.html.erb
<H1>postwords list</H1>
<ul>
<% #postwords.each do |p| %>
<li><%= p.postword %></li>
<% end %>
</ul>
Your routes.rb file will have this routes:
get "home/homepage"
get "home/showmsg"
post "home/create"
Define an attribute :new_text in a way similar to this:
class TheModel
# Virtual writer - everything assigned to this attribute
# will be added to self.text
#
def new_text=(v)
self.text += v.to_s
end
def new_text
"" # This is write-only attribute
end
end
Now, use the field 'new_text' in your form.
Of course, this is a very simple example. You should decide whether you want to add the content on every call to :new_text=, maybe some validation would help, the read accessor may need some care, and so on.
For some good guides which may help you start, see the site http://guides.rubyonrails.org/