【问题标题】:Effective Django database modeling有效的 Django 数据库建模
【发布时间】:2014-03-09 19:22:03
【问题描述】:

我想为以下应用建模:一个 Owner 有不同的 Shop,每个 Shop 都有一些 Customer 和一些为该 Shop 工作的员工;同一员工可以在属于同一业主的不同店铺工作,也可以在属于不同业主的店铺工作。 只有所有者和员工可以登录系统,客户不能登录。 我创建了以下模型并将用户添加到不同的组(使用 Django Auth 系统和允许自定义用户模型的 1.6.2 版),但我担心应用程序正在执行的查询数量,我真的不确定关于建模。 最大的难点在于,如果Owner有多个Shop,当Own登录系统时,需要选择合作的Shop,还要能够添加相关的Employees和Customers(只有Shop的Owner可以添加员工和客户)

from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin

class CustomUser(AbstractBaseUser, PermissionsMixin):

    email = models.CharField(max_length=254,
        unique=True)
    firstname = models.CharField(max_length=64)
    lastname = models.CharField(max_length=64)
    ...
    objects = CustomUserManager()

    USERNAME_FIELD = 'email'
    ...        

class Shop(models.Model):
    name = models.CharField(...)
    city = ...
    address = ...

class Customer(models.Model):
    shop = models.ForeignKey(Shop)
    ...

class Employee(CustomUser):
    shops = models.ManyToManyField(Shop)
    ...

class Owner(CustomUser):
    shops = models.ManyToManyField(Shop)
    ...

现在,当员工或所有者使用他的电子邮件登录系统时,应用程序需要显示一个包含可用商店的选择框,并且用户的选择需要传递到应用程序的每个视图:如何我这样做?我想不能是 POST,因为我将在应用程序中有其他表单,应该是 GET 请求,但是在每个请求中我需要验证商店是属于所有者还是属于员工(查询数量增加) .我已经开发了应用程序的很大一部分(例如订单),但我又回到了起点;我不知道我做的所有模型都应该与Shop相关还是与Owner相关。 任何建议表示赞赏。谢谢。

【问题讨论】:

    标签: django django-models django-templates


    【解决方案1】:

    我已经使用基于 Django 的身份验证中间件的会话和自定义中间件解决了类似的问题:

    shop/middleware.py

    from django.utils.functional import SimpleLazyObject
    from <appname> import shop
    
    def get_shop(request):
        if not hasattr(request, '_cached_shop'):
            request._cached_shop = shop.get_shop(request)
        return request._cached_shop
    
    
    class ShopMiddleware(object):
        def process_request(self, request):
            assert hasattr(request, 'session'), "The Shop middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
    
            request.shop = SimpleLazyObject(lambda: get_shop(request))
    

    shop/__init__.py

    from django.core.exceptions import ObjectDoesNotExist
    from <appname>.shop.models import Shop
    
    SHOP_SESSION_KEY = '_session_shop_id'
    
    def get_shop(request):
        try:
            shop_id = request.session[SHOP_SESSION_KEY]
            shop = Shop.objects.get(id=shop_id)
            return shop
        except (KeyError, ObjectDoesNotExist):
            return None
    
    def switch_shop(request, shop):
        if not isinstance(request.user, CustomUser):
            request.session[SHOP_SESSION_KEY] = None
        if request.user.shops.filter(id=shop.id).exists():
            request.session[SHOP_SESSION_KEY] = shop.id
    

    然后只需将ShopMiddleware 添加到您的中间件类中,如果选择了一个,request.shop 将始终指向当前商店。

    在我的例子中,我还编写了一个类似于login_required 的视图包装器,它重定向到一个页面,该页面允许在需要和未选择时选择商店。查看login_requiredsource code 以获得正确方向的良好指针。

    编辑:您仍然需要选择一家商店,因此编写一个向用户提供正确选项的视图,并让它调用switch_shop(request, shop)。如果商店是当前用户的有效商店,则会话将设置为该商店,直到它被更改或用户注销。

    【讨论】:

    • 我认为使用会话是一个很好的建议;我试图实现你的代码,它似乎工作,因为我没有错误,但 Shop 总是 None (在模板中使用 {{ request.shop }});如何“选择”店铺?应用程序的每个视图都需要一个选定的商店。
    • @Thomas 是的,您仍然需要更改当前商店。我已经用一种方法更新了我的答案。
    • 谢谢@knbk,我已经完成了选择商店的视图并且会话存储了值。最后一个问题:每次用户登录时,会话都是空的,所以我需要显示视图。我想做一个函数来检查商店是否被选中,但是这个函数应该在应用程序的每个视图中调用:我该怎么做?
    【解决方案2】:

    我现在的示例可能并不完美,但我认为它将阐明您应该如何为此使用 Django。 (另请阅读:https://docs.djangoproject.com/en/1.6/topics/db/managers/

    class ShopsUser(AbstractBaseUser, PermissionsMixin):
    
        email = models.CharField(max_length=254,
            unique=True)
        firstname = models.CharField(max_length=64)
        lastname = models.CharField(max_length=64)
        ...
        objects = CustomUserManager()
    
        USERNAME_FIELD = 'email'
        ...
        priviledge_flag = models.CharField(choices=(('o', 'owner'), ('e', 'employe'), ('c', 'customer'))
    
    class Customer(models.Model):
        shop = models.ForeignKey(Shop)
    
    class Shop(models.Model):
        customers = models.ForeignKey(Customer, related_name='shops')
        admins = models.ManyToMany(ShopsUser, related_name='managed_shops')
    

    现在您可以在视图中使用您登录的用户(使用会话)查找所有数据:

    class SomeView(View):
        def get(self, *args, **kwargs):
            admin = self.request.user
            all_singed_in_admin_shops = admin.managed_shops.all()
            first_shop = all_singed_in_admin_shops[0]
            first_shop_customers = first_shop.customers.all()
    

    【讨论】:

    • 试图理解你的建议,但是每个商店只有一个所有者,所以我不明白 admins = models.ManyToMany(ShopsUser) 这行。您还使用 priviledge_flag 来避免将用户添加到组,我认为这没关系。如果客户有一个 FK 要购物,为什么我需要在 Shop 模型中放入行 customers = models.ForeignKey(Customer, related_name='shops') ?谢谢
    • 您不需要 Shop.customers 引用(无论如何应该是多对数),因为 Customer 相关经理已经涵盖了这一点。您可以通过shop.customer_set.all() 获得一家商店的所有客户
    猜你喜欢
    • 2011-08-04
    • 1970-01-01
    • 2020-09-11
    • 2012-02-23
    • 2016-08-17
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 2015-06-21
    相关资源
    最近更新 更多