Преобразование метода в область в Rails 3 - PullRequest
2 голосов
/ 28 июня 2011

Я хотел бы преобразовать этот метод в область в рельсах, чтобы я мог вызвать что-то вроде Batch.all_completed, и он вернул бы все пакеты, которые соответствовали критериям в методе:

def all_completed?
  articles.with_status('Completed').count >= project.articles_per_week
end

Классопределение пакета, на котором это будет выполняться, таково:

class Batch < ActiveRecord::Base
  has_many :articles
  belongs_to :project

Есть ли способ сделать это просто?with_status - это именованная область, а articles_per_week - это метод, а не поле.Вот вывод SQL с левой стороны:

SELECT "articles".* FROM "articles" join jobs on jobs.article_id = articles.id join statuses on statuses.id = jobs.status_id WHERE ("articles".batch_id = XXX) AND (statuses.description = 'Completed')

Спасибо!

1 Ответ

0 голосов
/ 28 июня 2011

[Редактировать 2]

Учитывая, что Articles_per_week это не поле, а метод, вам придется отказаться от возможности цепочки областей видимости - области видимости сводятсяпросто AREL, который генерирует оператор SQL, и нет никакого способа преодолеть разрыв между методом экземпляра и базой данных.На этом этапе все становится проще, и вы можете просто сделать все это классовым методом с вызовом # reject в конце, чтобы отбросить те, которые не соответствуют критериям подсчета:

class Batch < ActiveRecord::Base
  belongs_to :project
  has_many :articles

  def self.all_completed
    joins(:articles).
    includes(:project).
    where("articles.status = 'Completed'").
    group("batches.id").
    select("batches.*, count(articles.id) as article_count").
    reject {|batch| batch.article_count.to_i < batch.project.articles_per_week}
  end
end

И вы по-прежнему получаете минимальные удары по БД с этим (2).Но, как уже было отмечено, вызов #reject преобразует ваш вывод в базовый массив объектов, а не в цепочку ActiveRecord с цепочкой.

Не уверен, почему мне пришлось преобразовать количество статей в целое число, но так оно и было.

Я оставил свои предыдущие версии, так как я думаю, что они предоставляют хорошую дополнительную информацию для других сценариев.

[Редактировать 1]

Вот версия, которую вы можете добавить в качестве области действия для Batch, чтобы она была цепной для других областей.

scope :all_completed,
  joins(:articles, :project).
  where("articles.status = 'Completed'").
  having("count(articles.id) >= projects.articles_per_week").
  group("batches.id").
  select("batches.*, projects.articles_per_week")

Я заметил, что при тестировании это может оказать влияние на некоторыестандартных методов ActiveRecord.Например, #size не работает, потому что в ActiveRecord, который переведен в SELECT COUNT (*), и он не будет работать здесь из-за отсутствия projects.articles_per_week в выбранных полях.

[Оригинал]

Мой первый ответ здесь.

Я не уверен, соответствует ли это вашему запросу "просто", но он выполняет задачу в одном операторе SQL.

Article.includes([:batch => :project]).where(:status => 'Completed').group(:batch_id).having("count(articles.id) >= projects.articles_per_week").map(&:batch)

Это предполагает Rails 3 и следующие настройки модели:

class Batch < ActiveRecord::Base
  has_many :articles
  belongs_to :project
end

class Project < ActiveRecord::Base
  has_many :batches
end

class Article < ActiveRecord::Base
  belongs_to :batch
end

Кстати, вот мои тестовые данные.Структура выше правильно вытащила партии 1 и 2 и проигнорировала партию 3.

-- phpMyAdmin SQL Dump
-- version 3.3.2deb1
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Jun 27, 2011 at 07:13 PM
-- Server version: 5.1.41
-- PHP Version: 5.3.2-1ubuntu4.9

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

--
-- Database: `stack_development`
--

-- --------------------------------------------------------

--
-- Table structure for table `articles`
--

DROP TABLE IF EXISTS `articles`;
CREATE TABLE IF NOT EXISTS `articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `batch_id` int(11) DEFAULT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `status` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=16 ;

--
-- Dumping data for table `articles`
--

INSERT INTO `articles` (`id`, `batch_id`, `name`, `status`, `created_at`, `updated_at`) VALUES
(1, 1, 'Test 1 Article', 'Completed', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(2, 1, 'Test 2 Article', 'Completed', '2011-06-27 22:40:36', '2011-06-27 22:40:36'),
(3, 1, 'Test 3 Article', 'Completed', '2011-06-27 22:40:42', '2011-06-27 22:40:42'),
(4, 1, 'Test Article 4', 'Completed', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(5, 1, 'Test Article 5', 'Pending', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(6, 2, 'Test 1 Article', 'Completed', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(7, 2, 'Test 2 Article', 'Completed', '2011-06-27 22:40:36', '2011-06-27 22:40:36'),
(8, 2, 'Test 3 Article', 'Completed', '2011-06-27 22:40:42', '2011-06-27 22:40:42'),
(9, 2, 'Test Article 4', 'Pending', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(10, 2, 'Test Article 5', 'Pending', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(11, 3, 'Test 1 Article', 'Completed', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(12, 3, 'Test 2 Article', 'Completed', '2011-06-27 22:40:36', '2011-06-27 22:40:36'),
(13, 3, 'Test 3 Article', 'Pending', '2011-06-27 22:40:42', '2011-06-27 22:40:42'),
(14, 3, 'Test Article 4', 'Pending', '2011-06-27 22:38:55', '2011-06-27 22:38:55'),
(15, 3, 'Test Article 5', 'Pending', '2011-06-27 22:38:55', '2011-06-27 22:38:55');

-- --------------------------------------------------------

--
-- Table structure for table `batches`
--

DROP TABLE IF EXISTS `batches`;
CREATE TABLE IF NOT EXISTS `batches` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `project_id` int(11) DEFAULT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=4 ;

--
-- Dumping data for table `batches`
--

INSERT INTO `batches` (`id`, `project_id`, `name`, `created_at`, `updated_at`) VALUES
(1, 1, 'Test 1 Batch', '2011-06-27 22:38:11', '2011-06-27 22:38:11'),
(2, 2, 'Test 2 Batch', '2011-06-27 22:38:11', '2011-06-27 22:38:11'),
(3, 3, 'Test 3 Batch', '2011-06-27 22:38:11', '2011-06-27 22:38:11');

-- --------------------------------------------------------

--
-- Table structure for table `projects`
--

DROP TABLE IF EXISTS `projects`;
CREATE TABLE IF NOT EXISTS `projects` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `articles_per_week` int(11) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=4 ;

--
-- Dumping data for table `projects`
--

INSERT INTO `projects` (`id`, `name`, `articles_per_week`, `created_at`, `updated_at`) VALUES
(1, 'Test 1 project', 3, '2011-06-27 22:37:11', '2011-06-27 22:37:11'),
(2, 'Test 2 Project', 3, '2011-06-27 22:37:21', '2011-06-27 22:37:21'),
(3, 'Test 3 Project', 3, '2011-06-27 22:37:21', '2011-06-27 22:37:21');
...