575

我正在用 Django 编写一个项目,我看到 80% 的代码都在文件中models.py。这段代码令人困惑,一段时间后,我不再理解真正发生的事情。

这是困扰我的问题:

  1. 我发现我的模型级别(应该只负责处理来自数据库的数据)也发送电子邮件、通过 API 访问其他服务等,这很丑陋。
  2. 另外,我发现将业务逻辑放在视图中是不可接受的,因为这样会变得难以控制。例如,在我的应用程序中,至少有三种方法可以创建 的新实例User,但从技术上讲,它应该统一创建它们。
  3. 我并不总是注意到我的模型的方法和属性何时变得不确定以及何时产生副作用。

这是一个简单的例子。起初,User模型是这样的:

class User(db.Models):

    def get_present_name(self):
        return self.name or 'Anonymous'

    def activate(self):
        self.status = 'activated'
        self.save()

久而久之,变成了这样:

class User(db.Models):

    def get_present_name(self): 
        # property became non-deterministic in terms of database
        # data is taken from another service by api
        return remote_api.request_user_name(self.uid) or 'Anonymous' 

    def activate(self):
        # method now has a side effect (send message to user)
        self.status = 'activated'
        self.save()
        send_mail('Your account is activated!', '…', [self.email])

我想要的是在我的代码中分离实体:

  1. 我的数据库的实体,持久性级别:我的应用程序保留了哪些数据?
  2. 我的应用程序的实体,业务逻辑级别:我的应用程序做什么?

实现这种可以在 Django 中应用的方法的良好实践是什么?

4

10 回答 10

740

您似乎在询问数据模型域模型之间的区别——后者是您可以找到最终用户感知的业务逻辑和实体的地方,前者是您实际存储数据的地方。

此外,我将您问题的第三部分解释为:如何注意到未能将这些模型分开。

这是两个非常不同的概念,总是很难将它们分开。但是,有一些常见的模式和工具可用于此目的。

关于领域模型

您需要认识到的第一件事是您的域模型并不是真正的数据;它是关于诸如“激活该用户”、“停用该用户”、“当前激活了哪些用户?”和“该用户的名字是什么?”之类的操作问题。用经典术语来说:它是关于查询命令的。

在命令中思考

让我们从查看示例中的命令开始:“激活此用户”和“停用此用户”。命令的好处是它们可以很容易地用小的给定时间场景来表达:

给定一个非活动用户
,当管理员激活该用户
,该用户变为活动状态
并向该用户发送一封确认电子邮件,
并将一个条目添加到系统日志中
(等等)

这样的场景有助于查看单个命令如何影响基础设施的不同部分——在这种情况下,您的数据库(某种“活动”标志)、邮件服务器、系统日志等。

这样的场景也确实可以帮助您设置测试驱动的开发环境。

最后,在命令中思考确实可以帮助您创建面向任务的应用程序。您的用户会欣赏这一点 :-)

表达命令

Django 提供了两种简单的命令表达方式;它们都是有效的选择,混合使用这两种方法并不罕见。

服务层

@Hedde已经描述了服务模块。在这里,您定义了一个单独的模块,每个命令都表示为一个函数。

服务.py

def activate_user(user_id):
    user = User.objects.get(pk=user_id)

    # set active flag
    user.active = True
    user.save()

    # mail user
    send_mail(...)

    # etc etc

使用表格

另一种方法是为每个命令使用 Django 表单。我更喜欢这种方法,因为它结合了多个密切相关的方面:

  • 命令的执行(它做了什么?)
  • 验证命令参数(可以这样做吗?)
  • 命令的介绍(我该怎么做?)

表格.py

class ActivateUserForm(forms.Form):

    user_id = IntegerField(widget = UsernameSelectWidget, verbose_name="Select a user to activate")
    # the username select widget is not a standard Django widget, I just made it up

    def clean_user_id(self):
        user_id = self.cleaned_data['user_id']
        if User.objects.get(pk=user_id).active:
            raise ValidationError("This user cannot be activated")
        # you can also check authorizations etc. 
        return user_id

    def execute(self):
        """
        This is not a standard method in the forms API; it is intended to replace the 
        'extract-data-from-form-in-view-and-do-stuff' pattern by a more testable pattern. 
        """
        user_id = self.cleaned_data['user_id']

        user = User.objects.get(pk=user_id)

        # set active flag
        user.active = True
        user.save()

        # mail user
        send_mail(...)

        # etc etc

在查询中思考

您的示例不包含任何查询,因此我冒昧地提出了一些有用的查询。我更喜欢使用术语“问题”,但查询是经典术语。有趣的查询是:“这个用户的名字是什么?”、“这个用户可以登录吗?”、“给我显示停用用户的列表”和“停用用户的地理分布是什么?”

在开始回答这些问题之前,您应该始终问自己这个问题,是这样的:

  • 仅针对我的模板的表示查询,和/或
  • 与执行我的命令相关的业务逻辑查询,和/或
  • 报告查询

呈现性查询仅用于改进用户界面。业务逻辑查询的答案直接影响命令的执行。报告查询仅用于分析目的,并且具有较宽松的时间限制。这些类别并不相互排斥。

另一个问题是:“我可以完全控制答案吗?” 例如,当查询用户名时(在此上下文中),我们无法控制结果,因为我们依赖于外部 API。

查询

Django 中最基本的查询是使用 Manager 对象:

User.objects.filter(active=True)

当然,这仅在数据实际表示在您的数据模型中时才有效。这并非总是如此。在这些情况下,您可以考虑以下选项。

自定义标签和过滤器

第一个替代方案对于仅是展示性的查询很有用:自定义标签和模板过滤器。

模板.html

<h1>Welcome, {{ user|friendly_name }}</h1>

模板标签.py

@register.filter
def friendly_name(user):
    return remote_api.get_cached_name(user.id)

查询方法

如果您的查询不仅仅是展示性的,您可以将查询添加到您的services.py(如果您正在使用它),或者引入一个queries.py模块:

查询.py

def inactive_users():
    return User.objects.filter(active=False)


def users_called_publysher():
    for user in User.objects.all():
        if remote_api.get_cached_name(user.id) == "publysher":
            yield user 

代理模型

代理模型在业务逻辑和报告的上下文中非常有用。您基本上定义了模型的增强子集。您可以通过覆盖Manager.get_queryset()方法来覆盖 Manager 的基本 QuerySet。

模型.py

class InactiveUserManager(models.Manager):
    def get_queryset(self):
        query_set = super(InactiveUserManager, self).get_queryset()
        return query_set.filter(active=False)

class InactiveUser(User):
    """
    >>> for user in InactiveUser.objects.all():
    …        assert user.active is False 
    """

    objects = InactiveUserManager()
    class Meta:
        proxy = True

查询模型

对于本质上复杂但经常执行的查询,可以使用查询模型。查询模型是一种非规范化形式,其中单个查询的相关数据存储在单独的模型中。诀窍当然是使非规范化模型与主模型保持同步。只有当更改完全在您的控制之下时,才能使用查询模型。

模型.py

class InactiveUserDistribution(models.Model):
    country = CharField(max_length=200)
    inactive_user_count = IntegerField(default=0)

第一个选项是在您的命令中更新这些模型。如果这些模型仅由一两个命令更改,这将非常有用。

表格.py

class ActivateUserForm(forms.Form):
    # see above
   
    def execute(self):
        # see above
        query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
        query_model.inactive_user_count -= 1
        query_model.save()

更好的选择是使用自定义信号。这些信号当然是由您的命令发出的。信号的优势在于您可以使多个查询模型与原始模型保持同步。此外,可以使用 Celery 或类似框架将信号处理卸载到后台任务。

信号.py

user_activated = Signal(providing_args = ['user'])
user_deactivated = Signal(providing_args = ['user'])

表格.py

class ActivateUserForm(forms.Form):
    # see above
   
    def execute(self):
        # see above
        user_activated.send_robust(sender=self, user=user)

模型.py

class InactiveUserDistribution(models.Model):
    # see above

@receiver(user_activated)
def on_user_activated(sender, **kwargs):
        user = kwargs['user']
        query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
        query_model.inactive_user_count -= 1
        query_model.save()
    

保持清洁

使用这种方法时,很容易确定您的代码是否保持干净。只需遵循以下准则:

  • 我的模型是否包含不仅仅管理数据库状态的方法?您应该提取一个命令。
  • 我的模型是否包含不映射到数据库字段的属性?您应该提取一个查询。
  • 我的模型是否引用了不是我的数据库的基础架构(例如邮件)?您应该提取一个命令。

视图也是如此(因为视图经常遇到同样的问题)。

  • 我的视图是否主动管理数据库模型?您应该提取一个命令。

一些参考资料

Django 文档:代理模型

Django 文档:信号

架构:领域驱动设计

于 2012-10-12T10:59:06.263 回答
176

我通常在视图和模型之间实现一个服务层。这就像您的项目的 API 一样,可以让您从直升机上清楚地了解正在发生的事情。我从我的一位同事那里继承了这种做法,他在 Java 项目 (JSF) 中经常使用这种分层技术,例如:

模型.py

class Book:
   author = models.ForeignKey(User)
   title = models.CharField(max_length=125)

   class Meta:
       app_label = "library"

服务.py

from library.models import Book

def get_books(limit=None, **filters):
    """ simple service function for retrieving books can be widely extended """
    return Book.objects.filter(**filters)[:limit]  # list[:None] will return the entire list

视图.py

from library.services import get_books

class BookListView(ListView):
    """ simple view, e.g. implement a _build and _apply filters function """
    queryset = get_books()

请注意,我通常将模型、视图和服务带到模块级别,并根据项目的大小进一步分离

于 2012-09-25T08:58:21.457 回答
79

首先,不要重复自己

然后,请注意不要过度设计,有时这只是浪费时间,并使某人失去对重要事物的关注。不时复习python之禅。

看看活跃的项目

  • 更多人 = 更多需要妥善组织
  • django 存储库它们具有简单的结构。
  • pip 存储库它们有一个简单的目录结构。
  • 织物存储库也是一个不错的选择。

    • 您可以将所有模型放在yourapp/models/logicalgroup.py
  • eg User,Group相关模型可以下yourapp/models/users.py
  • 例如Poll, Question, Answer... 可能会低于yourapp/models/polls.py
  • __all__在里面加载你需要的东西yourapp/models/__init__.py

更多关于 MVC

  • 模型是你的数据
    • 这包括您的实际数据
    • 这还包括您的会话/cookie/缓存/fs/索引数据
  • 用户与控制器交互以操作模型
    • 这可能是一个 API,或者是一个保存/更新数据的视图
    • 这可以用request.GET/ request.POST...等进行调整
    • 也考虑分页过滤
  • 数据更新视图
    • 模板获取数据并相应地格式化
    • 甚至没有模板的 API 也是视图的一部分;例如tastypiepiston
    • 这也应该考虑到中间件。

利用中间件/模板标签

  • 如果您需要为每个请求完成一些工作,中间件是一种方法。
    • 例如添加时间戳
    • 例如更新有关页面点击量的指标
    • 例如填充缓存
  • 如果您有总是重复出现用于格式化对象的代码片段,那么模板标签很好。
    • 例如活动标签/网址面包屑

利用模型管理器

  • 创建User可以进入UserManager(models.Manager).
  • 实例的血腥细节应该继续models.Model
  • 血淋淋的细节queryset可以进入models.Manager
  • 您可能想一次创建一个User,因此您可能认为它应该存在于模型本身上,但是在创建对象时,您可能没有所有详细信息:

例子:

class UserManager(models.Manager):
   def create_user(self, username, ...):
      # plain create
   def create_superuser(self, username, ...):
      # may set is_superuser field.
   def activate(self, username):
      # may use save() and send_mail()
   def activate_in_bulk(self, queryset):
      # may use queryset.update() instead of save()
      # may use send_mass_mail() instead of send_mail()

尽可能使用表格

如果您有映射到模型的表单,则可以消除许多样板代码。ModelForm documentation是相当不错的。如果您有很多自定义项(或者有时避免循环导入错误以用于更高级的用途),则将表单代码与模型代码分开可能会很好。

尽可能使用管理命令

  • 例如yourapp/management/commands/createsuperuser.py
  • 例如yourapp/management/commands/activateinbulk.py

如果你有业务逻辑,你可以把它分开

  • django.contrib.auth 使用 backends,就像 db 有一个后端...等等。
  • 为您的业务逻辑添加一个setting(例如AUTHENTICATION_BACKENDS
  • 你可以使用django.contrib.auth.backends.RemoteUserBackend
  • 你可以使用yourapp.backends.remote_api.RemoteUserBackend
  • 你可以使用yourapp.backends.memcached.RemoteUserBackend
  • 将困难的业务逻辑委托给后端
  • 确保在输入/输出上设置正确的期望。
  • 更改业务逻辑就像更改设置一样简单 :)

后端示例:

class User(db.Models):
    def get_present_name(self): 
        # property became not deterministic in terms of database
        # data is taken from another service by api
        return remote_api.request_user_name(self.uid) or 'Anonymous' 

可能变成:

class User(db.Models):
   def get_present_name(self):
      for backend in get_backends():
         try:
            return backend.get_present_name(self)
         except: # make pylint happy.
            pass
      return None

更多关于设计模式

更多关于接口边界

  • 您要使用的代码真的是模型的一部分吗?->yourapp.models
  • 代码是业务逻辑的一部分吗?->yourapp.vendor
  • 代码是通用工具/库的一部分吗?->yourapp.libs
  • 代码是业务逻辑库的一部分吗?->yourapp.libs.vendoryourapp.vendor.libs
  • 这是一个很好的选择:你能独立测试你的代码吗?
  • 分离合乎逻辑吗?
    • 对很好 :)
    • 不,您可能无法单独测试这些逻辑概念。
  • 你认为当你得到 10 倍的代码时你需要重构吗?
    • 是的,不好,不行,重构可能需要做很多工作
    • 不,这太棒了!

简而言之,你可以有

  • yourapp/core/backends.py
  • yourapp/core/models/__init__.py
  • yourapp/core/models/users.py
  • yourapp/core/models/questions.py
  • yourapp/core/backends.py
  • yourapp/core/forms.py
  • yourapp/core/handlers.py
  • yourapp/core/management/commands/__init__.py
  • yourapp/core/management/commands/closepolls.py
  • yourapp/core/management/commands/removeduplicates.py
  • yourapp/core/middleware.py
  • yourapp/core/signals.py
  • yourapp/core/templatetags/__init__.py
  • yourapp/core/templatetags/polls_extras.py
  • yourapp/core/views/__init__.py
  • yourapp/core/views/users.py
  • yourapp/core/views/questions.py
  • yourapp/core/signals.py
  • yourapp/lib/utils.py
  • yourapp/lib/textanalysis.py
  • yourapp/lib/ratings.py
  • yourapp/vendor/backends.py
  • yourapp/vendor/morebusinesslogic.py
  • yourapp/vendor/handlers.py
  • yourapp/vendor/middleware.py
  • yourapp/vendor/signals.py
  • yourapp/tests/test_polls.py
  • yourapp/tests/test_questions.py
  • yourapp/tests/test_duplicates.py
  • yourapp/tests/test_ratings.py

或任何其他对您有帮助的东西;找到您需要的接口边界将对您有所帮助。

于 2012-10-12T07:16:31.913 回答
30

Django 使用了一种稍微修改过的 MVC。Django 中没有“控制器”的概念。最接近的代理是“视图”,这往往会导致与 MVC 转换混淆,因为在 MVC 中,视图更像 Django 的“模板”。

在 Django 中,“模型”不仅仅是一个数据库抽象。在某些方面,它与 Django 的“视图”作为 MVC 的控制器共享职责。它包含与实例相关联的全部行为。如果该实例需要与外部 API 交互作为其行为的一部分,那么这仍然是模型代码。事实上,模型根本不需要与数据库交互,因此您可以想象模型完全作为外部 API 的交互层存在。这是一个更自由的“模型”概念。

于 2012-09-25T14:40:12.050 回答
10

在 Django 中,MVC 结构正如 Chris Pratt 所说,与其他框架中使用的经典 MVC 模型不同,我认为这样做的主要原因是避免过于严格的应用程序结构,就像在其他 MVC 框架如 CakePHP 中发生的那样。

在 Django 中,MVC 的实现方式如下:

视图层一分为二。视图应该只用于管理 HTTP 请求,它们被调用并响应它们。视图与应用程序的其余部分(表单、模型表单、自定义类,在简单情况下直接与模型)进行通信。为了创建界面,我们使用模板。模板对 Django 来说是类似字符串的,它将一个上下文映射到它们中,并且这个上下文由应用程序传递给视图(当视图询问时)。

模型层提供封装、抽象、验证、智能并使您的数据面向对象(他们说有一天 DBMS 也会这样做)。这并不意味着你应该制作巨大的models.py文件(事实上,一个很好的建议是将你的模型分成不同的文件,将它们放入一个名为'models'的文件夹中,在其中创建一个'__init__.py'文件导入所有模型并最终使用 models.Model 类的属性“app_label”的文件夹)。模型应该将您从对数据的操作中抽象出来,它将使您的应用程序更简单。如果需要,您还应该为模型创建外部类,例如“工具”。您还可以在模型中使用遗产,将模型的 Meta 类的“抽象”属性设置为“真”。

剩下的在哪里?好吧,小型 Web 应用程序通常是一种数据接口,在某些小型程序情况下,使用视图来查询或插入数据就足够了。更常见的情况将使用 Forms 或 ModelForms,它们实际上是“控制器”。这只不过是一个常见问题的实用解决方案,而且是一个非常快速的解决方案。这是一个网站用来做的事情。

如果 Forms 不适合您,那么您应该创建自己的类来发挥作用,管理应用程序就是一个很好的例子:您可以阅读 ModelAmin 代码,这实际上是一个控制器。没有标准结构,我建议您检查现有的 Django 应用程序,这取决于每种情况。这就是 Django 开发人员的意图,您可以添加 xml 解析器类、API 连接器类、添加 Celery 用于执行任务、扭曲用于基于反应器的应用程序、仅使用 ORM、创建 Web 服务、修改管理应用程序等等。 .. 编写高质量的代码、是否尊重 MVC 哲学、使其基于模块并创建自己的抽象层是您的责任。它非常灵活。

我的建议:尽可能多地阅读代码,周围有很多 django 应用程序,但不要那么认真。每个案例都是不同的,模式和理论都有帮助,但并非总是如此,这是一个不精确的科学,django 只是为您提供了可以用来缓解一些痛苦的好工具(如管理界面、Web 表单验证、i18n、观察者模式实现等)前面提到的和其他的),但好的设计来自经验丰富的设计师。

PS.:使用身份验证应用程序(来自标准 django)中的“用户”类,您可以制作例如用户配置文件,或者至少阅读其代码,这对您的情况很有用。

于 2012-10-16T16:02:22.947 回答
1

一个老问题,但无论如何我想提供我的解决方案。它基于接受模型对象也需要一些额外的功能,而将其放置在models.py中很尴尬。繁重的业务逻辑可能会根据个人喜好单独编写,但我至少喜欢模型做与自己相关的所有事情。该解决方案还支持那些喜欢将所有逻辑放置在模型中的人。

因此,我设计了一个 hack,允许我将逻辑与模型定义分开,并且仍然从我的 IDE 中获得所有提示。

优点应该是显而易见的,但这列出了我观察到的一些:

  • 数据库定义仍然如此 - 没有附加逻辑“垃圾”
  • 与模型相关的逻辑都整齐地放在一个地方
  • 所有服务(表单、REST、视图)都有一个逻辑访问点
  • 最重要的是:一旦我意识到我的models.py变得太杂乱并且不得不将逻辑分离出来,我就不必重写任何代码。分离是平滑和迭代的:我可以一次执行一个函数,或者整个类或整个models.py。

我一直在使用 Python 3.4 及更高版本以及 Django 1.8 及更高版本。

应用程序/模型.py

....
from app.logic.user import UserLogic

class User(models.Model, UserLogic):
    field1 = models.AnyField(....)
    ... field definitions ...

应用程序/逻辑/user.py

if False:
    # This allows the IDE to know about the User model and its member fields
    from main.models import User

class UserLogic(object):
    def logic_function(self: 'User'):
        ... code with hinting working normally ...

我唯一想不通的是如何让我的 IDE(在本例中为 PyCharm)识别 UserLogic 实际上是用户模型。但由于这显然是一个 hack,我很乐意接受总是为self参数指定类型的小麻烦。

于 2017-03-27T08:00:23.987 回答
1

我不得不同意你的看法。django 有很多可能性,但最好的起点是回顾Django 的设计理念

  1. 从模型属性调用 API 并不理想,在视图中执行类似的操作似乎更有意义,并可能创建一个服务层来保持干燥。如果对 API 的调用是非阻塞的并且调用成本很高,那么将请求发送给服务工作者(从队列中消费的工作者)可能是有意义的。

  2. 根据 Django 的设计理念,模型封装了“对象”的各个方面。因此,与该对象相关的所有业务逻辑都应该存在于那里:

包括所有相关的领域逻辑

模型应该按照 Martin Fowler 的 Active Record 设计模式封装“对象”的各个方面。

  1. 您描述的副作用很明显,这里的逻辑可以更好地分解为查询集和管理器。这是一个例子:

    模型.py

    import datetime
    
    from djongo import models
    from django.db.models.query import QuerySet
    from django.contrib import admin
    from django.db import transaction
    
    
    class MyUser(models.Model):
    
        present_name = models.TextField(null=False, blank=True)
        status = models.TextField(null=False, blank=True)
        last_active = models.DateTimeField(auto_now=True, editable=False)
    
        # As mentioned you could put this in a template tag to pull it
        # from cache there. Depending on how it is used, it could be
        # retrieved from within the admin view or from a custom view
        # if that is the only place you will use it.
        #def get_present_name(self):
        #    # property became non-deterministic in terms of database
        #    # data is taken from another service by api
        #    return remote_api.request_user_name(self.uid) or 'Anonymous'
    
        # Moved to admin as an action
        # def activate(self):
        #     # method now has a side effect (send message to user)
        #     self.status = 'activated'
        #     self.save()
        #     # send email via email service
        #     #send_mail('Your account is activated!', '…', [self.email])
    
        class Meta:
            ordering = ['-id']  # Needed for DRF pagination
    
        def __unicode__(self):
            return '{}'.format(self.pk)
    
    
    class MyUserRegistrationQuerySet(QuerySet):
    
        def for_inactive_users(self):
            new_date = datetime.datetime.now() - datetime.timedelta(days=3*365)  # 3 Years ago
            return self.filter(last_active__lte=new_date.year)
    
        def by_user_id(self, user_ids):
            return self.filter(id__in=user_ids)
    
    
    class MyUserRegistrationManager(models.Manager):
    
        def get_query_set(self):
            return MyUserRegistrationQuerySet(self.model, using=self._db)
    
        def with_no_activity(self):
            return self.get_query_set().for_inactive_users()
    

    管理员.py

    # Then in model admin
    
    class MyUserRegistrationAdmin(admin.ModelAdmin):
        actions = (
            'send_welcome_emails',
        )
    
        def send_activate_emails(self, request, queryset):
            rows_affected = 0
            for obj in queryset:
                with transaction.commit_on_success():
                    # send_email('welcome_email', request, obj) # send email via email service
                    obj.status = 'activated'
                    obj.save()
                    rows_affected += 1
    
            self.message_user(request, 'sent %d' % rows_affected)
    
    admin.site.register(MyUser, MyUserRegistrationAdmin)
    
于 2019-02-21T21:40:50.330 回答
0

我大多同意选择的答案(https://stackoverflow.com/a/12857584/871392),但想在“查询”部分添加选项。

可以为模型定义 QuerySet 类以进行过滤查询等。之后,您可以为模型的管理器代理此查询集类,就像内置的 Manager 和 QuerySet 类一样。

虽然,如果您必须查询多个数据模型来获得一个域模型,我认为将它放在单独的模块中似乎更合理,就像之前建议的那样。

于 2014-03-16T21:07:10.133 回答
0

关于利弊的不同选项的最全面的文章:

  1. 想法#1:胖模特
  2. 想法#2:将业务逻辑放入视图/表单中
  3. 理念#3:服务
  4. 想法#4:查询集/管理器
  5. 结论

来源: https ://sunscrapers.com/blog/where-to-put-business-logic-django/

于 2020-05-02T11:57:45.150 回答
-8

Django 旨在轻松用于交付网页。如果您对此不满意,也许您应该使用其他解决方案。

我正在模型上编写根操作或通用操作(以具有相同的界面)以及模型控制器上的其他操作。如果我需要其他模型的操作,我会导入它的控制器。

这种方法对我和我的应用程序的复杂性来说已经足够了。

Hedde 的回应是一个例子,展示了 django 和 python 本身的灵活性。

无论如何,非常有趣的问题!

于 2012-09-25T09:40:37.920 回答