I have two tables:
Client(id,name,...)
Purchase(id,item,date,client_id,...)
They have their respective Model, with their validations. What I need is to create a new client with a new purchase, all into the create method of Client controller. Something like this:
def create
#client = Client.new(params[:client])
respond_to do |format|
if #client.save
# Add purchase
#sell = Purchase.new
#sell.client_id = #client.id
#sell.date = params[:date]
# Fill another fields
if #sell.save
# Do another stuff...
else
format.html { render :action => "new" }
format.xml { render :xml => #client.errors, :status => :unprocessable_entity }
end
flash[:notice] = 'You have a new client!'
format.html { redirect_to(:action => :show, :id => #evento.id) }
format.xml { render :xml => #client, :status => :created, :location => #client }
else
format.html { render :action => "new" }
format.xml { render :xml => #evento.client, :status => :unprocessable_entity }
end
end
end
In Purchase's model I have:
belongs_to :client
validates_format_of :date, :with => /^20[0-9]{2}[-][0-9]{2}[-][0-9]{2}$/, :message => 'not valid'
validates_presence_of :date
And there is my problem: how can I validate the date input, through validations into the model, from Client controller? And, how can I rollback the new client created when errors?
Yes, I can do the check as the very first instruction in the method, with a regular expression, but I think it's ugly. I feel like might exist a conventional method for doing this validation or even doing all the stuff in another way (i.e. calling create method for Purchase from Client controller).
Can you put me back in the right way?
Thank you in advance.
Take a look at the following page on working with associations.
Rails provides you with a bunch of handy methods on your objects.
Like the following:
Client.purchases.empty?
Client.purchases.size,
Client.purchases
Client.purchases<<(purchase)
Client.purchases.delete(purchase)
Client.purchases.find(purchases_id)
Client.purchases.find_all(conditions)
Client.purchases.build
Client.purchases.create
When using these methods, you're taking advantage of the validations on each of the models.
Hop into your Rails console and create a new client and try any of the above methods. You'll quickly learn how powerful they are and you'll be on your way in no time.
Edit: Here's a much better guide on Rails associations!
Depends a little on the situation, but you can use validates_associated to run the validations on associated objects. Then you can create the user (but don't save), create the purchase (but don't save) and try to save the user. If you've done it right the user will fail to save with a validation error on the associated object.
Related
I have the following #create method:
def create
begin
#order = #api_user.orders.create!(order_params)
render :json => #order, :only => [:id], :status => :created, :location => #order
rescue
render :json => {}, :status => :unprocessable_entity
end
end
However, I am using a generalistic approach for the rescue. If the order could not be created because one of the passed fields failed the validation, I would like to let the user know about that. So, if the creation of the order raised this:
ActiveRecord::RecordInvalid: Validation failed: Description1 is too long (maximum is 35 characters)
What is the proper way of catching and letting the API user know about it?
One thing you can do is make use of a light API library like rocketpants (https://github.com/Sutto/rocket_pants)
in which case, the method you want could be written like this:
def create
if #order = #api_user.orders.create!(order_params)
expose #order
else
error! :bad_request, :metadata => {:error_description => "#{#order.errors.full_messages}"}
end
end
This is assuming you have set the #api_user instance variable earlier somewhere. Also, the gem uses Active Model Serializers (https://github.com/rails-api/active_model_serializers) to serialize the #order into JSON, so you can always customize the output to your liking by creating a basic serializer, look at the github page for more info :)
Here is another way:
def create
#order = #api_user.orders.build(order_params)
if #order.save
render :json => #order,
:only => [:id], :status => :created, :location => #order
else
render :status => :unprocessable_entity,
:json => {:errors => #order.errors.full_messages}
end
end
You'll get back an array of errors in the JSON
I have a form that sets the attributes of a model, however, there is an attribute that I want to set through the code. That is, I want the user to set some attributes, but I want the program to set other attributes.
Is there any way of doing this?
Example:
If I have table with a "text" column and a "user" column, I want the user to enter the text, but I want the "user" column to be set by the program. How would I accomplish this?
Sure. You could do something like this:
def create
#something = Something.new(params[:something])
#something.programmatically_set_attribute = "Some value" #Here's the part that matters
respond_to do |format|
if #host.save
format.html { redirect_to(#something, :notice => 'Something was successfully created.') }
format.xml { render :xml => #something, :status => :created, :location => #something}
else
format.html { render :action => "new" }
format.xml { render :xml => #something.errors, :status => :unprocessable_entity }
end
end
end
In your form, you would just leave out the field that you don't want edited by human hands. You would also have to change the update function, as well.
If you want to ensure that only the text attribute is updateable by the user and none other you may want to use attr_accessible as follows
class MyModel < ActiveRecord::Base
attr_accesssible :text
end
This will ensure that only the text attribute of MyModel can be updated via mass-assignment.
I'm using Rails 3 for this one. I've got a collections model, a user model and an intermediate subscription model. This way a user can subscribe to multiple collections, with a particular role. However, I don't want a user to be able to subscribe to the same collection twice.
So in my Subscription model I've got something like:
validate :subscription_duplicates
def subscription_duplicates
self.errors.add_to_base "This user is already subscribed" if Subscription.where(:user_id => self.user.id, :collection_id => self.collection.id)
end
However this seems ugly. Also, it breaks when I want to do something like the following in my collection controller:
def create
#collection = Collection.new(params[:collection])
#collection.subscriptions.build(:user => current_user, :role => Subscription::ROLES['owner'])
#collection.save
respond_with(#collection)
end
When I do the build the subscription does not have an id so I get a "Called id for nil" error.
Thanks for any guidance!
use validates_uniqueness_of
validates_uniqueness_of :user_id, :scope => :collection_id
First of all, your create action should always test if the object was saved, and if not then handle that (usually by re-rendering the new/edit page and showing the errors to the user).
A standard sort of create action would look like this (for a #post in this case):
def create
#post = Post.new(params[:post])
#created = #post.save
respond_to do |format|
if #created
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to #post }
format.xml { render :xml => #post, :status => :created, :location => #post }
format.js
else
format.html { render :action => :new } #or edit or wherever you got here from
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
format.js
end
end
end
Shingara's approach to avoiding duplicates should work fine for you.
This is something I've been stuck on for a while now, and I have to apologize in advance for going into so much detail for such a simple problem. I just want to make it clear what I'm trying to do here.
Scenario
So, there's a model Foo, each Foo can either be red, green, or blue. Having URLs like /reds to list all red objects, and /reds/some-red-object to show a certain object. In that "show" view, there should be next/previous links, that would essentially "find the next RedFoo in alphabetical order, and once at the last RedFoo, the next record should be the first GreenFoo, continuing in alphabetical order, and so on".
I've tried implementing this in a couple of ways and mostly ended up at a roadblock somewhere. I did get it working for the most part with single table inheritance though, having something like this:
class Foo < ActiveRecord::Base
class RedFoo < Foo
class GreenFoo < Foo
class BlueFoo < Foo
Each subclass's models and controllers are identical, just replace the model names. So the controllers look something like:
class RedFoosController < ApplicationController
def index
#foos = RedFoo.find(:all, :order => "title ASC")
respond_to do |format|
format.html { render :template => 'foos/index'}
format.xml { render :xml => #foos }
end
end
def show
#foo = RedFoo.find(params[:id])
respond_to do |format|
format.html { render :template => 'foos/show'}
format.xml { render :xml => #foo }
end
end
def new
#foo = RedFoo.new
respond_to do |format|
format.html { render :template => 'foos/new'}
format.xml { render :xml => #foo }
end
end
def edit
#foo = RedFoo.find(params[:id])
respond_to do |format|
format.html { render :template => 'foos/edit'}
end
end
def create
#foo = RedFoo.new(params[:foo])
respond_to do |format|
if #foo.save
flash[:notice] = 'Foo was successfully created.'
format.html { redirect_to(#foo) }
format.xml { render :xml => #foo, :status => :created, :location => #foo }
else
format.html { render :action => "new" }
format.xml { render :xml => #foo.errors, :status => :unprocessable_entity }
end
end
end
def update
#foo = RedFoo.find(params[:id])
respond_to do |format|
if #foo.update_attributes(params[:foo])
flash[:notice] = 'Foo was successfully updated.'
format.html { redirect_to(#foo) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #foo.errors, :status => :unprocessable_entity }
end
end
end
def destroy
#foo = RedFoo.find(params[:id])
#foo.destroy
respond_to do |format|
format.html { redirect_to(foos_url) }
format.xml { head :ok }
end
end
end
The models only contain methods for next/previous, which work fine, surprisingly.
class RedFoo < Foo
def next
if self == RedFoo.find(:all, :order => "title ASC").last
GreenFoo.find(:all, :order => "title ASC").first
else
RedFoo.find(:first, :conditions => ["title > ?", self.title], :order => "title ASC")
end
end
def previous
if self == RedFoo.find(:all, :order => "title ASC").first
BlueFoo.find(:all, :order => "title ASC").last
else
RedFoo.find(:first, :conditions => ["title < ?", self.title], :order => "title DESC")
end
end
end
Problem
For whatever reason when I try to create and edit records, none of the attributes get saved in the database. It simply adds a new record with completely empty columns, regardless of what's filled in the form. No errors get returned in the script/server output or in the log files. From the script/console however, everything works perfectly fine. I can create new records and update their attributes no problem.
It's also quite a bad code smell that I have a lot of code duplication in my controllers/models (they're using the same views as the base model, so that's fine though). But I think that's unavoidable here unless I use some meta-goodness.
Any advice or suggestions about tackling this record saving issue would be great, but the reason I posted my setup in detail is because I have a feeling I'm probably going about this whole thing the wrong way. So, I'm open to other approaches if you know of something more practical than using STI. Thanks.
Update
The parameters hash looks about right:
{"commit"=>"Create", "authenticity_token"=>"+aOA6bBSrZP2B6jsDMnKTU+DIAIkhc8fqoSicVxRJls=", "red_foo"=>{"title"=>"Hello world!"}}
But #foo.inspect returns the following RedFoo object (all nil, except for type):
#<RedFoo id: nil, title: nil, type: "RedFoo", created_at: nil, updated_at: nil>
Problem is the params
:red_foo
is the name of the params in the view, whereas you use
params[:foo]
in the controller, I think the best way would be to be use :foo, in the view by using text_field_tag rather than any (what i assume can be) form builders text_field.
You can get out of the controller smell by using a module to do the basic crud stuff, since i assume most of the new/create/edit/update/destroy stuff is the same
OR
you could map all the routes to a foo controller and use some sort of parameter either passed in from the route, or through URI analysis to get the red/green/blue foo
Please take a look at the section called "Single table inheritance" on this page and let us know if it solves your problem.
Must admit, the way I go about STI is to use set_table_name inside a model.
e.g.
class RedFoo < AR::Base
set_table_name "foos"
include FooModule
extend FooClassModule # for self methods
def next; ...; end
end
But anyway, for this situation, what does your logger say when you do a #foo.inspect just before a save, and also what is the SQL that is ran on insert/update?
Right, so #foo.inspect gives you "nil" in the log?
What I mean (if I wasn't clear enough) was:
def create
#foo = RedFoo.new(params[:foo])
logger.error "******************* foo: #{#foo.inspect} **************"
respond_to do |format|
if #foo.save
...
if you do that and tail -f your log you can easily find out what is happening to foo and compare that to the incoming params hash
Infact, that would also be some useful information to have, what is the params hash?
I have an app that models a House. The House has_many Rooms, Rooms has_many Lights and Small_appliances, etc. I also have a controller called Calculator that is how the app is accessed. Data is added to the house (and its rooms) using the Calculator controller. Then a report is generated, which is located at app/views/calculator/report.html.erb.
My question is where should all the calculations and logic for the report go? Currently I have it all in the view, with some things in calculator_helper. Normally this would go in the model, right? But Calculator doesn't have a model that was generated. What is the standard for this?
Here is the calculator controller.
class CalculatorController < ApplicationController
def index
end
def save_house
#house = House.new(params[:house])
respond_to do |format|
if #house.save
format.html { render :action => 'add_rooms', :id => #house }
format.xml { render :xml => #house, :status => :created, :location => #house }
else
format.html { render :action => 'index' }
format.xml { render :xml => #house.errors, :status => :unprocessable_entity }
end
end
end
def add_rooms
#house = House.find(params[:id])
#rooms = Room.find_by_house_id(#house.id)
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid house #{params[:id]}")
flash[:notice] = "You must create a house before adding rooms"
redirect_to :action => 'index'
end
def add_room
#room = Room.new(params[:room])
#house = #room.house
respond_to do |format|
if #room.save
flash[:notice] = "Room \"#{#room.name}\" was successfully added."
format.html { render :action => 'add_rooms' }
format.xml { render :xml => #room, :status => :created, :location => #room }
else
format.html { render :action => 'add_rooms' }
format.xml { render :xml => #room.errors, :status => :unprocessable_entity }
end
end
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid house #{params[:id]}")
flash[:notice] = "You must create a house before adding a room"
redirect_to :action => 'index'
end
def report
flash[:notice] = nil
#house = House.find(params[:id])
#rooms = Room.find_by_house_id(#house.id)
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid house #{params[:id]}")
flash[:notice] = "You must create a house before generating a report"
redirect_to :action => 'index'
end
end
There are a few ways to approach it, but the logic certainly does not belong in the view. You have the various models associated with one another in a clear hierarchy with the top of the hierarchy being the House model, if I am reading your description correctly. That being the case, I would add an appropriate method of set of methods to the House model that may be composed of calls to calculation methods in the Room models associated with a given House instance and on down the line of association. That ways the relevant calculation can be performed at each level and through composing one or more methods at the House model level you are able to have a clean, expressive and maintainable way to deal with calculations.
One thing to do, as well, would be to make sure that any calculations that can be performed by the DB are. For example, if there is a calculation that a Room model can do by simply querying it's own data then by all means push that computational burden to the DB using the ability of ActiveRecord to invoke such lower level calculation logic. Check out the API docs for the details.
I would look very carefully at the logic you want and see how it can be pushed into the model since that is probably where it belongs, close to the actual data of the calculations, and within the class structures that represent that data specifically; I would not create a model just to handle the calculation logic unless you really need to store the calculations persistently for some reason.
I would create a class in RAILS_ROOT/lib/ called, for example, Calculator and put the code in there.
Classes in /lib/ should be loaded an available anywhere in your app.
You can also create a plain ruby object in /app/models/. There's no reason they all have to inherit from ActiveRecord::Base
Ok, now I can see the code posted. I can see the calculator_controller actually has no calculations in it, are they in the views?. Try this approach:
Write a test that sets up an object which will return the results you need to return to the user of the web page, given a house, rooms or whatever else it needs.
Build a model (in models) to make that test pass.
Modify your controller code above to use your new calculator model
Modify the tests of your controller so they also pass. These tests, of course, do not need to test any business logic.
My prior respose:
If the business logic is fairly simple and only used behind this web app, then you can put it in your app/models folder.
class MyCoolClass
def initialize(clues)
#other_things = OtherThing.all
end
def do_cool_thing; end
def calculate_coolness
#other_things.length
end
end
Then in your controller, create an instance of your model
def index
#mcc = MyCoolClass "A clue as to what I want"
render
end
Then in your templates you can access it
<%=h #mcc.calculate_coolness %>
Note that #other_things is an instance__variable of MyCoolClass and generally not accessible to the templates without accessor methods being defined
This all depends on what kind of data you're creating. What does the calculator controller look like?
You can create your own classes in /lib and use them in your models, which can be a good way to separate out logic from the controller/helpers. Is there a reason why you couldn't put some of the logic in the models?