Django-具有唯一数据的多个用户

Django-具有唯一数据的多个用户,django,Django,我正在构建一个类似于财产维护应用程序的SaaS。一家物业维护公司会使用我的应用程序来管理多栋建筑和一大堆租户。因此,我们的建筑物和租户都由一家管理公司管理。但是,管理公司可能有多个员工(用户),他们都在管理这些建筑物/租户 由于这是一个SaaS,我希望有一大堆管理公司都使用我的应用程序来管理他们自己的一组建筑/租户。因此,每个管理公司只需要能够查看其数据,而无法访问另一个管理公司的数据 我不知道如何在Django实现这一点,尽管我有一个想法: 可能有一个名为ManagementCompany的模

我正在构建一个类似于财产维护应用程序的SaaS。一家物业维护公司会使用我的应用程序来管理多栋建筑和一大堆租户。因此,我们的建筑物和租户都由一家管理公司管理。但是,管理公司可能有多个员工(用户),他们都在管理这些建筑物/租户

由于这是一个SaaS,我希望有一大堆管理公司都使用我的应用程序来管理他们自己的一组建筑/租户。因此,每个管理公司只需要能够查看其数据,而无法访问另一个管理公司的数据

我不知道如何在Django实现这一点,尽管我有一个想法:

可能有一个名为
ManagementCompany
的模型,所有建筑物和租户都有一个指向该
ManagementCompany
模型的
ForeignKey
字段。该管理公司的员工将通过
ForeignKey
连接到
ManagementCompany
记录。这样,用户只能看到
ManagementCompany
与用户匹配的建筑物/租户

这是一个合理的方法,还是有更好的方法

您当然可以使用

编辑:

鉴于您在对我的原始答案的评论中提到所使用的数据库是MySQL,django租户模式在您的案例中是无用的。如何使用多个数据库和数据库路由器,这样每个公司都可以有单独的数据库,使用数据库路由器,您可以通过它路由数据库请求


这可能是工作过度,但你可能会想出一个巧妙的方法来完成它。

你想过子域吗

自定义子域是为SaaS产品的客户提供定制和区分内容的一种很好的方法,而无需求助于长URL路径


因此,当一家公司注册时,您可以调用_lower()方法的company name,也可以像Slack或Jira一样提示他们选择他们喜欢的方法。我建议您阅读这篇文章

这取决于您希望在SaaS应用程序中存储数据的方式—所有实例使用单个数据库或多个数据库(每个实例都有单独的数据库)。当您想要添加新功能、迁移等时,多数据库方法是一种痛苦。单个数据库更易于管理,但您需要为每个型号添加一组
ForeignKey

对于单个db,您需要:

  • 检测SaaS实例的中间件(按子域、域、端口、自定义url等)
  • 根据SaaS实例返回读/写数据库名称的数据库路由器
  • 就这些。Django将读取/写入单独的数据库

    对于多个db,您需要:

  • 检测SaaS实例的中间件(按子域、域、端口、自定义url等)
  • 因为您可能不想手动向每个模型添加
    ForeignKey
    ,并手动对其进行过滤:

  • 带有
    ForeignKey
    和自定义保存方法的抽象模型,用于自动设置
    ForeignKey
  • 使用自定义
    get\u queryset
    方法的自定义模型管理器,该方法将使用当前SaaS实例过滤所有ORM查询。此管理器应覆盖
    create
    方法,以自动设置
    ForeignKey
    ,用于以下查询:
    Foo.objects.create(**data)
  • 将适用于SaaS实例的每个模型都应该继承自该抽象模型,并且您需要将该模型管理器设置为该自定义模型管理器

    就这些。Django将过滤当前SaaS实例的ORM查询

    示例中间件(使用域模型检查域是否存在,如果不存在,您将获得HTTP404):

    抽象模型示例:

    class SaaSModelAbstract(Model):
        SAAS_FIELD_NAME = 'company'
        company = ForeignKey(Company, null=True, blank=True)
    
        class Meta:
            abstract = True
    
        def save(self, *args, **kwargs):
            from .middleware import get_current_saas_instance
            self.company = get_current_saas_instance()
            super(SaaSModelAbstract, self).save(*args, **kwargs)
    
    模型管理器示例:

    class CurrentSaaSInstanceManager(models.Manager):
        def get_current_saas_instance(self):
            from .middleware import get_current_saas_instance
            return get_current_saas_instance()
    
        def get_queryset(self):
            current_instance = self.get_current_saas_instance()
    
            if current_instance is not None:
                return super(CurrentSaaSInstanceManager, self).get_queryset().filter(
                    **{self.model.SAAS_FIELD_NAME: current_instance})
    
            return super(CurrentSaaSInstanceManager, self).get_queryset()
    
        def create(self, **kwargs):
            current_instance = self.get_current_saas_instance()
    
            if current_instance is not None:
                kwargs[self.model.SAAS_FIELD_NAME] = current_instance
    
            instance = self.model(**kwargs)
            self._for_write = True
            instance.save(force_insert=True, using=self.db)
            return instance
    
    示例模型:

    class FooModel(SaaSModelAbstract):
        # model fields, methods 
    
        objects = CurrentSaaSInstanceManager()
    
    class BarModel(models.Model):
        # model fields, methods
        pass
    
    示例查询:

    FooModel.objects.all() # will return query with all objects for current SaaS instance
    BarModel.objects.all() # will return all objects withoout SaaS filtering
    
    # Create objects for SaaS instance:
    FooModel.objects.create(**data)
    # or:
    foo = FooModel()
    foo.save()
    
    在这两种情况下(单个/多个db),django admin都能正常工作


    我没有发布db router,因为实现非常简单,您所需要的一切都可以在django文档中找到。

    有趣的应用程序,但我使用的是MySQL。这个概念很有趣。@Garfonzo在了解您使用MySQL后编辑了我的答案。编辑后的答案可能适合您的要求。@Garfonzo与Multiple db和db router的用户属于一家公司(该公司基本上拥有自己的一个db实例)无法访问其他公司的数据感谢您的澄清-我认为多个数据库在任何更新和迁移中都会变得非常乏味。这是一篇有趣的文章,但它是否仍然提供了手动访问其他人数据的可能性?例如,如果用户有一个子域
    user1.domain.com
    ,并且他们只是手动输入
    user2.domain.com
    ,那么生成的数据不就是
    user2
    的数据吗?在他们的示例中,它将包括
    user2
    帐户?我的意思是,使用子域的想法很好,但我觉得需要内置更多的安全性,或者我遗漏了什么?嗯,这就是你需要实现身份验证和授权的地方。此外,我并不是要为每个用户创建子域,只是为公司创建子域,而且用户与公司之间有M2M关系(因为您可以同时在这两个领域工作)。假设一个用户已经在company1.domain.com中通过了身份验证,现在如果他创建了一个建筑对象,那么这个建筑将有两个字段,creator=user,company=company。现在,如果你想显示所有的company1建筑,你需要一个简单的查询集Building.objects.filter(company=company)是的,每个公司都有一个子域,其中有多个用户“连接”到一个公司。这似乎是一个非常有趣的方法。我可能走这条路
    
    FooModel.objects.all() # will return query with all objects for current SaaS instance
    BarModel.objects.all() # will return all objects withoout SaaS filtering
    
    # Create objects for SaaS instance:
    FooModel.objects.create(**data)
    # or:
    foo = FooModel()
    foo.save()