设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

Rails和Django的深度技术对比

2015-1-19 14:51| 发布者: joejoe0332| 查看: 6702| 评论: 0|原作者: Ev4n, LeoXu, chxm1990, BruceLinxu, edwardlu_|来自: oschina

摘要: 6个月以前我在大学用Ruby on Rails做 了一个项目而且一直做到现在。我做地第一件事就是仔细地学习了这两个框架并对它们进行了比较,但是我记得当时我很泄气的。当我寻找这些问题(比如说:”对 于这两者来说,数据库 ...


测试

在Rails中测试很轻松,与Django比较起来更需要着重强调。

Fixture

两个框架以相似的方式都支持fixture(示例数据)。我却给Rails更高的评价,因为它更实用,能从文件的名称得知你在使用哪个模板。Rails使用YAML格式的fixture,这是人类可读的数据序列化格式。

1
2
3
4
5
6
7
8
9
10
# users.yml (Rails当前知道我们在使用user的fixtures)
john:
  name: John Smith
  birthday: 1989-04-17
  profession: Blacksmith
 
bob:
  name: Bob Costa
  birthday: 1973-08-10
  profession: Surfer

所有的fixture会自动加载而且在测试中可以作为本地变量来访问。

1
users(:john).name # John Smith

Django也支持YAML格式的fixture但是开发人员更倾向于使用JSON格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[
  {
    "model""auth.user",
    "fields": {
      "name""John Smith",
      "birthday""1989-04-17",
      "profession""Blacksmith",
    }
  },
  {
    "model""auth.user",
    "fields": {
      "name""Bob Costa",
      "birthday""1973-08-10",
      "profession""Surfer",
    }
  }
]

这没什么吸引力,注意它有多啰嗦,因为你必须显式的定义它属于哪个模板,然后在 fields下面列出每个字段。


测试模板

在单元测试模板时两种框架的方式基本一致。使用一组不同类型的断言来进行确定,比如assert_equal,assert_not_equal,assert_nil,assert_raises等等。

1
2
3
4
5
6
class AnimalTest < ActiveSupport::TestCase
  test "Animals that can speak are correctly identified" do
     assert_equal animals(:lion).speak(), 'The lion says "roar"'
     assert_equal animals(:cat).speak(), 'The cat says "meow"'
  end
end

类似功能的代码在Django非常相似。

1
2
3
4
5
6
7
8
9
class AnimalTestCase(TestCase):
    def test_animals_can_speak(self):
        """Animals that can speak are correctly identified"""
        # no way of directly accessing the fixtures, so we have to
        # manually select the objects
        lion = Animal.objects.get(name="lion"
        cat = Animal.objects.get(name="cat")
        self.assertEqual(lion.speak(), 'The lion says "roar"')
        self.assertEqual(cat.speak(), 'The cat says "meow"')

测试控制器(controller)

Rails又因为它魅力而更胜一筹。Rails 使用类名称来决定哪个控制器正在被测试,而测试某个特定动作(action)就像调用http_verb :action_name一样简单。我们看一下例子。

1
2
3
4
5
6
7
8
class UsersControllerTest < ActionController::TestCase
  test "should get index" do
    get :index # 向index 动作发起GET请求
    assert_response :success # 请求返回200
    # assigns是包含所有实例变量的hash
    assert_not_nil assigns(:users)
  end
end

上面的代码很容易理解正在发生什么。第一行测试模拟了向 User控制器的 index动作发起一个请求。第二行随后检查请求是否成功(返回代码200-299)。 assigns是一个hash,包含了传递到视图(view)的实例变量。所以第三行检查是否存在名为 users的实例变量并且值不是 nil



也有一些类似于assert_difference 这样方便的断言帮助方法。

1
2
3
4
5
  # assert_difference检查被测试的数字在开始和结束之间是否更改 
  assert_difference('Post.count'do
    # 创建post
    post :create, post: {title: 'Some title'}
  end

在Django中测试控制器可以通过使用一个叫 Client类来完成,它扮演着虚拟浏览器(dummy web browser)的角色。下面是Django中对应的代码。

1
2
3
4
5
6
7
8
9
class UsersTest(unittest.TestCase):
    def setUp(self):
        self.client = Client()
 
    def test_index(self):
        """ should get index """
        response = self.client.get(reverse('users:index'))
        self.assertEqual(response.status_code, 200)
        self.assertIsNotNone(response.context['users'])

首先我们必须在测试设置时初始化 Client test_index的第一行模拟了向 Users控制器的 index动作申请了一个 GET请求。 reverse查找对应index动作的URL。注意代码是如此冗余并且没有类似于assert_response :success的帮助方法。 response.context包含我们传递给视图的变量。

很显然Rails的magic是相当有帮助的。Rails/Ruby同时也拥有很多第三方app,比如factory_girlRSpecMochaCucumber,这使得编写测试是一种乐趣。


工具和其他特征

依赖性管理(Dependency management)

两种框架都有出色的依赖性管理工具。Rails使用 Bundler 来读取Gemfile文件并跟踪文件中对应ruby应用程序运行所依赖的gem。

1
2
3
4
gem 'nokogiri' 
gem 'rails''3.0.0.beta3' 
gem 'rack''>=1.0' 
gem 'thin''~>1.1'

简单的在Gemfile文件中添加一个新行即可完成增加依赖( Dependency)。通过简单调用如下命令即可安装所有需要的gem:

1
bundle install

Django强烈推荐使用 virtualenv 来隔离Python环境。 pip 则用来管理python包。单独的python包的安装可以通过以下命令完成:

1
pip install django-debug-toolbar

而项目依赖文件可以通过以下集中起来:

1
pip freeze > requirements.txt

管理命令

基本上每个项目完成时都有相同的管理工作要做,比如预编译文件(precompiling assets),清理记录(log)等等。Rails使用Rake来管理这些任务。Rake非常灵活而且将开发任务变得简单,特别是依赖于其他任务的。

1
2
3
4
5
desc "吃掉食物。在吃之前需要烹饪(Cooks)和设置表格(table)。"
task eat: [:cook:set_the_tabledo
  # 在吃掉美味的食物之前, :cook和:set_the_table需要做完
  # 吃的哪部分代码可以写在这里
end

Rake的任务可以有前提条件(prerequisite)。上面称为 eat的任务,在执行之前必须运行任务 cook和任务set_the_table。Rake也支持命名空间(namespace),能将相同的任务结合成组(group)来完成。执行任务只需简单的调用任务的名称:

1
rake eat

Django管理命令就没那么灵活而且不支持前提条件和命名空间。虽然任务最终也会完成,但不是很出色。

1
2
3
4
5
6
7
class Command(BaseCommand):
    help = '吃掉食物'
 
    def handle(self*args, **options):
        call_command('cook'# 这里是如何在代码中调用管理命令
        set_the_table() # 但子任务需要是常规的python函数
        # 这里是吃的那些代码

如果我们将上面内容保存到eat.py中,我们可以如下调用它:

1
python manage.py eat

国际化和本地化

在Rails中国际化有些偏弱。在文件夹config/locales,翻译字符串在文件中作为ruby哈希来定义。

1
2
3
4
5
6
7
# config/locales/en.yml
en: # the language identifier
  greet_username: "Hello, %{user}!" # translation_key: "value"
 
# config/locales/pt.yml
pt:
  greet_username: "Olá, %{user}!"

通过函数t进行翻译。函数第一个变量是决定哪个字符串需要使用的key(比如greet_username)。Rails会自动选择正确的语言。

1
t('greet_username', user: "Bill"# Hi, Bill or Olá, Bill

我发现处理本地语言文件中key名字和手动注册这些内容很繁琐。Django将其打包进非常便捷的gettext。翻译也是通过一个帮助函数完成(ugettext),但是这次的key是不需要翻译的字符串本身。

1
ugettext('Hi, %(user)s.'% {'user''Bill'# Hi, Bill 或者 Olá, Bill

Django会检查所有的源代码并调用以下命令自动收集将要翻译的字符串:

1
django-admin.py makemessages -a

上面的命令执行后会为每一种你想要的翻译的语言生成一个文件。文件内容可能像这样:

1
2
3
# locale/pt_BR/LC_MESSAGES/django.po
msgid "Hi, %(user)s." # key
msgstr "Olá, %(user)s" # 值 (翻译)

注意到我已经在msgstr填充了翻译内容(那些本来是空的)。一旦翻译完成,必须对它们进行编译。

1
django-admin.py compilemessages

这种本地化项目的方法实际上更实用,因为不需要考虑key的名字,在需要的时候也不需要现查找。

【译注】即无需自定义key,django会将整句话作为key值代入



酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部