I have designed a very simple web application which associates authors, books and ratings.
In each of the respective models
Author
has_many :books
Book
belongs_to :author
has_many :reviews
Review
belongs_to :book
Model attributes
Author : title, fname, lname, DOB
Book : ISBN, title, publish_date, pages
Review : rating(1-5), description
I am wondering if I completely validate all of these attributes to my liking in the models, 1 attribute for example
validates :ISBN, :only_integer => true, length: { is: 13 }
do I need to worry about validations for data elsewhere?
I know that validations for the model run on the server side so there may need to be some validation on the client side (in JS). I am trying to ensure that there are no flaws when it comes to asserting data correctness.
As is so often the case: it depends.
In a simple Rails application, all models will be updated through a view request to a controller which in turn fills in the params into the models, then tries to save the model and any validation errors that occur are rendered back to the server.
In that scenario, all your code will have to do is to react to failed calls to #save but you can sleep soundly knowing that everything in your database is how it is supposed to be.
In more complex applications, putting all the validation logic into your model might not work as well anymore: every call to #save will have to run through all the validation logic, slowing things down, and different parts of your application might have different requirements to the input parameters.
In that scenario there are many ways to go about it. Form objects with validations specific to the forms they represent are a very common solution. These form models then distribute their input among one or more underlying ActiveRecord models.
But the Rails way is to take these one step at a time and avoid premature optimization. For the foreseeable future, putting your validation into your model will be enough to guarantee consistency.
do I need to worry about validations for data elsewhere?
Yes you do.
Application level validations are still prone to race conditions.
For things that should be unique like for example ISBN numbers database constraints are vital if uniqueness is to be guarenteed. Other areas where this can cause issues are when you have a limit on the count of an association.
While validations prevent most errors they are not a replacement for database constraints to ensure the correctness of data. Both are needed if correctness is important.
Related
This question already has answers here:
Ruby on Rails: Is it better to validate in the model or the database?
(6 answers)
Closed 7 years ago.
By SQL level validations, I meant the validations that we put in migrations
like this:
add_column :leads, :count, null: false
By model-level validations, I meant the validations we put in models like this:
validates_presence_of :count
How are these two different? How is having both helpful? Is it enough that I only put model-level validations?
SQL validations are performed on database level and not having them implemented in the model might lead into some exceptions raised from db layer (basically from mysql/pg/put-your-adapter gem).
Model level validations are performed before the query is run against database.
The best option is to have both. Even if something goes wrong with your model validation - it will be catched on database level.
Good example here is unique constraint. If you have a huge load on the database you can encounter race condition problem and model validation is not enough here. Thanks to having unique constraint you can be sure that your data integrity is protected.
Model level validation helps you by providing user error messages when there is a validation error. and other helpers like valid? methods to check the object is ready to insert.
Mean while db level validations make sure your database sanity at DB layer.
That said you can always have more complex business logic based validations in model level may be by involving and transforming data in few other tables.
With ActiveRecord models, I know you can validate the length of an input field like so
class User
validates :user_name, length: { maximum: 20 }
end
However, one of the design patterns in Rails recommends thin models. If you have a ton of validations, the above code might seem intimidating. I read there was another way you could do this.
You can simply use an ActiveRecord::Schema to accomplish the same task.
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :user_name, limit: 20
end
end
end
That accomplishes the exact same thing only you don't even need the second line in your Users model.
What is the standard Rails convention regarding this?
Some people would argue that you have to have skinny controllers and skinny models. However, this can create several additional classes in your application.
Sometimes having a fat model if documented and laid out logically can be easier to read. I will ignore the 'best practices' if it makes the code easier to read as I may not always be the only person touching that code. If the application scales to a point where multiple people will be accessing the same files, I will consider extracting it at that point as a refactor. However, this has rarely been the case.
While it is good to set limits on your database, you also want to have client validations to prevent someone having their data truncated with no feedback to them. For example, (a really horrible example), if you were to limit the username of an User to only six characters and I type in kobaltz as my username, I will wonder why my username/password never works as the database truncated it to kobalt. You will also run into issues where MySQL (or similar) will throw database level errors which is annoying to fix/troubleshoot. You also have to consider if modifying a database in production, if you set the limits where they did not exist before, you could end up corrupting your data.
Having a few validations in your model does not make it a 'fat' model in my opinion. It makes it easier to read. If you're not using an IDE like RubyMine and only using an editor, you do not have the luxury of Jump to Definition which can make the abstraction of your model easier to follow.
If you use second approach, you won't be able to get the error. Its on mysql level and not on model level, so active record won't tell you the reason for user not getting created or updated.
object.errors
will be empty.
Check this
http://apidock.com/rails/ActiveModel/Errors/full_messages
Several times I've been in a situation where we have a model with a business-driven validation such as:
class Order < ActiveRecord::Base
validates_numericality_of :total, :greater_than => 5.0
# Some more logic
end
At some point, the boss man decides that the new minimum order should be $10, so we update the validation to 10. However, this means that any existing orders with values between $5 and $10 will no longer validate, and any logic where I call order.save() will begin to fail (sometimes unpleasantly silently). I've run into this many times in a largish shipping Rails app, and haven't found a good solution yet. Some ideas:
Ensure that there are no pending "orders" that will be affected when the code change is rolled out
Add an :if => Proc.new { |o| o.created_at.nil? or o.created_at > date_new_validation_is_effective } to the new validation, but certain this quickly becomes unwieldy
"Validate" business rules somewhere else, such as the controllers where user-specified input is accepted, rather than as model validations. But this violates the Fat Model/Skinny Controller principle that has many proponents in Rails.
Is there a different approach for integrating this logic, or keeping a strategy like #2 manageable in the long run?
You could set this business logic validation to only run :on => :create. I'm assuming that you don't often edit/update the total of an order.
That would put it into effect for all orders going forward, while not affecting the validity of existing models in the system.
You could add a version to the order record and version specific validations.
The data source I am working with is terrible. Some places where you would expect integers, you get "Three". In the phone number field, you may get "the phone # is xxx". Some fields are simply blank.
This is OK, as I'm parsing each field so "Three" will end up in my model as integer 3, phone numbers (and such) will be extracted via regex. Users of the service KNOW that the data is sketchy and incomplete, as it's an unfortunate fact of the way our data source is maintained and there's nothing we can do about it but step up our parsing game! As an aside, we are producing our own version of the data slowly as we parse more and more of the original data, but this poor source has to do for now.
So users select the data they wish to parse, and we do what we can, returning a partial/incorrect model. Now the final model that we want to store should be validated - there are certain fields that can't be null, certain strings must adhere to a format and so on.
The flow of the app is:
User tells the service which data to
parse.
Service goes off and grabs
the data, parses what it can and
returns a partial model with
whatever data it could retrieve.
We display the data to the user,
allowing them to make corrections
and to fill in any mandatory fields
for which no data was collected.
This user-corrected data is to be
saved, and therefore validated.
If validation fails, show data again
for user to make fixes, rinse &
repeat.
What is the best way to go about having a model which starts off being potentially completely invalid or containing no data, but which needs to be validated eventually? The two ways I've thought of (and partially implemented) are:
2 models - a Data model, which has validations etc, and an UnconfirmedData model, which has no validations. The original data is put into an UnconfirmedData model until the user has made their corrections, at which point it it put into a Data model and validation is attempted.
One model, with a "confirmed data" flag, with validation being performed manually rather than Rails' validation.
In practice I lean towards using 2 models, but I'm pretty new to Rails so I thought there me be a nicer way to do this, Rails has a habit of surprising me like that :)
Must you save your data in between requests? If so, I would use your two model format, but use Single Table Inheritance (STI) to keep things dry.
The first model, the one responsible for the parsing and the rendering and the doing-the-best-it-can, shouldn't have any validations or restrictions on saving it. It should however have the type column in the migration so you can use the inheritance goodness. If you don't know what I'm talking about, read up on the wealth of information on STI, a good place to start would be a definitive guide.
The second model would be the one you would use in the rest of the application, the strict model, the one which has all the validations. Every time a user submitted reworked and potentially valid data, your app would try and move your instance of the open model created from the params, to an instance of the second model, and see if it was valid. If it was, save it to the database, and the type attribute will change, and everything will be wonderful. If it isn't valid, save the first instance, and return the second instance to the user so the validation error messages can be used.
class ArticleData < ActiveRecord::Base
def parse_from_url(url)
# parses some stuff from the data source
end
end
class Article < ArticleData
validates_presence_of :title, :body
validates_length_of :title, :greater_than => 20
# ...
end
You'll need a pretty intense controller action to facilitate the above process, but it shouldn't be too difficult. In the rest of your application, make sure you run your queries on the Article model to only get back valid ones.
Hope this helps!
Using one model should be straightforward enough. You'll need an attribute/method to determine whether the validations should be performed. You can pass :if => to bypass/enable them:
validates_presence_of :title, :if => :should_validate
should_validate can be a simple boolean attribute that returns false when the model instance is "provisional", or a more complicated method if necessary.
I'm looking for guidelines on when to know when a RESTful approach to a model and it's associations is proper, and when it's not. Perhaps it's the nature of my current application, but I'm finding that simple models with no associations work well with REST, but complex models with many has_many assocations really seem to complicate the view and the setup required in the controller. form_for calls start to become especially complicated.
Or perhaps it's my neophyte understanding. I've been doing Rails for over three years now, but REST and form helpers together seem to mystify me.
Make a resource of each top-level model in your system. By top-level, I mean models that are independent and have meaning outside of the associated model. Generally, that's most models. In the following example Position and Candidate are top-level. You could consider Candidate to be composed of PastEmployment and positions to which she has applied. Applications to positions and prior work history can be accessed through the Candidate resource, since they don't exist on their own.
Models
class Position
has_many :candidate_positions
has_many :candidates, :through => :candidate_positions
end
class Candidate
has_many :candidate_positions
has_many :positions, :through => :candidate_positions
has_many :past_employments
accepts_nested_attributes_for :past_employments
accepts_nested_attributes_for :candidate_positions
end
class PastEmployment
belongs_to :candidate
end
class CandidatePosition
belongs_to :candidate
belongs_to :position
end
Routes
map.resources :positions
map.resources :candidates
Use a non-resourceful controller for interactions with the user that span models. For example, if you wanted to have a HomeController that shows available positions as well as recent candidates, that would be a new, plain controller. If you want to edit any of the information on this controller, cool! You already have controllers available to handle the form posts, that will automatically be wired with <% form_for #candidate %>. You can render your collection of positions with <%= render #positions %>, and because you've made them a resource, Rails will know to look in views/positions/_position.html.erb for the appropriate partial.
The end result should be that you're never writing logic to handle the persistence of an object in more than one place. That's when controllers get complex and forms get out of hand. It also means that Rails and external systems know where to retrieve and store objects. Same URL, same controller, just a different format.
You might check out this series of Railscasts, which talks about has-many relationships and forms in the context of REST:
Complex Forms Part 1
Complex Forms Part 2
Complex Forms Part 3
I don't know RoR, so I will make generate statements on REST.
REST and ROI treats the URLs as series of nouns, and it uses HTTP methods like GET, POST, PUT, and DELETE as the verbs. This model works for CRUD, and even for models with multiple associations. Since URLs represent resources, associated objects can be expressed as list of URLs.
However if your system requires more fine-grain verbs like validate, move, copy, honk, read, write etc. it may not suit REST.
disclaimer: I know rails, but I still am pretty much a newbie.
Short Answer: REST and form helpers are completely different areas.
Long answer:
As I understand it,Representational State Transfer is only loosely related to the actual rendering of forms and views.
REST really has to do with controllers, and to a certain extend models. The idea is that instead of trying to think about an entire conversation with a client, you write a webapp to respond in specific, predictable ways to individual client messages.
i.e., if a client GETs a model, you just retrieve it, format it for them, send it to them, and forget about it.
if a client POSTS an update of some sort, you change the webapps state to reflect that, send back any response, and then forget about it. Any future GET or POST will look at the new state, but not the message that created it.
So, really, whether or not an application is RESTful depends not really on how complicated the model is, but on how users interact with it. An app meant to be at least somewhat client-agnostic, that is data-centric, is a good candidate for REST. Something that relies heavily on sessions, and interacting with a specific user, and is process-centric, might not be such a good candidate.
On the other hand, you have Rails form helpers. These are great for scaffolding, but sometimes can be frustrating when you try to use them in more complicated ways.
So, what is your main question? Do you have a specific question about rails form helpers? about rails controllers? or something specific to REST?