Here is my code
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
So I'm kinda confused with what models.DateTimeField(default=timezone.now) do, what does "default" mean here?
And I'm also confused what models.ForeignKey(User, on_delete=models.CASCADE) do, what is "on_delete=models.CASCADE" do and mean?
And is this code ( from django.contrib.auth.models ) a database for users?
models.DateTimeField(default=timezone.now) do, what does "default" mean here?
You can pass a callable to the default=… parameter. When the model object is the created, and there is no value for date_posted, it will call the timezone.now function and use the result as value for the date_posted.
And I'm also confused what models.ForeignKey(User, on_delete=models.CASCADE) do, what is on_delete=models.CASCADE do and mean?
A ForeignKey refers to an object. The question is what to do if the object it is referring to is removed. With on_delete=… [Django-doc] you can specify a strategy. CASCADE means that it will remove the Post(s) from a User, if that User is removed itself.
And is this code ( from django.contrib.auth.models ) a database for users?
These are models defined in the auth app. Django has such app to make it easy to start with a simple user model, but you can decide to impelement your own. It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
I have been using Django for quite a while but never have I thought of this until now.
Currently, I have a project that contains different user levels. Usually, in my past experience, I only developed systems using Django with only two user levels which are superuser and normal/regular user. So my question is what are the effective ways to present these different user levels in the model/database? Here, I'm going to use a school system as an example and also provide some of my initial thoughts on implementing it.
User levels:
Admin (superuser & staff)
Principal
Teacher
Students
Method #1: Add new tables based on each user level
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
user = models.CharfieldField(max_length = 10, unique = True)
class Admin(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
class Pricipal(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
Method #2: Add additional user types attributes in the User model
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
user = models.CharfieldField(max_length = 10, unique = True)
is_superuser = models.BooleanField(default = False)
is_staff = models.BooleanField(default = False)
is_principal = models.BooleanField(default = False)
is_teacher = models.BooleanField(default = False)
is_student = models.BooleanField(default = False
'''
User table in DB:
user | is_superuser | is_staff | is_principal | is_teacher | is_student
'''
My thoughts:
In Method #1, as the built-in User model has two fields, is_staff and is_superuser, Is it possible to implement/change the fields into a SuperUser/Admin table as in the example above? This means that when I create an admin/superuser, I want it to add a new row into the Admin table, instead of adding a new user and updating the user's is_superuser and is_staff fields into 1 in the built-in User model.
In Method #2, the problem with it is that tables with different access privileges are directly connected to it. For example, Salary model (which cannot be accessed by Student user) has a direct link with the User model (contains Student user).
I hope I am able to get some insights and also a proper effective way of implementing this so that to prevent any implementation inconvenience and mistakes in the future. Thank you very much.
I think you are in the right path with method #2. It is lighter, and more straightforward.
I would not use a custom "user-like" model for each permission level. Over-complicated, does not scale, and multiply the number of queries, with no very benefit for your problem. Not your UML schema but its content must guarantee your permission requirements.
If the permission levels are not mutual-exclusive :
from django.db import models
from django.contrib.postgres.fields import ArrayField
class User(AbstractUser):
ADMIN = 0
PRINCIPLE = 1
TEACHER = 2
STUDENT = 3
USER_LEVEL_CHOICES = (
(ADMIN, "Admin"),
(PRINCIPLE, "Principle"),
(TEACHER, "Teacher"),
(STUDENT, "Student"),
)
status = ArrayField(
models.IntegerField(choices=USER_LEVEL_CHOICES, blank=True, default=STUDENT),
)
But you need to have a wider reflexion.
I think you are talking about two separate problems : polymorphism, and permissions
Polymorphism :
Polymorphism is the ability of an object to take on many forms. For a Django model, it can be done with many strategies : OneToOneField -as you mentioned- multi-table inheritance, abstract models, or proxy-models.
Very good resources : this article, and Django doc about model inheritance
This very complex problem all refer to : how much your several forms of a same entity are similar, or different. And which operations are particularly similar or different (data shape, querying, permission, ...etc)
Permissions design :
You can choose among several patterns
Model-oriented permission : A user is granted "add", "view", "edit" or "delete" permission to a Model. This is done in Django with the built-in Permission model, that have a ForeignKey to ContentType
Object-oriented permission : A user is granted "add", "view", "edit" or "delete" permission for each Model instance. Some packages provides this ability, django-guardian for example.
Rule-oriented permission : A user is granted permission to a Model instance through custom logic instead of M2M table. The django rules package provide this kind of architecture.
You can create from AbstractUser (a full User model, complete with fields, including is_superuser and is_staff) a Profile and then, once you have the profile, give the chance of users to create other type of profile (Student, Teacher or Principle) which could have functionalities of its own.
For instances, in your models.py
class Profiles(AbstractUser):
date_of_birth = models.DateField(max_length=128, blank=True, null=True, default=None, verbose_name=_(u'Date of birth'))
principle = models.OneToOneField(Principles, null=True, blank=True, verbose_name=_(u'Principles'), on_delete=models.CASCADE)
teacher = models.OneToOneField(Teachers, null=True, blank=True, verbose_name=_(u'Teachers'), on_delete=models.CASCADE)
student = models.OneToOneField(Students, null=True, blank=True, verbose_name=_(u'Students'), on_delete=models.CASCADE)
class Meta:
db_table = 'profiles'
verbose_name = _('Profile')
verbose_name_plural = _('Profiles')
To that model you can add class methods, such as
def is_teacher(self):
if self.teacher:
return True
else:
return False
Then, your Teachers model could look like this
class Teachers(models.Model):
image = models.FileField(upload_to=UploadToPathAndRename(settings.TEACHERS_IMAGES_DIR), blank=True, null=True, verbose_name=_('Teacher logo'))
name = models.CharField(blank=False, null=False, default=None, max_length=255, validators=[MaxLengthValidator(255)], verbose_name=_('Name'))
street = models.CharField( max_length=128, blank=False, null=True, default=None, verbose_name=_('Street'))
created_by = models.ForeignKey('Profiles', null=True, blank=True, on_delete=models.SET_NULL)
One of the methods that I used in several projects is this (pseudo code):
class User(AbstractUser):
ADMIN = 0
PRINCIPLE = 1
TEACHER = 2
STUDENT = 3
USER_LEVEL_CHOICES = (
(ADMIN, "Admin"),
(PRINCIPLE, "Principle"),
(TEACHER, "Teacher"),
(STUDENT, "Student"),
)
user_level = models.IntgerField(choices=USER_LEVEL_CHOICES)
def lvl_decorator():
def check_lvl(func):
def function_wrapper(self, actor, action_on, *args, **kwargs):
if 'action_lvl' not in action_on: # then action_on is user
if actor.user_lvl < action_on.user_lvl:
return True
return False
else: # then action_on is action of some kind for that user (you can add action_lvl to ... and pas them to this wapper)
if actor.user_lvl < action_on.action_lvl:
return True
return False
return function_wrapper
return check_lvl
Then you can write wrapper function with this logic for any action check if action level is bigger than user level e.g.: if someone wants to change superuser password he/she should be logged-in with level-0-user but for changing normal user's password he/she should be level 0, 1. This logic also can be applied to class, functions, etc actions.
Create base class and then add lvl_decorator to it then inherent from it => this keeps your code super clean and prevents further copy paste.
example of what i mean:
def lvl_decorator():
def check_lvl(func):
def function_wrapper(self, actor, action_on, *args, **kwargs):
if 'action_lvl' not in action_on: # then action_on is user
if actor.user_lvl < action_on.user_lvl:
return True
return False
else:
if actor.user_lvl < action_on.action_lvl:
return True
return False
return function_wrapper
return check_lvl
class BaseClass(type):
def __new__(cls, name, bases, local):
for attr in local:
value = local[attr]
if callable(value):
local[attr] = lvl_decorator()
return type.__new__(cls, name, bases, local)
# in other locations like views.py use this sample
class FooViewDjango(object, ApiView): # don't remove object or this won't work, you can use any Django stuff you need to inherent.
__metaclass__ = BaseClass
def baz(self):
print('hora hora')
Use this base class in any where you want.
I am upgrading a large Django-based app from Django 1.7 app to Django 2.2 and am having a lot of trouble with a permissions-related mixin.
class PrincipalRoleRelation(models.Model):
"""A role given to a principal (user or group). If a content object is
given this is a local role, i.e. the principal has this role only for this
content object. Otherwise it is a global role, i.e. the principal has
this role generally.
user
A user instance. Either a user xor a group needs to be given.
group
A group instance. Either a user xor a group needs to be given.
role
The role which is given to the principal for content.
content
The content object which gets the local role (optional).
"""
:::
user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True, on_delete=models.SET_NULL)
group = models.ForeignKey(Group, verbose_name=_(u"Group"), blank=True, null=True, on_delete=models.SET_NULL)
role = models.ForeignKey(Role, verbose_name=_(u"Role"), on_delete=models.CASCADE)
:::
However, this fails to load during app initialization because User, Group, and Role etc are also apps whose loading is in progress and "populate() is not re-entrant" (so Dango complains)
I tried to work round this by amending the above code to create a sort of "skeleton" class which does not attempt to reference any other apps, e.g. :
app_models_loaded = True
try:
from django.contrib.auth import get_user_model
User = get_user_model()
except:
app_models_loaded = False
if app_models_loaded:
from django.contrib.auth.models import Group
user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True, on_delete=models.SET_NULL)
group = models.ForeignKey(Group, verbose_name=_(u"Group"), blank=True, null=True, on_delete=models.SET_NULL)
role = models.ForeignKey(Role, verbose_name=_(u"Role"), on_delete=models.CASCADE)
:::
Then in manage.py I would define the full mixin class, called say PrincipalRoleRelation2 and overwrite the skeleton class via the code :
from django.contrib import admin
from permissions.models import PrincipalRoleRelation
if admin.site.is_registered(PrincipalRoleRelation):
admin.site.unregister(PrincipalRoleRelation)
admin.site.register(PrincipalRoleRelation, PrincipalRoleRelation2)
However, although this almost seems to work, I am not seeing some of the PrincipalRoleRelation2 attributes, "role" for example, in what I hoped would be the re-mapped PrincipalRoleRelation class with all attributes present.
I feel I am digging myself into an ever deeper hole, and that the above approach is unsound and will never work properly. So any help would be very much appreciated!
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
edit: In response to schillingt's comment, the User class is defined as follows:
class User(AbstractBaseUser): # , PermissionsMixin):
""" Custom user model
Currently just used by the tests for django-permissions
All unique user fields required for a user
NB: Fields that are customizable across multiple identities will be part of a Profile object
"""
# Dont use PermissionsMixin since not using contrib.auth.models.Permissions
# and not using authentication backend perms ... so its only relevant for groups
# ... however it causes user.groups relations name clashes ..
# But we are using the groups part with django-permissions:
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'),
related_name="user_set", related_query_name="user")
is_superuser = models.BooleanField(_('superuser status'), default=False,
help_text=_('Designates that this user has all permissions without '
'explicitly assigning them.'))
username = models.EmailField(_('Email (Username)'), max_length=255, unique=True)
# Make username an email and just dummy in email here so its clearer for user.email use cases
As a solution for circular refferrence, django has an ability to specify ForeignKey (or any other relational field) with string refferrence to related model, instead of importing the actual class.
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
This is imo the recommended way to define related fields.
I am creating my own users, Restaurant and Customer. I have extended the AbstractUser class and then created a OneToOneField field for each user. I am wondering if I need to add the AUTH_USER_MODEL in my settings.py. And also wondering what that does exactly...
What I was planning on doing was adding to my settings.py:
AUTH_USER_MODEL = 'myapp.Customer','myapp.Restaurant'
Do I have the right idea here?
My models.py:
class User(AbstractUser):
is_restaurant = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
class Restaurant(models.Model):
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
restaurant_name = models.CharField(max_length=50)
def __str__(self):
return self.restaurant_name
class Customer(models.Model):
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
address = models.CharField(max_length=200)
def __str__(self):
return self.user.get_full_name()
No. AUTH_USER_MODEL isn't expecting a tuple, so this won't work.
In any case, Restaurant and Customer are not your user model; your subclassed User is. That's what you should be putting in that setting.
I would suggest create single user table instead of three different tables and add type as restaurant, customer, admin etc. And add only one table into settings file. this won't lead any further issues authentication etc. Having single user table is always robust. In your case having three tables seems not good to maintain.
========== UPDATE ===========
Create model for user named as CustomUser (or name which you feel better) and extends to User Model of Django using AbstractBaseUser,PermissionsMixin. like
class CustomUser(AbstractBaseUser): have all fields which user table has already. and add your desired table to bifurcate type of restaurant and
customer have type field with choices option.
For further help you can check section https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#substituting-a-custom-user-model
In Django Project I am using two databases, first is MySQL second is PostgreSQL, in PostgreSQL I have only geometric values but I need users too, so In models I have:
from django.conf import settings
from django.contrib.gis.db import models
class geo(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
location = models.PointField()
Everything is working fine but when I try to save, user field shows an error user instance with id 1L does not exist So what could be the answer? Is problem coming from the router.py file?
The error comes from this line :
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
Here you are defining default=1 so when you are not assigning any user instance to user field of geo model, default then user field is taking integer default value, but it is expecting a user instance.
There are two solutions :
At the time of creation of new instance/record of geo model, add a user instance to it.
Or
Use blank=True, null=True in your geo model,like this:
class geo(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True)
location = models.PointField()
I would personally recommend the second method. Thanks.