在django中分离业务逻辑和数据访问

本文翻译自:Separation of business logic and data access in django

I am writing a project in Django and I see that 80% of the code is in the file models.py . 我正在Django编写一个项目,我发现80%的代码都在models.py文件models.py This code is confusing and, after a certain time, I cease to understand what is really happening. 这段代码令人困惑,经过一段时间后,我不再明白究竟发生了什么。

Here is what bothers me: 这是困扰我的:

  1. I find it ugly that my model level (which was supposed to be responsible only for the work with data from a database) is also sending email, walking on API to other services, etc. 我发现我的模型级别(它应该只负责处理数据库中的数据)也发送电子邮件,在API上运行到其他服务等等,我觉得很难看。
  2. Also, I find it unacceptable to place business logic in the view, because this way it becomes difficult to control. 此外,我发现在业务逻辑中放置业务逻辑是不可接受的,因为这样很难控制。 For example, in my application there are at least three ways to create new instances of User , but technically it should create them uniformly. 例如,在我的应用程序中,至少有三种方法可以创建User新实例,但从技术上讲,它应该统一创建它们。
  3. I do not always notice when the methods and properties of my models become non-deterministic and when they develop side effects. 我并不总是注意到我的模型的方法和属性何时变得不确定,何时会产生副作用。

Here is a simple example. 这是一个简单的例子。 At first, the User model was like this: 起初, User模型是这样的:

class User(db.Models):

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

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

Over time, it turned into this: 随着时间的推移,它变成了这样:

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])

What I want is to separate entities in my code: 我想要的是在我的代码中分离实体:

  1. Entities of my database, database level: What contains my application? 我的数据库,数据库级别的实体:什么包含我的应用程序?
  2. Entities of my application, business logic level: What can make my application? 我的应用程序的实体,业务逻辑级别:什么可以使我的应用程序?

What are the good practices to implement such an approach that can be applied in Django? 实现可以在Django中应用的这种方法有哪些好的做法?


#1楼

参考:https://stackoom.com/question/qmLc/在django中分离业务逻辑和数据访问


#2楼

I usually implement a service layer in between views and models. 我通常在视图和模型之间实现服务层。 This acts like your project's API and gives you a good helicopter view of what is going on. 这就像您的项目的API一样,可以让您直观地了解正在发生的事情。 I inherited this practice from a colleague of mine that uses this layering technique a lot with Java projects (JSF), eg: 我从我的同事那里继承了这种做法,这种做法在Java项目(JSF)中使用了这种分层技术,例如:

models.py models.py

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

   class Meta:
       app_label = "library"

services.py services.py

from library.models import Book

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

views.py views.py

from library.services import get_books

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

Mind you, I usually take models, views and services to module level and separate even further depending on the project's size 请注意,我通常会将模型,视图和服务提供给模块级别,并根据项目的大小进一步分离


#3楼

Django is designed to be easely used to deliver web pages. Django旨在轻松地用于提供网页。 If you are not confortable with this perhaps you should use another solution. 如果您对此不满意,也许您应该使用另一种解决方案。

I'm writting the root or common operations on the model (to have the same interface) and the others on the controller of the model. 我正在模型的根目录或常用操作(具有相同的接口)和模型的控制器上的其他操作。 If I need an operation from other model I import its controller. 如果我需要其他模型的操作,我导入其控制器。

This approach it's enough for me and the complexity of my applications. 这种方法对我来说足够了,我的应用程序也很复杂。

Hedde's response is an example that shows the flexibility of django and python itself. Hedde的回答是一个例子,展示了django和python本身的灵活性。

Very interesting question anyway! 反正非常有趣的问题!


#4楼

Django employs a slightly modified kind of MVC. Django使用了一种稍微修改过的MVC。 There's no concept of a "controller" in Django. Django中没有“控制器”的概念。 The closest proxy is a "view", which tends to cause confusion with MVC converts because in MVC a view is more like Django's "template". 最接近的代理是“视图”,这往往会导致与MVC转换混淆,因为在MVC中,视图更像是Django的“模板”。

In Django, a "model" is not merely a database abstraction. 在Django中,“模型”不仅仅是数据库抽象。 In some respects, it shares duty with the Django's "view" as the controller of MVC. 在某些方面,它与Django作为MVC控制者的“观点”共享义务。 It holds the entirety of behavior associated with an instance. 它包含与实例关联的整个行为。 If that instance needs to interact with an external API as part of it's behavior, then that's still model code. 如果该实例需要与外部API交互作为其行为的一部分,那么这仍然是模型代码。 In fact, models aren't required to interact with the database at all, so you could conceivable have models that entirely exist as an interactive layer to an external API. 事实上,模型根本不需要与数据库交互,因此您可以想象将模型完全作为外部API的交互层存在。 It's a much more free concept of a "model". 这是一个更“自由”的“模型”概念。


#5楼

First of all, Don't repeat yourself . 首先, 不要重复自己 。

Then, please be careful not to overengineer, sometimes it is just a waste of time, and makes someone lose focus on what is important. 然后,请注意不要过度工程,有时这只是浪费时间,并使某人失去对重要事项的关注。 Review the zen of python from time to time. 不时回顾蟒蛇的禅宗 。

Take a look at active projects 看看活跃的项目

  • more people = more need to organize properly 更多人=更需要正确组织
  • the django repository they have a straightforward structure. django存储库他们有一个简单的结构。
  • the pip repository they have a straigtforward directory structure. pip存储库他们有一个直接的目录结构。
  • the fabric repository is also a good one to look at. 结构存储库也是一个很好看的。

    • you can place all your models under yourapp/models/logicalgroup.py 您可以将所有模型放在yourapp/models/logicalgroup.py
  • eg User , Group and related models can go under yourapp/models/users.py 例如, UserGroup和相关模型可以在yourapp/models/users.py
  • eg Poll , Question , Answer ... could go under yourapp/models/polls.py 例如PollQuestionAnswer ......可以在yourapp/models/polls.py
  • load what you need in __all__ inside of yourapp/models/__init__.py yourapp/models/__init__.py中的__all__加载你需要的yourapp/models/__init__.py

More about MVC 更多关于MVC的信息

  • model is your data model是你的数据
    • this includes your actual data 这包括您的实际数据
    • this also includes your session / cookie / cache / fs / index data 这还包括您的session / cookie / cache / fs / index数据
  • user interacts with controller to manipulate the model 用户与控制器交互以操纵模型
    • this could be an API, or a view that saves/updates your data 这可以是API,也可以是保存/更新数据的视图
    • this can be tuned with request.GET / request.POST ...etc 这可以通过request.GET / request.POST ...等来调整
    • think paging or filtering too. 想想分页过滤
  • the data updates the view 数据更新视图
    • the templates take the data and format it accordingly 模板采用数据并相应地格式化
    • APIs even w/o templates are part of the view; API甚至没有模板都是视图的一部分; eg tastypie or piston 例如tastypiepiston
    • this should also account for the middleware. 这也应该占中间件。

Take advantage of middleware / templatetags 利用中间件 / 模板标签

  • If you need some work to be done for each request, middleware is one way to go. 如果您需要为每个请求完成一些工作,中间件是一种可行的方法。
    • eg adding timestamps 例如,添加时间戳
    • eg updating metrics about page hits 例如,更新有关页面命中的指标
    • eg populating a cache 例如填充缓存
  • If you have snippets of code that always reoccur for formatting objects, templatetags are good. 如果你有代码片段总是重复出现格式化对象,那么templatetags就是好的。
    • eg active tab / url breadcrumbs 例如活动标签/网址面包屑

Take advantage of model managers 利用模型经理

  • creating User can go in a UserManager(models.Manager) . 创建User可以进入UserManager(models.Manager)
  • gory details for instances should go on the models.Model . 实例的血腥细节应该放在models.Model
  • gory details for queryset could go in a models.Manager . 对于血淋淋的细节queryset可以去在models.Manager
  • you might want to create a User one at a time, so you may think that it should live on the model itself, but when creating the object, you probably don't have all the details: 您可能希望一次创建一个User ,因此您可能认为它应该存在于模型本身上,但是在创建对象时,您可能没有所有细节:

Example: 例:

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()

Make use of forms where possible 尽可能使用表格

A lot of boilerplate code can be eliminated if you have forms that map to a model. 如果您有映射到模型的表单,则可以删除许多样板代码。 The ModelForm documentation is pretty good. ModelForm documentation非常好。 Separating code for forms from model code can be good if you have a lot of customization (or sometimes avoid cyclic import errors for more advanced uses). 如果您有大量的自定义(或者有时为了更高级的用途而避免循环导入错误),那么从模型代码中分离表单代码可能会很好。

Use management commands when possible 尽可能使用管理命令

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

if you have business logic, you can separate it out 如果你有业务逻辑,你可以把它分开

  • django.contrib.auth uses backends , just like db has a backend...etc. django.contrib.auth 使用后端 ,就像db有后端......等。
  • add a setting for your business logic (eg AUTHENTICATION_BACKENDS ) 为您的业务逻辑添加setting (例如AUTHENTICATION_BACKENDS
  • you could use django.contrib.auth.backends.RemoteUserBackend 你可以使用django.contrib.auth.backends.RemoteUserBackend
  • you could use yourapp.backends.remote_api.RemoteUserBackend 你可以使用yourapp.backends.remote_api.RemoteUserBackend
  • you could use yourapp.backends.memcached.RemoteUserBackend 你可以使用yourapp.backends.memcached.RemoteUserBackend
  • delegate the difficult business logic to the backend 将困难的业务逻辑委托给后端
  • make sure to set the expectation right on the input/output. 确保在输入/输出上设置期望值。
  • changing business logic is as simple as changing a setting :) 改变业务逻辑就像更改设置一样简单:)

backend example: 后端示例:

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' 

could become: 可能成为:

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

more about design patterns 更多关于设计模式

  • there's already a good question about design patterns 关于设计模式已经有了一个很好的问题
  • a very good video about practical design patterns 关于实用设计模式的非常好的视频
  • django's backends are obvious use of delegation design pattern. django的后端显然使用了委托设计模式。

more about interface boundaries 更多关于界面边界

  • Is the code you want to use really part of the models? 您想要使用的代码是否真的是模型的一部分? -> yourapp.models - > yourapp.models
  • Is the code part of business logic? 代码是业务逻辑的一部分吗? -> yourapp.vendor - > yourapp.vendor
  • Is the code part of generic tools / libs? 代码是通用工具/库的一部分吗? -> yourapp.libs - > yourapp.libs
  • Is the code part of business logic libs? 代码是业务逻辑库的一部分吗? -> yourapp.libs.vendor or yourapp.vendor.libs - > yourapp.libs.vendoryourapp.vendor.libs
  • Here is a good one: can you test your code independently? 这是一个很好的:你可以独立测试你的代码吗?
    • yes, good :) 对很好 :)
    • no, you may have an interface problem 不,你可能有接口问题
    • when there is clear separation, unittest should be a breeze with the use of mocking 当有明显的分离时,使用模拟来进行单元测试应该是轻而易举的
  • Is the separation logical? 分离是否合乎逻辑?
    • yes, good :) 对很好 :)
    • no, you may have trouble testing those logical concepts separately. 不,您可能无法单独测试这些逻辑概念。
  • Do you think you will need to refactor when you get 10x more code? 当你获得10倍以上的代码时,你认为你需要重构吗?
    • yes, no good, no bueno, refactor could be a lot of work 是的,没有好,没有bueno,重构可能是很多工作
    • no, that's just awesome! 不,那太棒了!

In short, you could have 简而言之,你可以拥有

  • 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

or anything else that helps you; 或任何其他帮助你; finding the interfaces you need and the boundaries will help you. 找到您需要接口边界将有助于您。


#6楼

It seems like you are asking about the difference between the data model and the domain model – the latter is where you can find the business logic and entities as perceived by your end user, the former is where you actually store your data. 您似乎在询问数据模型域模型之间的区别 - 后者是您可以找到最终用户所感知的业务逻辑和实体的位置,前者是您实际存储数据的位置。

Furthermore, I've interpreted the 3rd part of your question as: how to notice failure to keep these models separate. 此外,我已将您问题的第3部分解释为:如何注意未能将这些模型分开。

These are two very different concepts and it's always hard to keep them separate. 这是两个非常不同的概念,并且总是很难将它们分开。 However, there are some common patterns and tools that can be used for this purpose. 但是,有一些常用的模式和工具可用于此目的。

About the Domain Model 关于域模型

The first thing you need to recognize is that your domain model is not really about data; 您需要认识到的第一件事是您的域模型并不是真正的数据; it is about actions and questions such as "activate this user", "deactivate this user", "which users are currently activated?", and "what is this user's name?". 它是关于诸如“激活此用户”,“停用此用户”,“当前哪些用户已被激活?”以及“该用户名称是什么?”等操作问题 In classical terms: it's about queries and commands . 用经典术语来说:它是关于查询命令的

Thinking in Commands 在命令中思考

Let's start by looking at the commands in your example: "activate this user" and "deactivate this user". 让我们从查看示例中的命令开始:“激活此用户”和“停用此用户”。 The nice thing about commands is that they can easily be expressed by small given-when-then scenario's: 关于命令的好处是它们很容易通过小的给定时间场景来表达:

given an inactive user 给予非活动用户
when the admin activates this user 管理员激活此用户时
then the user becomes active 然后用户变得活跃
and a confirmation e-mail is sent to the user 并向用户发送确认电子邮件
and an entry is added to the system log 在系统日志中添加一个条目
(etc. etc.) (等等)

Such scenario's are useful to see how different parts of your infrastructure can be affected by a single command – in this case your database (some kind of 'active' flag), your mail server, your system log, etc. 这样的场景对于了解单个命令如何影响基础架构的不同部分(在这种情况下是您的数据库(某种“活动”标志),邮件服务器,系统日志等)非常有用。

Such scenario's also really help you in setting up a Test Driven Development environment. 这样的场景也真正帮助您建立测试驱动开发环境。

And finally, thinking in commands really helps you create a task-oriented application. 最后,思考命令确实可以帮助您创建面向任务的应用程序。 Your users will appreciate this :-) 您的用户会很感激:-)

Expressing Commands 表达命令

Django provides two easy ways of expressing commands; Django提供了两种表达命令的简单方法; they are both valid options and it is not unusual to mix the two approaches. 它们都是有效的选择,并且混合这两种方法并不罕见。

The service layer 服务层

The service module has already been described by @Hedde . @Hedde已经描述了 服务模块 Here you define a separate module and each command is represented as a function. 在这里,您可以定义一个单独的模块,每个命令都表示为一个函数。

services.py services.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

Using forms 使用表格

The other way is to use a Django Form for each command. 另一种方法是为每个命令使用Django表单。 I prefer this approach, because it combines multiple closely related aspects: 我更喜欢这种方法,因为它结合了多个密切相关的方面:

  • execution of the command (what does it do?) 执行命令(它做什么?)
  • validation of the command parameters (can it do this?) 验证命令参数(可以这样做吗?)
  • presentation of the command (how can I do this?) 命令的介绍(我该怎么做?)

forms.py forms.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

Thinking in Queries 在查询中思考

You example did not contain any queries, so I took the liberty of making up a few useful queries. 您的示例不包含任何查询,因此我冒昧地编写了一些有用的查询。 I prefer to use the term "question", but queries is the classical terminology. 我更喜欢使用术语“问题”,但查询是经典术语。 Interesting queries are: "What is the name of this user?", "Can this user log in?", "Show me a list of deactivated users", and "What is the geographical distribution of deactivated users?" 有趣的查询是:“此用户的名称是什么?”,“此用户可以登录吗?”,“显示已停用的用户列表”和“已停用用户的地理分布是什么?”

Before embarking on answering these queries, you should always ask yourself two questions: is this a presentational query just for my templates, and/or a business logic query tied to executing my commands, and/or a reporting query. 在着手回答这些问题之前,您应该总是问自己两个问题:这是一个仅针对我的模板的表示性查询,和/或与执行我的命令和/或报告查询相关的业务逻辑查询。

Presentational queries are merely made to improve the user interface. 仅仅是为了改进用户界面而进行表示性查询。 The answers to business logic queries directly affect the execution of your commands. 业务逻辑查询的答案直接影响命令的执行。 Reporting queries are merely for analytical purposes and have looser time constraints. 报告查询仅用于分析目的,并且具有较宽松的时间限制。 These categories are not mutually exclusive. 这些类别并不相互排斥。

The other question is: "do I have complete control over the answers?" 另一个问题是:“我能完全控制答案吗?” For example, when querying the user's name (in this context) we do not have any control over the outcome, because we rely on an external API. 例如,在查询用户名时(在此上下文中),我们无法控制结果,因为我们依赖于外部API。

Making Queries 制作查询

The most basic query in Django is the use of the Manager object: Django中最基本的查询是使用Manager对象:

User.objects.filter(active=True)

Of course, this only works if the data is actually represented in your data model. 当然,这仅在数据实际表示在数据模型中时才有效。 This is not always the case. 这并非总是如此。 In those cases, you can consider the options below. 在这些情况下,您可以考虑以下选项。

Custom tags and filters 自定义标签和过滤器

The first alternative is useful for queries that are merely presentational: custom tags and template filters. 第一种替代方法对于仅仅是表示的查询非常有用:自定义标记和模板过滤器。

template.html template.html

Welcome, {{ user|friendly_name }}

template_tags.py template_tags.py

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

Query methods 查询方法

If your query is not merely presentational, you could add queries to your services.py (if you are using that), or introduce a queries.py module: 如果您的查询不仅仅是表示性的,您可以向services.py添加查询(如果您正在使用它),或者引入queries.py模块:

queries.py queries.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 

Proxy models 代理模型

Proxy models are very useful in the context of business logic and reporting. 代理模型在业务逻辑和报告的上下文中非常有用。 You basically define an enhanced subset of your model. 您基本上定义了模型的增强子集。 You can override a Manager's base QuerySet by overriding the Manager.get_queryset() method. 您可以通过覆盖Manager.get_queryset()方法来覆盖Manager的基本QuerySet。

models.py models.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

Query models 查询模型

For queries that are inherently complex, but are executed quite often, there is the possibility of query models. 对于本质上复杂但经常执行的查询,可能存在查询模型。 A query model is a form of denormalization where relevant data for a single query is stored in a separate model. 查询模型是非规范化的一种形式,其中单个查询的相关数据存储在单独的模型中。 The trick of course is to keep the denormalized model in sync with the primary model. 当然,技巧是使非规范化模型与主模型保持同步。 Query models can only be used if changes are entirely under your control. 只有在完全由您控制的情况下才能使用查询模型。

models.py models.py

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

The first option is to update these models in your commands. 第一个选项是在命令中更新这些模型。 This is very useful if these models are only changed by one or two commands. 如果仅通过一个或两个命令更改这些模型,这非常有用。

forms.py forms.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()

A better option would be to use custom signals. 更好的选择是使用自定义信号。 These signals are of course emitted by your commands. 这些信号当然是由您的命令发出的。 Signals have the advantage that you can keep multiple query models in sync with your original model. 信号的优势在于您可以使多个查询模型与原始模型保持同步。 Furthermore, signal processing can be offloaded to background tasks, using Celery or similar frameworks. 此外,可以使用Celery或类似的框架将信号处理卸载到后台任务。

signals.py signals.py

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

forms.py forms.py

class ActivateUserForm(forms.Form):
    # see above

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

models.py models.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()

Keeping it clean 保持清洁

When using this approach, it becomes ridiculously easy to determine if your code stays clean. 使用这种方法时,确定代码是否保持干净变得非常容易。 Just follow these guidelines: 请遵循以下准则:

  • Does my model contain methods that do more than managing database state? 我的模型是否包含的方法不仅仅是管理数据库状态? You should extract a command. 你应该提取一个命令。
  • Does my model contain properties that do not map to database fields? 我的模型是否包含不映射到数据库字段的属性? You should extract a query. 您应该提取查询。
  • Does my model reference infrastructure that is not my database (such as mail)? 我的模型是否引用了不属于我的数据库的基础设施(例如邮件)? You should extract a command. 你应该提取一个命令。

The same goes for views (because views often suffer from the same problem). 视图也是如此(因为视图经常遇到同样的问题)。

  • Does my view actively manage database models? 我的观点是否主动管理数据库模型? You should extract a command. 你应该提取一个命令。

Some References 一些参考文献

Django documentation: proxy models Django文档:代理模型

Django documentation: signals Django文档:信号

Architecture: Domain Driven Design 架构:领域驱动设计

你可能感兴趣的:(在django中分离业务逻辑和数据访问)