This project is archived and is in readonly mode.

#4064 ✓invalid
Andreas Korth

validates_uniqueness_of should honor default_scope (or not)

Reported by Andreas Korth | February 26th, 2010 @ 02:25 PM

A model that has a default_scope with :conditions will not use this scope in uniqueness validations. I consider this the wrong behavior, but maybe I just fail to see the rationale behind it. Since default_scope is so fundamental and applies to all database operations I'd expect it to be honored in uniqueness validations as well.

I'll be happy to provide a patch if there is agreement upon this being the desired behavior.

Comments and changes to this ticket

  • David Jones

    David Jones March 27th, 2010 @ 05:58 AM

    +1

    This is killing me right now!

  • José Valim

    José Valim March 27th, 2010 @ 12:06 PM

    I think it should not use the default scope. You have to explicitly give it to validate_uniqueness_of. If we make it a default, it will be hard to opt-out, so I prefer explicit opt-in.

  • José Valim

    José Valim March 27th, 2010 @ 12:07 PM

    • State changed from “new” to “invalid”
  • Yehuda Katz (wycats)

    Yehuda Katz (wycats) March 27th, 2010 @ 12:14 PM

    Imagine this case:

    class Post < ActiveRecord::Base
      default_scope :public => true
      validates_uniqueness_of :name
    end
    
    Post.create(:name => "wycats", :public => false)
    Post.create(:name => "wycats")
    

    With this proposal, the second post would be created, because the constraint is implicitly on :public => true, and there is no "wycats" with :public => true yet. This seems bad, no?

  • Andreas Korth

    Andreas Korth March 27th, 2010 @ 03:02 PM

    • Assigned user set to “José Valim”

    @José: Don't you think your decision is arbitrary? By the same token, I could argue that the default scope should not be honored in delete operations since opting-in on something destructive is clearly better.

    So my argument still stands (inconsistent bahavior) and I would love to hear a rationale (rather than merely an opinion) on why this behavior should be kept.

    @Yehuda: Thanks for the example.

  • José Valim

    José Valim March 27th, 2010 @ 03:06 PM

    Yehuda's example is exactly why I think it should not be used by default. Most of cases, like public above, it would surprise me. It should be opt-in, and not automatic.

  • José Valim

    José Valim March 27th, 2010 @ 03:34 PM

    Don't get me wrong, I can definitely see some cases where the behavior would be positive, like using it in single table inheritance, as Pratik tells us here:

    http://m.onkey.org/2009/3/24/default-scopes-and-inheritance-to-the-...

    But I can see in other bunch of cases, like public above, where it's really a bad idea. Besides, if we change it, a lot of business logic will break in several applications which relies in the current behavior.

  • Andreas Korth

    Andreas Korth March 29th, 2010 @ 01:05 AM

    "if we change it, a lot of business logic will break in several applications which relies in the current behavior."

    Oh, the Microsoft argument! Don't you think we should keep that attitude away from Rails as long as possible ;-)

    Seriously, every other database operation and validation in Rails does honor the default_scope. Why does validates_uniqueness_of get a different treatment? (purely rhetorical question)

    So we have one contrived example vs. two real world scenarios in which it is not the desired behavior. Your call.

    As a side note: after looking at the code for validates_uniqueness_of I realized that it is broken to begin with and I decided to bake my own uniqueness validation that actually works. As soon as time permits I'm going to pluginize and put it on GitHub and post it here. So Rails can go with the existing behavior and those who want to have it the other way can use the plugin.

    Thanks for your thoughts, guys!

  • José Valim

    José Valim March 29th, 2010 @ 09:10 AM

    I just looked at three applications that I developed in the last six months and all three wouldn't benefit from this change, because they have a use case like :public (one of them is also using :published). So why are they contrived examples? Because they do not support your patch? (don't worry, rhetorical question)

    "Seriously, every other database operation and validation in Rails does honor the default_scope. Why does validates_uniqueness_of get a different treatment?"

    Btw, no other validations uses the default_scope. A patch where you could say :use_default_scope to validates_uniqueness_of would be ok, but I'm still -1 making it the default.

  • Andreas Korth

    Andreas Korth March 29th, 2010 @ 01:04 PM

    Ok, sarcastic tones aside, yes, I still think that I have the stronger argument. The "public" example above would be a good candidate for a named_scope but not default_scope. Consider this:

    Post.all(:conditions => {:public => false})
    

    Will this return all non-public post? It certainly won't since the default scope kicks in adding another condition for public posts and you end up with no posts at all. So default_scope is not really a default scope. It's an ultimate scope that you cannot get around using the public interface of a model. It is as fundamental as set_table_name.

    That's why I was calling Yehuda's example contrived. Using default_scope in this manner would quickly turn out to be impractical in a real application, because at some point you would want to access non-public posts as well. I wonder how you do this with your "unpublished" things in the example you gave.

    As to Pratik's example, let's just say it's a creative use of inheritance but it is only marginally related to the topic we're discussing here.

    Maybe I have a different understanding of default_scope than the rest of the world and that's why I chose to write my own uniqueness validation that is compatible with my interpretation.

  • José Valim

    José Valim March 29th, 2010 @ 01:18 PM

    If you have 10 places where you need public or published tasks and just one place where you need all tasks, I do use default_scope. If I want to get rid of the public scope, I use unscoped on Rails 3 or with_exclusive_scope on Rails 2.3.

    class Task < ActiveRecord::Base
      default_scope where(:public => true)
    end
    
    Task.unscoped.all #=> brings all tasks
    

    And this is actually a good pattern. If I want to show private tasks it requires me to do it explicitly, becoming harder to expose them accidentally.

  • Andreas Korth

    Andreas Korth March 29th, 2010 @ 04:48 PM

    FYI

    Task.with_exlusive_scope.all
    

    doesn't work in 2.3 - it's a protected method.

    If it's actually a good pattern to pull authorization from the controller layer, where it belongs into the model layer remains controversial. My preferred approach to make sure I don't expose anything accidentially is to have an appropriate test in place. YMMV.

    Still not convinced, but let's just leave it at that, ok?

  • José Valim

    José Valim March 29th, 2010 @ 05:45 PM

    My bad! I have the habit to make the method public, we even had a patch to make it public on the Rails repo, have to check if it was applied.

    Better to leave at it, otherwise we would discuss how setting a default scope is pushing all the authorization layer to the model. ;) I still prefer to avoid writing Task.public all over the place.

  • Andreas Korth

    Andreas Korth March 29th, 2010 @ 05:49 PM

    Totally unrelated, but +1 for making with_exlusive_scope public by default :)

  • lichtamberg

    lichtamberg October 21st, 2010 @ 07:39 PM

    • Importance changed from “” to “Low”

    Could you tell me how to tell validates_uniqueness_of to use the default_scope?
    I'm using authlogic and rails3_acts_as_paranoid...
    And I can't get authlogic to work with the case when a user deletes his account and registers new with the same login - validates_uniqueness_of :login validates the uniqueness without ignoring the deleted records - and the default_scope is :active => true...

    Could you help me?

    See also: http://stackoverflow.com/questions/3990644/authlogic-rails3-acts-as...

  • lichtamberg

    lichtamberg October 21st, 2010 @ 07:44 PM

    Beside my question, i like Andreas Korth mentioned behaviour more...

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Pages