Django using get_user_model vs settings.AUTH_USER_MODEL - python

Reading the Django Documentation:
get_user_model()
Instead of referring to User directly, you should reference the user
model using django.contrib.auth.get_user_model(). This method will
return the currently active User model – the custom User model if one
is specified, or User otherwise.
When you define a foreign key or many-to-many relations to the User
model, you should specify the custom model using the AUTH_USER_MODEL
setting.
I'm confused with the above text. Should I be doing this:
author = models.ForeignKey(settings.AUTH_USER_MODEL)
or this...
author = models.ForeignKey(get_user_model())
Both seem to work.

Using settings.AUTH_USER_MODEL will delay the retrieval of the actual model class until all apps are loaded. get_user_model will attempt to retrieve the model class at the moment your app is imported the first time.
get_user_model cannot guarantee that the User model is already loaded into the app cache. It might work in your specific setup, but it is a hit-and-miss scenario. If you change some settings (e.g. the order of INSTALLED_APPS) it might very well break the import and you will have to spend additional time debugging.
settings.AUTH_USER_MODEL will pass a string as the foreign key model, and if the retrieval of the model class fails at the time this foreign key is imported, the retrieval will be delayed until all model classes are loaded into the cache.

New since Django 1.11.
Since Django 1.11 you can use get_user_model() in both cases! So if you don't want to bother about it further, just take it.
"in both cases" means: if you need the user model for accessing its attributes, as well as if you want to define a ForeignKey/ManyToMany-relation.
From the changelog:
get_user_model() can now be called at import time, even in modules that define models.
so... is there still a reason to use settings.AUTH_USER_MODEL? Well, the docs still recommend the settings.AUTH_USER_MODEL (which is a string) for defining relations, but without giving an explicit reason. Might be beneficial for performance, but doesn't seem to matter much.
Code example:
from django.db import models
from django.contrib.auth import get_user_model
...
...
user = models.ForeignKey(
get_user_model(),
null=True, # explicitly set null, since it's required in django 2.x. - otherwise migrations will be incompatible later!
...
)

Since Django 1.11, get_user_model() actually uses settings.AUTH_USER_MODEL:
def get_user_model():
"""
Return the User model that is active in this project.
"""
try:
return django_apps.get_model(settings.AUTH_USER_MODEL, require_ready=False)
except ValueError:
raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'")
except LookupError:
raise ImproperlyConfigured(
"AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL
)

settings.AUTH_USER_MODEL returns a string (the location of the User model) e.g. user_accounts.User.
get_user_model() returns the ACTUAL model class, not a string.
So in cases where you need the User model, use get_user_model(). If you need it's location (module.model as a string), use the settings.AUTH_USER_MODEL.

as i read from book "Two Scoops of Django 3.x"
Using get_user_model():
This is bad, as it tends to create import loops.
bad example:
owner = models.OneToOneField(get_user_model()) -> BAD DONT DO IT

A way to fallback to the default user model if AUTH_USER_MODEL is not set:
from django.conf import settings
from django.contrib.auth.models import User
USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', User)

Related

How to use both Django Auth and DRF Token auth?

I am building a Django web app where I want two things:
Use Django's in-built User model for Django's Admin app usage (store owner)
Use DRF's Token Auth on a custom User model that I will be naming "Customer" (store customer)
How do I keep both the Authentication systems for the above stated purposes. From what I have read every one asks to override the User model but I don't want to do that. Instead I want to keep both. What strategy should I take up?
PS: It might be me, but I am not able to find any solution for this in DRF's Documentation. If there is please do point me in the right direction.
Django does provide an option for using a custom user model. But you can have one and only one user model.
The process is fairly simple, create your own model inheriting django.contrib.auth.models.AbstractUser and specify the AUTH_USER_MODEL settings variable. The Django admin works pretty well with the "custom user model" concept. The DRF token model also uses the settings.AUTH_USER_MODEL var for its OneToOne relation. So, this is can be a viable solution.
To separate out the user types, you can either use a char field with choices representing the user type or use the existing Django groups mechanism. But, in both cases, you could still only have one user model.
For any specific details, you can have OneToOne relations with different models storing extra info.
Something like this would do,
from django.contrib.auth.models import AbstractUser
from model_utils.choices import Choices # Useful package
from django.utils.functional import cached_property
class User(AbstractUser):
USER_TYPES = Choices(
("store_owner", "Store Owner"),
("customer", "Customer"),
)
...hack...
user_type = models.CharField(choices=USER_TYPES)
...hack...
#cached_property
def is_store_owner(self):
return (
self.user_type == self.USER_TYPES.store_owner
and self.store_owner is not None
)
#cached_property
def is_customer(self):
return (
self.user_type == self.USER_TYPES.customer
and self.customer is not None
)
class StoreOwner(models.Model):
user = models.OneToOneField(
"yourapp.User",
related_name="store_owner",
)
# ...extra store owner details...
class Customer(models.Model):
user = models.OneToOneField(
"userapp.User",
related_name="customer",
)
# ...extra customer details...

from django.contrib.auth.models import User vs defining an user ForeignKey as owner = models.ForeignKey('auth.User')

How come to import User from the auth package that comes with Django, I need to do:
from django.contrib.auth.models import User
while to refer to the same User model to create a ForeignKey, I need to do:
owner = models.ForeignKey('auth.User', related_name='snippets')
and not 'auth.models.User'?
I am following the example here : http://www.django-rest-framework.org/tutorial/4-authentication-and-permissions
This is due to Django's "lazy relationships". You can see the code for this here. You don't need to specify the exact module, in this case models because anything inheriting from Django's models.Model will fire off a class_prepared signal once it's initialised and up to this point, it's still only a string.
Just provide the app and model, or just the model name if it's in the same app.

Custom User Model in admin on Django 1.5

I'm using a custom user model myapp.MyUser in a Django 1.5 app. I updated my settings as documentation says to do, AUTH_USER_MODEL = "myapp.MyUser". As MyUser extends AbstractUser, I created the admin with this code:
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
admin.site.register(get_user_model(), UserAdmin)
And it works fine, except the creation form. When I try to create a new user, the following exception is raised:
DatabaseError at /admin/core/user/add/
(1146, "Table 'mydatabase.auth_user' doesn't exist")
The full traceback can be found here.
Digging out Django's source code it looks like UserCreationForm - which is used by UserAdmin - references django's built-in auth.User directly, instead of use get_user_model.
Can it be the problem? Why everything references myapp.MyUser, including admin's auth and the change form, except the creation?
As you say, the UserCreationForm references auth.User directly. The Django docs on custom users and built in auth forms state that you must rewrite it for any custom user model.
UserCreationForm
Depends on the User model. Must be re-written for any custom user model.
There is a related ticket Ticket 20086, which was initially closed as invalid, because the user creation form is working as documented.
There is a full example on the same page, which shows you how to rewrite the forms and register a custom user model in the admin.
If you don't mind to store your custom model in a table called auth_user you can simply set this in Meta and this solves auth_user table doesn't exist. All you need is:
class MyUser(AbstractUser):
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
db_table = 'auth_user'

Making first name, last name a required attribute rather than an optional one in Django's auth User model

I'm trying to make sure that the first name and last name field are not optional for the auth User model but I'm not sure how to change it. I can't use a sub class as I have to use the authentication system.
Two solutions I can think of are:
to put the name in the user profile but it's a little silly to have a field that I can't use correctly.
To validate in the form rather than in the model. I don't think this really fits with Django's philosophy...
For some reason I can't seem to find a way to do this online so any help is appreciated. I would have thought that this would be a popular question.
Cheers,
Durand
Simplest solution
Just create a custom UserRegisterForm which inherits the django's default UserCreationForm.
The first_name and last_name are already attributes of django's default User. If you want to make them as required fields, then recreate those fields as forms.CharField(...).
Now use your own User register form.
# Contents usersapp/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
# Inherit Django's default UserCreationForm
class UserRegisterForm(UserCreationForm):
first_name = forms.CharField(max_length=50) # Required
last_name = forms.CharField(max_length=50) # Required
# All fields you re-define here will become required fields in the form
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2']
I would definitely go with validating on the form. You could even go as far as having more form validation in the admin if you felt like it.
Thanks Mbuso for the advice. Here's my full implementation for those who are interested. Before taking a look at the source, let's see what it looks like:
I've implemented a profile model, but this will work just fine without it.
from django.core.exceptions import ValidationError
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.models import User
from apps.profiles.models import Profile
# Define an inline admin descriptor for Profile model
# which acts a bit like a singleton
class UserProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = 'profile'
class MyUserChangeForm(UserChangeForm):
def clean_first_name(self):
if self.cleaned_data["first_name"].strip() == '':
raise ValidationError("First name is required.")
return self.cleaned_data["first_name"]
def clean_last_name(self):
if self.cleaned_data["last_name"].strip() == '':
raise ValidationError("Last name is required.")
return self.cleaned_data["last_name"]
# Define a new User admin
class MyUserAdmin(UserAdmin):
form = MyUserChangeForm
inlines = UserProfileInline,
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
Note: If you do implement a profile model, recommend using UserProfile as the name, since is this is what's in the documentation and seems to be the standard (this part was developed before I started working on the project). If you're using Django 1.5 or higher, skip UserProfile all together and extend the User model.
The Django way of extending the basic User model is through user profiles: see "Storing additional information about users".
If it does not fit your needs, django.contrib.auth is just a Django application, I would simply fork it. As long as you abide by the original interface, I think you will be out of trouble.
Another option is Pinax - it has OpenId support built in, you can use it with your own openid provider. OpenId native support is a battery I really miss in Django.

How to add custom permission to the User model in django?

in django by default when syncdb is run with django.contrib.auth installed, it creates default permissions on each model... like foo.can_change , foo.can_delete and foo.can_add. To add custom permissions to models one can add class Meta: under the model and define permissions there, as explained here https://docs.djangoproject.com/en/4.1/topics/auth/customizing/#custom-permissions
My question is that what should I do if I want to add a custom permission to the User model? like foo.can_view. I could do this with the following snippet,
ct = ContentType.objects.get(app_label='auth', model='user')
perm = Permission.objects.create(codename='can_view', name='Can View Users',
content_type=ct)
perm.save()
But I want something that plays nicely with syncdb, for example the class Meta under my custom models. Should I just have these in class Meta: under UserProfile since that is the way to extend the user model. but is that the RIGHT way to do it? Wouldn't that tie it to UserProfile model?
You could do something like this:
in the __init__.py of your Django app add:
from django.db.models.signals import post_syncdb
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import models as auth_models
from django.contrib.auth.models import Permission
# custom user related permissions
def add_user_permissions(sender, **kwargs):
ct = ContentType.objects.get(app_label='auth', model='user')
perm, created = Permission.objects.get_or_create(codename='can_view', name='Can View Users', content_type=ct)
post_syncdb.connect(add_user_permissions, sender=auth_models)
I don't think there is a "right" answer here, but i used the exact same code as you except i changed Permission.objects.create to Permission.objects.get_or_create and that worked find to sync with syncdb
An updated answer for Django 1.8. The signal pre_migrate is used instead of pre_syncdb, since syncdb is deprecated and the docs recommend using pre_migrate instead of post_migrate if the signal will alter the database. Also, #receiver is used to connect add_user_permissions to the signal.
from django.db.models.signals import pre_migrate
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import models as auth_models
from django.contrib.auth.models import Permission
from django.conf import settings
from django.dispatch import receiver
# custom user related permissions
#receiver(pre_migrate, sender=auth_models)
def add_user_permissions(sender, **kwargs):
content_type = ContentType.objects.get_for_model(settings.AUTH_USER_MODEL)
Permission.objects.get_or_create(codename='view_user', name='View user', content_type=content_type)
This is a bit hacky but mentioning it here anyway for reference.
My site has a generic model called Setting, which stores various settings concerning the site I want certain users to be able to edit, without needing to go through me the developer (like registration limit, or an address, or the cost of items, etc).
All the permissions that don't nicely map onto other models (eg "Send Password Reminder Email to Student", "Generate Payment Reconciliation Report", "Generate PDF Receipt"), which really just relate to pages that get viewed in the admin area, get dumped onto this Setting model.
For example, here's the model:
class Setting(models.Model):
name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(editable=False)
description = models.TextField()
value = models.TextField()
class Meta:
#for permissions that don't really relate to a particular model, and I don't want to programmatically create them.
permissions = (
("password_reminder", "Send Password Reminder"),
("generate_payment_reconciliation_report", "Generate Payment Reconciliation Report"),
("generate_pdf_receipt", "Generate PDF Receipt"),
)
Do each of those settings strictly relate to the Setting model? No, which is why I said this is a bit hacky. But it is nice that I can now just dump all those permissions here, and Django's migration commands will take care of the rest.

Categories

Resources