Rails order a has_many relation using a through table












0















I have a User and a Campaign model in my rails app. A campaign has_many users and a user has_one campaign.



I want to order the users in the campaign by the date that they were added to it.



To do that, I created a through table called CampaignUser. I thought that I'd be able to order by the created_at column in that table, but I couldn't see an easy way to do it. See the classes below:



class Campaign < ApplicationRecord
has_many :campaign_users
has_many :users, through: :campaign_users
end

class User < ApplicationRecord
has_one :campaign, through: :campaign_users, dependent: :destroy
end

class CampaignUser < ApplicationRecord
belongs_to :campaign
belongs_to :user
end


Ideally, I'd like to write a line like this in my Campaign class:



has_many :users, through: campaign_users, -> { order(created_at: :desc) }


Where created_at refers to campaign_users and not to users. Is there a way to do that?



I could just write a method on Campaign myself to order the users manually, but then I'd have to make sure I call that method everywhere instead. It seems like there should be an easier way.



Edit:



Adding a scope to the user, as suggested in other answers is more problematic in this case. I'm looking to order users by a property of the through table, not a property of the user itself. Is there a way to write the following line, replacing email with campaign_users.created_at, or something similar?



has_many :users, -> { order(email: :desc) }, :through => :campaign_users 









share|improve this question

























  • Possible duplicate of How do I order a has_many through association in Ruby on Rails?

    – mrzasa
    Nov 15 '18 at 13:11











  • @mrzasa This case is slightly different as I'm trying to order based on the through table and not the user itself. See my edit for details.

    – AdColvin
    Nov 15 '18 at 14:29











  • I'd advise to remove through and implement it as two separate has_many :campaign_users associations in both classes

    – mrzasa
    Nov 15 '18 at 14:31
















0















I have a User and a Campaign model in my rails app. A campaign has_many users and a user has_one campaign.



I want to order the users in the campaign by the date that they were added to it.



To do that, I created a through table called CampaignUser. I thought that I'd be able to order by the created_at column in that table, but I couldn't see an easy way to do it. See the classes below:



class Campaign < ApplicationRecord
has_many :campaign_users
has_many :users, through: :campaign_users
end

class User < ApplicationRecord
has_one :campaign, through: :campaign_users, dependent: :destroy
end

class CampaignUser < ApplicationRecord
belongs_to :campaign
belongs_to :user
end


Ideally, I'd like to write a line like this in my Campaign class:



has_many :users, through: campaign_users, -> { order(created_at: :desc) }


Where created_at refers to campaign_users and not to users. Is there a way to do that?



I could just write a method on Campaign myself to order the users manually, but then I'd have to make sure I call that method everywhere instead. It seems like there should be an easier way.



Edit:



Adding a scope to the user, as suggested in other answers is more problematic in this case. I'm looking to order users by a property of the through table, not a property of the user itself. Is there a way to write the following line, replacing email with campaign_users.created_at, or something similar?



has_many :users, -> { order(email: :desc) }, :through => :campaign_users 









share|improve this question

























  • Possible duplicate of How do I order a has_many through association in Ruby on Rails?

    – mrzasa
    Nov 15 '18 at 13:11











  • @mrzasa This case is slightly different as I'm trying to order based on the through table and not the user itself. See my edit for details.

    – AdColvin
    Nov 15 '18 at 14:29











  • I'd advise to remove through and implement it as two separate has_many :campaign_users associations in both classes

    – mrzasa
    Nov 15 '18 at 14:31














0












0








0








I have a User and a Campaign model in my rails app. A campaign has_many users and a user has_one campaign.



I want to order the users in the campaign by the date that they were added to it.



To do that, I created a through table called CampaignUser. I thought that I'd be able to order by the created_at column in that table, but I couldn't see an easy way to do it. See the classes below:



class Campaign < ApplicationRecord
has_many :campaign_users
has_many :users, through: :campaign_users
end

class User < ApplicationRecord
has_one :campaign, through: :campaign_users, dependent: :destroy
end

class CampaignUser < ApplicationRecord
belongs_to :campaign
belongs_to :user
end


Ideally, I'd like to write a line like this in my Campaign class:



has_many :users, through: campaign_users, -> { order(created_at: :desc) }


Where created_at refers to campaign_users and not to users. Is there a way to do that?



I could just write a method on Campaign myself to order the users manually, but then I'd have to make sure I call that method everywhere instead. It seems like there should be an easier way.



Edit:



Adding a scope to the user, as suggested in other answers is more problematic in this case. I'm looking to order users by a property of the through table, not a property of the user itself. Is there a way to write the following line, replacing email with campaign_users.created_at, or something similar?



has_many :users, -> { order(email: :desc) }, :through => :campaign_users 









share|improve this question
















I have a User and a Campaign model in my rails app. A campaign has_many users and a user has_one campaign.



I want to order the users in the campaign by the date that they were added to it.



To do that, I created a through table called CampaignUser. I thought that I'd be able to order by the created_at column in that table, but I couldn't see an easy way to do it. See the classes below:



class Campaign < ApplicationRecord
has_many :campaign_users
has_many :users, through: :campaign_users
end

class User < ApplicationRecord
has_one :campaign, through: :campaign_users, dependent: :destroy
end

class CampaignUser < ApplicationRecord
belongs_to :campaign
belongs_to :user
end


Ideally, I'd like to write a line like this in my Campaign class:



has_many :users, through: campaign_users, -> { order(created_at: :desc) }


Where created_at refers to campaign_users and not to users. Is there a way to do that?



I could just write a method on Campaign myself to order the users manually, but then I'd have to make sure I call that method everywhere instead. It seems like there should be an easier way.



Edit:



Adding a scope to the user, as suggested in other answers is more problematic in this case. I'm looking to order users by a property of the through table, not a property of the user itself. Is there a way to write the following line, replacing email with campaign_users.created_at, or something similar?



has_many :users, -> { order(email: :desc) }, :through => :campaign_users 






ruby-on-rails activerecord has-many-through






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 15 '18 at 14:27







AdColvin

















asked Nov 15 '18 at 11:50









AdColvinAdColvin

3111212




3111212













  • Possible duplicate of How do I order a has_many through association in Ruby on Rails?

    – mrzasa
    Nov 15 '18 at 13:11











  • @mrzasa This case is slightly different as I'm trying to order based on the through table and not the user itself. See my edit for details.

    – AdColvin
    Nov 15 '18 at 14:29











  • I'd advise to remove through and implement it as two separate has_many :campaign_users associations in both classes

    – mrzasa
    Nov 15 '18 at 14:31



















  • Possible duplicate of How do I order a has_many through association in Ruby on Rails?

    – mrzasa
    Nov 15 '18 at 13:11











  • @mrzasa This case is slightly different as I'm trying to order based on the through table and not the user itself. See my edit for details.

    – AdColvin
    Nov 15 '18 at 14:29











  • I'd advise to remove through and implement it as two separate has_many :campaign_users associations in both classes

    – mrzasa
    Nov 15 '18 at 14:31

















Possible duplicate of How do I order a has_many through association in Ruby on Rails?

– mrzasa
Nov 15 '18 at 13:11





Possible duplicate of How do I order a has_many through association in Ruby on Rails?

– mrzasa
Nov 15 '18 at 13:11













@mrzasa This case is slightly different as I'm trying to order based on the through table and not the user itself. See my edit for details.

– AdColvin
Nov 15 '18 at 14:29





@mrzasa This case is slightly different as I'm trying to order based on the through table and not the user itself. See my edit for details.

– AdColvin
Nov 15 '18 at 14:29













I'd advise to remove through and implement it as two separate has_many :campaign_users associations in both classes

– mrzasa
Nov 15 '18 at 14:31





I'd advise to remove through and implement it as two separate has_many :campaign_users associations in both classes

– mrzasa
Nov 15 '18 at 14:31












3 Answers
3






active

oldest

votes


















2














EDIT : Thanks to @AdColvin I changed the code block to make it work ;)



Have you tried something like



has_many :users, -> { order('campaign_users.created_at DESC') }, through: campaign_users


You can do that because ActiveRecord will generate a JOIN in the resulting SQL, then you can order on any table that is joined.



Also, the campaign_users in the order statement should be the name of the table, not the name of the model or the relation






share|improve this answer





















  • 1





    This worked for me, with a slight modification. See my answer below.

    – AdColvin
    Nov 17 '18 at 13:40



















0














The trick is as @kevcha has already pointed out calling order with a string of the column you want.



But instead of adding the order clause directly to the association you may want to use a association extension:



class Campaign < ApplicationRecord
has_many :campaign_users
has_many :users, through: :campaign_users do
def order_by_join_date
order('campaign_users.created_at DESC')
end
end
end


This lets you call campaign.users.order_by_join_date to explicitly get the records in a specific order. It avoids some of the same pitfalls that surround default scope.






share|improve this answer































    0














    @kevcha When I tried your answer exactly as you suggested, I got the following error:




    syntax error, unexpected 'n', expecting => ...mpaign_users.created_at
    ASC') }




    But, when I add the scope just after has_many :users, it works fine:



    has_many :users, -> { order('campaign_users.created_at DESC') }, through: :campaign_users


    Also worth noting is that created_at seems to be identical for objects created from a fixture. I wasn't aware of that. I had to explicitly set created_at in my fixtures for my tests around this to pass.






    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53318872%2frails-order-a-has-many-relation-using-a-through-table%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      2














      EDIT : Thanks to @AdColvin I changed the code block to make it work ;)



      Have you tried something like



      has_many :users, -> { order('campaign_users.created_at DESC') }, through: campaign_users


      You can do that because ActiveRecord will generate a JOIN in the resulting SQL, then you can order on any table that is joined.



      Also, the campaign_users in the order statement should be the name of the table, not the name of the model or the relation






      share|improve this answer





















      • 1





        This worked for me, with a slight modification. See my answer below.

        – AdColvin
        Nov 17 '18 at 13:40
















      2














      EDIT : Thanks to @AdColvin I changed the code block to make it work ;)



      Have you tried something like



      has_many :users, -> { order('campaign_users.created_at DESC') }, through: campaign_users


      You can do that because ActiveRecord will generate a JOIN in the resulting SQL, then you can order on any table that is joined.



      Also, the campaign_users in the order statement should be the name of the table, not the name of the model or the relation






      share|improve this answer





















      • 1





        This worked for me, with a slight modification. See my answer below.

        – AdColvin
        Nov 17 '18 at 13:40














      2












      2








      2







      EDIT : Thanks to @AdColvin I changed the code block to make it work ;)



      Have you tried something like



      has_many :users, -> { order('campaign_users.created_at DESC') }, through: campaign_users


      You can do that because ActiveRecord will generate a JOIN in the resulting SQL, then you can order on any table that is joined.



      Also, the campaign_users in the order statement should be the name of the table, not the name of the model or the relation






      share|improve this answer















      EDIT : Thanks to @AdColvin I changed the code block to make it work ;)



      Have you tried something like



      has_many :users, -> { order('campaign_users.created_at DESC') }, through: campaign_users


      You can do that because ActiveRecord will generate a JOIN in the resulting SQL, then you can order on any table that is joined.



      Also, the campaign_users in the order statement should be the name of the table, not the name of the model or the relation







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Nov 18 '18 at 17:13

























      answered Nov 15 '18 at 15:37









      kevchakevcha

      580513




      580513








      • 1





        This worked for me, with a slight modification. See my answer below.

        – AdColvin
        Nov 17 '18 at 13:40














      • 1





        This worked for me, with a slight modification. See my answer below.

        – AdColvin
        Nov 17 '18 at 13:40








      1




      1





      This worked for me, with a slight modification. See my answer below.

      – AdColvin
      Nov 17 '18 at 13:40





      This worked for me, with a slight modification. See my answer below.

      – AdColvin
      Nov 17 '18 at 13:40













      0














      The trick is as @kevcha has already pointed out calling order with a string of the column you want.



      But instead of adding the order clause directly to the association you may want to use a association extension:



      class Campaign < ApplicationRecord
      has_many :campaign_users
      has_many :users, through: :campaign_users do
      def order_by_join_date
      order('campaign_users.created_at DESC')
      end
      end
      end


      This lets you call campaign.users.order_by_join_date to explicitly get the records in a specific order. It avoids some of the same pitfalls that surround default scope.






      share|improve this answer




























        0














        The trick is as @kevcha has already pointed out calling order with a string of the column you want.



        But instead of adding the order clause directly to the association you may want to use a association extension:



        class Campaign < ApplicationRecord
        has_many :campaign_users
        has_many :users, through: :campaign_users do
        def order_by_join_date
        order('campaign_users.created_at DESC')
        end
        end
        end


        This lets you call campaign.users.order_by_join_date to explicitly get the records in a specific order. It avoids some of the same pitfalls that surround default scope.






        share|improve this answer


























          0












          0








          0







          The trick is as @kevcha has already pointed out calling order with a string of the column you want.



          But instead of adding the order clause directly to the association you may want to use a association extension:



          class Campaign < ApplicationRecord
          has_many :campaign_users
          has_many :users, through: :campaign_users do
          def order_by_join_date
          order('campaign_users.created_at DESC')
          end
          end
          end


          This lets you call campaign.users.order_by_join_date to explicitly get the records in a specific order. It avoids some of the same pitfalls that surround default scope.






          share|improve this answer













          The trick is as @kevcha has already pointed out calling order with a string of the column you want.



          But instead of adding the order clause directly to the association you may want to use a association extension:



          class Campaign < ApplicationRecord
          has_many :campaign_users
          has_many :users, through: :campaign_users do
          def order_by_join_date
          order('campaign_users.created_at DESC')
          end
          end
          end


          This lets you call campaign.users.order_by_join_date to explicitly get the records in a specific order. It avoids some of the same pitfalls that surround default scope.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 16 '18 at 18:34









          maxmax

          46.2k1060104




          46.2k1060104























              0














              @kevcha When I tried your answer exactly as you suggested, I got the following error:




              syntax error, unexpected 'n', expecting => ...mpaign_users.created_at
              ASC') }




              But, when I add the scope just after has_many :users, it works fine:



              has_many :users, -> { order('campaign_users.created_at DESC') }, through: :campaign_users


              Also worth noting is that created_at seems to be identical for objects created from a fixture. I wasn't aware of that. I had to explicitly set created_at in my fixtures for my tests around this to pass.






              share|improve this answer




























                0














                @kevcha When I tried your answer exactly as you suggested, I got the following error:




                syntax error, unexpected 'n', expecting => ...mpaign_users.created_at
                ASC') }




                But, when I add the scope just after has_many :users, it works fine:



                has_many :users, -> { order('campaign_users.created_at DESC') }, through: :campaign_users


                Also worth noting is that created_at seems to be identical for objects created from a fixture. I wasn't aware of that. I had to explicitly set created_at in my fixtures for my tests around this to pass.






                share|improve this answer


























                  0












                  0








                  0







                  @kevcha When I tried your answer exactly as you suggested, I got the following error:




                  syntax error, unexpected 'n', expecting => ...mpaign_users.created_at
                  ASC') }




                  But, when I add the scope just after has_many :users, it works fine:



                  has_many :users, -> { order('campaign_users.created_at DESC') }, through: :campaign_users


                  Also worth noting is that created_at seems to be identical for objects created from a fixture. I wasn't aware of that. I had to explicitly set created_at in my fixtures for my tests around this to pass.






                  share|improve this answer













                  @kevcha When I tried your answer exactly as you suggested, I got the following error:




                  syntax error, unexpected 'n', expecting => ...mpaign_users.created_at
                  ASC') }




                  But, when I add the scope just after has_many :users, it works fine:



                  has_many :users, -> { order('campaign_users.created_at DESC') }, through: :campaign_users


                  Also worth noting is that created_at seems to be identical for objects created from a fixture. I wasn't aware of that. I had to explicitly set created_at in my fixtures for my tests around this to pass.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 17 '18 at 13:38









                  AdColvinAdColvin

                  3111212




                  3111212






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53318872%2frails-order-a-has-many-relation-using-a-through-table%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Xamarin.iOS Cant Deploy on Iphone

                      Glorious Revolution

                      Dulmage-Mendelsohn matrix decomposition in Python