当前位置: 首页 > 知识库问答 >
问题:

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

祁通
2023-03-14
    null
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有哪些实施这种办法的良好做法?

共有1个答案

闻昊英
2023-03-14

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

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

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

这样的场景还可以帮助您建立测试驱动的开发环境。

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

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

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
    null
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

您的示例没有包含任何查询,所以我冒昧地编了几个有用的查询。我更喜欢使用术语“question”,但queries是经典术语。有趣的查询有:“这个用户的名字是什么?”、“这个用户可以登录吗?”、“给我看一个停用用户的列表”,以及“停用用户的地理分布是什么?”

在开始回答这些问题之前,你应该经常问自己这个问题,是不是:

  • 仅针对我的模板的演示查询,和/或
  • 绑定到执行我的命令的业务逻辑查询,和/或
  • 报告查询。
User.objects.filter(active=True)

第一种选择对于仅仅是表示性的查询很有用:自定义标记和模板筛选器。

template.html

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

template_tags.py

@register.filter
def friendly_name(user):
    return remote_api.get_cached_name(user.id)
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()方法重写管理器的基查询集。

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

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

class InactiveUserDistribution(models.Model):
    country = CharField(max_length=200)
    inactive_user_count = IntegerField(default=0)
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()

signals.py

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

Forms.py

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

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

Django文档:代理模型

Django文档:信号

体系结构:领域驱动设计

 类似资料:
  • 问题内容: 我正在Django中编写一个项目,我发现文件中有80%的代码。这段代码令人困惑,并且在一段时间之后,我不再了解实际发生的事情。 这是困扰我的事情: 我发现模型级别(应该只负责处理数据库中的数据)在发送电子邮件,使用API​​到其他服务等方面也很丑陋。 另外,我发现在视图中放置业务逻辑也是不可接受的,因为这样很难控制。例如,在我的应用程序中,至少有三种方法来创建的新实例,但从技术上讲,它

  • 在实现访问服务器和与本地数据库交互之后,是时候把事情整合起来了。逻辑步骤如下: 从数据库获取数据 检查是否存在对应星期的数据 如果有,返回UI并且渲染 如果没有,请求服务器获取数据 结果被保存在数据库中并且返回UI渲染 但是我们的commands不应该去处理所有这些逻辑。数据源应该是一个具体的实现,这样就可以被容易地修改,所以增加一些额外的代码,然后把command从数据访问中抽象出来听起来是个不

  • 综述 在多功能的动态web应用程序中测试业务逻辑漏洞需要用非常规手段来思考。如果应用认证机制原先以1、2、3的步骤依次执行的验证身份目的来开发,万一用户从步骤1直接跳到步骤3会发生什么?用更加简单的例子来说,在打开失败、权限拒绝或仅仅500的错误的情况下,应用程序是否依然能够提供访问权限? 可以举出许多例子,但是不变的思想是“跳出常规思维”。这种类型的漏洞无法被漏洞扫描工具探测到,依赖于渗透测试人

  • 因此,每个功能区显然都在数据库中,但它们还需要一些逻辑来确定用户何时获得了功能区。 按照我的编码方式,是一个简单的接口: 是一个抽象类,它实现了接口,避免了方法的定义: 现在,将像这样实现一个特定的功能区: 这段代码工作得很好,表是按照我期望的方式在数据库中创建的(我在本地环境中使用DDL生成)。 问题是,在域对象中编写业务逻辑感觉是错误的。这是好的练习吗?你能提出一个更好的解决方案吗?此外,我不

  • 问题内容: 软件体系结构中的域对象和域服务是什么?我不熟悉它们,或者它们与业务逻辑层有何不同? 问题答案: 不同的人以不同的方式使用这些术语,但这是我的看法: 1)“业务”和“域”大致是同义词。“域”更为通用,因为它不会假设您正在编写业务应用程序。因此,如果我们正在编写科学应用程序或游戏,则可能更喜欢将代码的相关部分称为“域”代码,而不是“业务”代码。因此,在本说明的其余部分中,我将使用“域”,因

  • 1.1  概述 业务逻辑模块主要用于编写业务逻辑,一般包含三个子模块:action(定义action和procedure)、code(业务逻辑的实现,一般使用java实现,有src,dsrc,lib)、fn(函数定义)。 1.2  Action定义 语法: <action name=" n" global="true" log-enabled="true" procedure=" "> <参