Effective Django database modeling - django

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.

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.

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

Related

Django:How to perform a query with settings.AUTH_USER_MODEL?

This is my model:
class Company(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,related_name="Company_Owner",on_delete=models.CASCADE,null=True,blank=True)
name = models.CharField(max_length=50,blank=False)
auditor = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='main_auditor',blank=True)
I want to perform a query which will display the list of company to which a particular auditor is associated with...
How to do query with settings.AUTH_USER_MODEL in django?
Any idea anyone?
Please help
You can do it like this using get_user_model():
from django.contrib.auth import get_user_model
User = get_user_model()
first_user = User.objects.first()
first_user.Company_Owner.all() # will get all companies associated with the User
Your model relationships are badly named, change the related_name parameters, since they need to represent that you're fetching Company objects:
class Company(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,related_name="owned_companies",on_delete=models.CASCADE,null=True,blank=True)
name = models.CharField(max_length=50,blank=False)
auditor = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='audited_companies',blank=True)
Then you'll be able, once you have a user (e.g. user = self.request.user) to do:
user.owned_companies.all() # companies for which the user is the owner
user.audited_companies.all() # companies for which the user is a main auditor
user.owned_companies.filter(auditor=user) # companies of which the user is both owner and auditor

Django AUTHENTICATION AND AUTHORIZATION groups and permissions want to create verified users

Hi Djangonauts,
I am new to Django please forgive any silly mistake in logic or code.
Intro:
I am building a web app in which members can write posts on a topic and offer courses on that topic. Example A member can write a blog about doing a wheelie on a bicycle and offer courses on that.
What I want:
I want members who want to offer courses to be verified. Example: The member has to fill a form with their details like...
name, address, and photo ID. Plus pay a charge of $9.99 to get verified. After admin (I in this case) checks if everything is good I will approve them. and then they will be "Verified Members" and be able to offer courses
What I have so far: Right now members can offer courses as there is no verified clause
class Event(models.Model):
user = models.ForeignKey(User, related_name='seller')
post = models.ForeignKey(Post, related_name='course')
price = models.DecimalField(max_digits=6, decimal_places=2)
stock = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(35)])
date = models.DateField()
time_from = models.TimeField()
time_to = models.TimeField()
event_types = (
('1', 'Webinar'),
('2', 'Actual Meet'),
)
event_choice = models.CharField(max_length=1, choices=event_types)
def get_absolute_url(self):
return reverse('posts:single', kwargs={'username': self.user.username,
'slug': self.post.slug})
def __str__(self):
return 'Meet for ' + self.post.title
How I plan to do it: I was planning to add a group in Django's admin AUTHENTICATION AND AUTHORIZATION
Home › Authentication and Authorization › Groups › Add group
Name: Verified
Permissions: Chosen permissions
event| event| Can add event
event| event| Can change event
event| event| Can delete event
Now what do I do from here?: Have I done things right so far, How do I take it from here. Do I create a model called verified and add forms.py to have members verified. How do permissions come in the picture.
My monkey patch (not a part of the question, for #Ojas Kale )
class Contact(models.Model):
user_from = models.ForeignKey(User, related_name='supporter')
user_to = models.ForeignKey(User, related_name='leader')
def __str__(self):
return '{} follows {}'.format(self.user_from, self.user_to)
User.add_to_class('following',
models.ManyToManyField('self', through=Contact, related_name='followers', symmetrical=False))
One way to go about it is adding a is_verified column in the user. There are various ways for doing this. but extending from abstractUser is probably the most straightforward and suitable in your case, since the class django.contrib.auth.models.AbstractUser provides the full implementation of the default User as an abstract model.
in your app_name.models.py create user class like this.
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
is_verified = models.BooleanField(default=False)
in your settingps.py
AUTH_USER_MODEL = 'app_name.User'
notice how app_name is used.
Now you can add as many attributes as you want as well.
By defaul is_verified is set to False, As soon as admin approves (verifies) the user change it to True.
Hope this helps.

django model - fetching user data accross multiple tables

I am writing a django (1.10) website and using allauth for authorisations. I don't want to extend the user model in django - because allauth adds a further layer of complexity to what is already a seemingly convoluted process.
I want to create a model (Custom UserManager?) that will have the following methods:
get_all_subscriptions_for_user(user=specified_user)
get_unexpired_subscriptions_for_user(user=specified_user)
Note: unexpired subscriptions are defined by subscriptions whose end_date > today's date.
This is a snippet of my models.py below
from django.db import models
from django.contrib.auth.models import User
#...
class Subscription(models.Model):
token = models.CharKey()
start_date = models.DateTime()
end_date = models.DateTime()
# other attributes
class UserSubscription(models.Model):
user = models.ForeignKey(User)
subscription = models.ForeignKey(Subscription)
# In view
def foo(request):
user = User.objects.get(username=request.user)
# how can I implement the following methods:
# get_all_subscriptions_for_user(user=specified_user)
# get_unexpired_subscriptions_for_user(user=specified_user)
Ideally, I would like to have a custom user manager, which can fetch this data in one trip to the database - but I'm not sure if I can have a custom user manager without having a custom user model.
[[Aside]]
I'm trying to avoid using a custom model, because it wreaks havoc on the other applications (in my project) which have User as a FK. makemigrations and migrate always barf with a message about inconsistent migration history
You can go with a custom Manager, don't need a UserManager since you are fetching related models:
class UserSubscriptionManager(models.Manager):
def for_user(self, user):
return super(UserSubscriptionManager, self).get_queryset().filter(user=user)
def unexpired_for(self, user):
return self.for_user(user).filter(
suscription__end_date__gt=datetime.date.today() # import datetime
)
in your models:
class UserSubscription(models.Model):
user = models.ForeignKey(User)
subscription = models.ForeignKey(Subscription)
user_objects = UserSubscriptionManager()
this way you can do chain filters in the view, for example:
unexpired_suscriptions = UserSubscription.user_objects().unexpired_for(
user=request.user
).exclude(suscription__token='invalid token')
Try this:
response = []
user_sub = UserSubscription.objects.filter(user=user.pk)
for row in user_sub:
subscription = Subscription.objects.get(pk=row.subscription)
end_date = subscription.end_date
if end_date > timezone.now():
response.append(subscription)

Create Django Table displaying information about users

I am currently using django 1.8 and I'd like to create a more intelligent way to display information about users. Say I have something like this:
class User(models.Model):
name = models.CharField()
class Invitation(models.Model):
inviter = models.ForeignKey(User)
invitee = models.ForeignKey(User)
I want to create a field that is the unique number of user's an inviter has invited. I could see how this could be done with something like set("SELECT invitee FROM INVITATIONS WHERE inviter = 'my-user';"), but if I want this displayed in the admin panel, is there a simple way to present this?
Also, I would want this done for every user, so it feels like there is a simple way to make a field generated for every user in the table.
First, let's setup proper related_name- it'll help reduce a lot of confusion in the code.
class Invitation(models.Model):
inviter = models.ForeignKey(User, related_name="invitation_sent")
invitee = models.ForeignKey(User, related_name="invitation_recv")
With the related_name setup, we can do queries such as
user = User.objects.get(pk=1)
# retrieve all invitation objects sent by this user
user.invitation_sent.all()
# retrieve all invitation objects received by this user
user.invitation_recv.all()
Now we can actually count the number of unique invitations a user has sent out quite easily:
# count number of distinct invitee for user
user.invitation_sent.all().values('invitee').distinct().count()
Next, we can actually count the number of unique users a user has invited in a single database query for all users:
user_list = User.objects.all().annotate(
uniq_inv=Count('invitation_sent__invitee', distinct=True)
)
Each user object returned will have an additional property called uniq_inv which contains the count of unique users the user has invited
for user in user_list:
print(user.name + ' invited ' + user.uniq_inv + ' unique users')
To apply this to the admin interface, you'll need to override get_queryset method:
class MyAdmin(admin.ModelAdmin):
...
list_display = [..., 'uniq_inv']
def uniq_inv(self, obj):
return obj.uniq_inv
uniq_inv.short_description = 'Unique Invitees'
def get_queryset(self, request):
qs = super(MyAdmin, self).get_queryset(request)
qs = qs.annotate(uniq_inv=Count('invitation_sent__invitee', distinct=True))
return qs
You can use annotate, which allows to add calculated fields to a queryset.
Models:
class User(models.Model):
name = models.CharField()
class Invitation(models.Model):
inviter = models.ForeignKey(User,related_name="inviter_user")
invitee = models.ForeignKey(User,related_name="invited_user")
Queryset:
from django.db.models import Count
q = Invitation.objects.annotate(count_invitee=Count('invitee')).all()
Now "count_invitee" field has the number for each invitation object.
If you want to filter invitee from the user side.
For a single user:
User.objects.get(pk=1).invited_user.all.count()
For all users queryset:
User.objects.annotate((count_invitee=Count('invited_user')).all()

Django multiple User profiles/subprofiles

I am trying to create an intranet/extranet with internal/external user-specific profiles, with a common generic profile. I've looked at several answers on this site, but none specifically address what I'm looking to do. Below are the (stripped down) files I have so far.
What's the best way to create a profile model, with subprofiles for each user type? I'm trying not to require a custom authentication backend if at all possible.
https://gist.github.com/1196077
I have a solution I dont Know if its the best but see it:
models.py
from django.db import models
from django.contrib.auth.models import User
class Pollster(models.Model):
"""docstring for Polister"""
user = models.OneToOneField(User, related_name = 'polister', unique=True)
cedule = models.CharField( max_length = 100 )
class Respondent(models.Model):
""" """
born_date = models.DateField( verbose_name=u'fecha de nacimiento' )
cedule = models.CharField( max_length = 100, verbose_name=u'cedula' )
comunity = models.CharField( max_length = 100, verbose_name=u'comunidad')
phone = models.CharField( max_length = 50, verbose_name=u'telefono')
sanrelation = models.TextField( verbose_name =u'Relacion con SAN')
user = models.OneToOneField( User, related_name = 'respondent')
I create a MiddleWare: so
i create middleware.py
from django.contrib.auth.models import User
from encuestas.models import Pollster, Respondent
class RequestMiddleWare(object):
"""docstring for """
def process_request(self,request):
if isPollster(request.user):
request.user.userprofile = Pollster.objects.get( user = request.user.id)
elif isRespondent(request.user):
request.user.userprofile = Respondent.objects.get(user = request.user.id)
return None
def isPollster(user):
return Pollster.objects.filter(user=user.id).exists()
def isRespondent(user):
return Respondent.objects.filter(user=user.id).exists()
and you need to configure settings.py for the middleware:
add to MIDDLEWARE_CLASSES atribute:
'encuestas.middleware.RequestMiddleWare'
encuestas is my_app name
middleware is the Middleware file
RequestMiddleWare is the middleware class
You need a combination of storing additional information about users and model inheritance.
Basically, you'll need the generic User models we all know and either love or hate, and then you need a generic profile model that is your AUTH_PROFILE_MODULE setting.
That profile model will be a top-level model, with model subclasses for internal and extrernal users. You probably don't want an abstract model in this case since you'll need a common profile table to load user profiles from with User.get_profile().
So...I think the major thing you want to change is to make your Associate, External, etc. models inherit from your Profile model.
Please check this excellent article that describes how to inherit from the User class and add your own information. For me, at least, this clearly seems to be the way to go: http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/
Using this method one should easily be able to add multiple user types to their Django application.