I'm modeling a quite complex system in Django. I will post here only the relevant part of it and I will show simplified use cases diagrams to better express my ideas.
I basically have 2 type of users: Seller and Customer.
A Seller "acquires" a Customer, that means that the seller now has
the customer's personal information and can interact with him/her.
A seller cannot interact with a customer that he didn't acquire.
A Seller creates a model hierarchy of related objects (each model in a sublevel is connected with a Foreign key to its parent)
A Seller shares the created box object and all its related objects with some customers
The Authorized Customers can:
Questions:
With Django, you can create groups to class users and assign permissions to each group so when creating users, you can just assign the user to a group and, in turn, the user has all the permissions from that group. To create a group, you need the Group model from django. contrib. auth.
By default, Django automatically gives add, change, and delete permissions to all models, which allow users with the permissions to perform the associated actions via the admin site. You can define your own permissions to models and grant them to specific users.
1. No matter what strategy you pick, or what is your business model, always use one, and only one Django model to handle the authentication. You can still have multiple user types, but generally speaking it's a bad idea to store authentication information across multiple models/tables.
Initially, there are two types of users in a Django application: superuser accounts and regular users. Superusers have every attribute and privilege of regular accounts, but also have access to the Django admin panel, a powerful application that we'll explore in detail in a future article.
All users are not created equal. django-relationship creates arbitrary relations between arbitrary users, which is likely not what you want. What you really want is to constrain this relationship to be strictly Seller -> Customer
# This example assumes that both customers and sellers have user table entries.
from django.contrib.auth.models import User
class Customer(User): pass
class Seller(User):
acquired_customers = ManyToManyField(Customer,
related_name="acquired_sellers")
def acquire(customer):
" A convenience function to acquire customers "
self.acquired_customers.add(customer.id)
You can use custom "through" model of a ManyToManyField
to add extra information that you want to track. In this case, we're adding the seller, and an automatic timestamp when was shared. This allows you to do things like show the products that have been shared with you sorted by when they were shared, along with the name of the seller that sent it to you.
# Install mptt for heirararchical data.
from mptt.models import MPTTModel
class Box(MPTTModel):
" Nestable boxen for your Items "
owner = ForeignKey(Seller)
title = CharField(max_length=255)
shared_with = ManyToManyField(Customer,
related_name='boxes_sharedwithme', through=SharedBox)
class Item(Model):
" A shareable Item "
box = ForeignKey(Box)
title = CharField(max_length=255)
class SharedBox(Model):
" Keeps track of who shares what to whom, and when "
when = DateTimeField(auto_now_add=True)
box = ForeignKey(Box)
seller = ForeignKey(Seller)
customer = ForeignKey(Customer)
#----------------------------
# share an Item with a Customer
def share_item(request, box_id, customer_id, **kwargs):
# This will fail if the user is not a seller
seller = request.user.seller
# This will fail if the seller is not the owner of the item's box
box = Box.objects.get(
id=box_id, owner=seller)
# This will fail if the seller has not acquired the customer
customer = Customer.objects.get(
id=customer_id, acquired_sellers=seller)
# This will share the item if it has not already been shared.
SharedBox.objects.create_or_update(
box=box, seller=seller, customer=customer)
return HttpResponse("OK")
Implicit permissions are "business logic" which means you'll likely need to implement it yourself. Fortunately, Django's permission system is pluggable so that you can add your own rules that recurse up the hierarchy to check permissions. Or, you can create custom Managers that add the appropriate rules to the the query wherever used.
from django.db.models import Manager
from django.db.models.query import EmptyQuerySet
class ItemManager(Manager):
def visible(user):
iqs = self.get_query_set()
oqs = EmptyQuerySet()
# add all the items a user can see as a seller
try: oqs |= iqs.filter(box__owner=user.seller)
except Seller.DoesNotExist: pass
# add all the items a user can see as a customer
try: oqs |= iqs.filter(box__shared_with=user.customer)
except Customer.DoesNotExist: pass
# return the complete list of items.
return oqs
class Item(Model): objects = ItemManager()
class ItemListView(ListView):
model = Item
def get_queryset(request):
return self.model.objects.visible(request.user)
If this needs to be super granular or per-user then django-guardian is the way to go. If permissions are rule-based, then you may be better off with a simple field in order to keep the complexity of your database queries down.
class Property(Model):
title = CharField(max_length=255)
units = CharField(max_length=10,
choices=UNIT_TYPES, null=True, blank=True)
# -- A simple field that toggles properties for all users
class ItemProperty(Model):
item = ForeignKey(Item)
property = ForeignKey(Property)
value = CharField(max_length=100)
customer_viewable = BooleanField(default=False)
customer_editable = BooleanField(default=False)
# -- A simple field that defines user classes who can view/edit
from django.contrib.auth.models import Group
class ItemProperty(Model):
item = ForeignKey(Item)
property = ForeignKey(Property)
value = CharField(max_length=100)
viewable_by = ForeignKey(Group)
editable_by = ForeignKey(Group)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With