0
votes

I want to model the following application: an Owner has different Shops and each Shop has some Customers and some Employees working for that Shop; the same Employee can work in different Shops belonging to the same Owner, but also in Shops belonging to different Owners. Only Owner and Employee can login into the system, Customer can't login. I created the following models and added users to different Groups (using Django Auth system and version 1.6.2 which allows custom user models), but I'm concerned with the number of query that the application is doing and I'm really not sure about the modeling. The big difficulty is that, if the Owner has various Shops, when the Owner login into the system he needs to choose which Shop is working with, also to be able to add the related Employees and Customers (only the Owner of the Shop can add Employees and Customers)

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

Now, when the Employee or the Owner login into the system with his email, the app needs to show a select box with the available shops, and the choice of the user need to pass to every view of the application: how do I do that? I suppose can't be a POST since I'll have other forms in the app, should be a GET request, but on every request I need to verify is the Shop belongs to the Owner or to the Employee (increasing number of queries). I already developed a big part of the application (order form for example) but I'm coming back to the beginning; I don't know if all the models I've done should be related to the Shop or to the Owner. Any advice is appreciated. Thanks.

2

2 Answers

0
votes

I now my example can be unperfect but i think it will clarify how you should use Django for this. (Also read this: 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')

Now you can find all data by using you logged in user (use sessions) in view:

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

I have solved a similar problem using sessions and custom middleware based on Django's authentication middleware:

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

Then just add ShopMiddleware to your middleware classes, and request.shop will always point to the current shop if one is selected.

In my case I have also written a view wrapper similar to login_required that redirects to a page that allows selection of a shop whenever one is required and not selected. Take a look at login_required's source code for a good pointer in the right direction.

EDIT: You still need to select a shop, so write a view that presents the user with the right options, and let it call switch_shop(request, shop). If the shop is a valid shop for the current user, the session will be set to that shop until it is changed or the user logs out.