I would like to access to a model related to a user in User. I know that it's possible to get the username or the name of the user using: request.user.get_username()
model.py
class Profile(models.Model):
profile_user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
profile_note = models.CharField(max_length=30,...)
Is there any method to take the related model field without a query?
Example: request.user.profile.profile_note
If you want request.user to always have .profile available without an additional query you can write your own authentication backend that uses select_related when looking up the user so that there is only 1 database query
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
class ProfileBackend(ModelBackend):
def get_user(self, user_id):
UserModel = get_user_model()
try:
return UserModel._default_manager.select_related('profile').get(pk=user_id)
except UserModel.DoesNotExist:
return None
settings.py
AUTHENTICATION_BACKENDS = [
'app.backends.ProfileBackend',
]
Now when request.user is loaded from the DB, the profile will be loaded in the same query. You can now access request.user.profile.profile_note without any additional queries
Related
I am recently working in a django project with a SQLServer database. I already connected the database with SQLServer, and I want to make an authentication system for a table I have in that database.
I know django comes with a built-in authentication system, but there is no way to tell django to use a specific table in the database to make the authentication, it just seems to look for users in the default admin page.
Is there any way for django to look for data inside a specific table in a SQLServer database and validate the information put by an user?
You can do it by implementing you own user model and then telling django how to authenticate the user. Your model should look something like this:
class Users(models.Model):
id = models.IntegerField(primary_key=True)
is_active = models.IntegerField(default=1)
date_joined = models.DateTimeField(default=timezone.now)
last_login = models.DateTimeField(default=timezone.now)
username = models.CharField(max_length=30, unique=True)
password = models.CharField(max_length=30)
#property
def is_authenticated(self):
return True
You can add extra fields, but those are required by django. The property is_authenticated shall always return true, as defined in the documentation.
Next step is to define how will your login authenticate. Create a file name backends.py anywhere in your project and inside of it declared two methods: authenticate and get_user, it should something like this:
from django.contrib.auth.backends import ModelBackend
from users.models import Users
from django.contrib.auth.hashers import *
from login.util import *
class PersonalizedLoginBackend(ModelBackend):
def authenticate(self, request=None, username=None, password=None, **kwars):
#Here define you login criteria, like encrypting the password and then
#Checking it matches. This is an example:
try:
user = Users.objects.get(username=username)
except Users.DoesNotExist:
return None
if check_password(password, user.password):
return user
else:
return None
def get_user(self, user_id):
#This shall return the user given the id
from django.contrib.auth.models import AnonymousUser
try:
user = Users.objects.get(id=user_id)
except Exception as e:
user = AnonymousUser()
return user
Now you need to tell django that where is the backends located, on your settings.py:
AUTHENTICATION_BACKENDS = (
# ... your other backends
'path.to.backends.PersonalizedLoginBackend',
)
From there you should be able to do your login like normal, first authenticate and then use do_login function.
Read more details in here:
https://docs.djangoproject.com/en/2.2/topics/auth/customizing/
I am using a custom user model with builtin and custom authentication backends. the default for username-password login and the custom one for uid based login The login is working fine but the 'user' (Other than superuser) is not available in the template.Nor is the last_login getting updated in database. Here is my code
backend.py
from support.models import CustomUser
class UsernameIdModelBackend(object):
def authenticate(self, request, uid=None):
try:
user= CustomUser.objects.get(uid=uid)
return user
except CustomUser.DoesNotExist:
return None
def get_user(self, user_id):
try:
return CustomUser.objects.get(pk=user_id)
except CustomUser.DoesNotExist:
return None
models.py
from django.db import models
# Create your models here.
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
uid = models.CharField(max_length=50)
Where did i go wrong..
EDIT :
I was using if user.is_authenticated before the 'user'. After removing the if condition i am seeing AnonymousUser.
I have a model linked to Django User model but when I try saving to that model using User instance, it says 'User' object has no attribute 'mymodel_set'
My models.py:
from django.contrib.auth.models import User
from django.db import models
class MyModel(models.Model):
user = models.OneToOneField(User, blank=True, null=True, related_name='mymodel')
name = models.CharField(max_length=14, blank=True, null=True)
My views.py:
from django.contrib.auth.models import User
from myapp.models import mymodel
def register(request):
#gets data here from template
user = User(username=reg_username, password=reg_password)
user.save()
user.mymodel_set.create(name= display_name)
return HttpResponse('Success')
If the related object existed, you would use mymodel, but it does not exist and the relationship is void, so it cannot be accessed via the user. Create it first and set the relationship to that user:
mymodel = MyModel.objects.create(name=display_name, user=user)
# ^^^^ set related user
The _set suffix is usually used for reverse ForeignKey relationships and not for OneToOne relationships.
Also note that the related_name on the user field was already specified as mymodel, and the related field can now be accessed from the User model via user.mymodel
I have django custom user model MyUser with one extra field:
# models.py
from django.contrib.auth.models import AbstractUser
class MyUser(AbstractUser):
age = models.PositiveIntegerField(_("age"))
# settings.py
AUTH_USER_MODEL = "web.MyUser"
I also have according to these instructions custom all-auth Signup form class:
# forms.py
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
age = forms.IntegerField(max_value=100)
class Meta:
model = MyUser
def save(self, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.age = self.cleaned_data['age']
user.save()
# settings.py
ACCOUNT_SIGNUP_FORM_CLASS = 'web.forms.SignupForm'
After submitting SignupForm (field for property MyUser.age is rendered corectly), I get this error:
IntegrityError at /accounts/signup/
(1048, "Column 'age' cannot be null")
What is the proper way to store Custom user model?
django-allauth: 0.12.0; django: 1.5.1; Python 2.7.2
Though it is a bit late but in case it helps someone.
You need to create your own Custom AccountAdapter by subclassing DefaultAccountAdapter and setting the
class UserAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=True):
"""
This is called when saving user via allauth registration.
We override this to set additional data on user object.
"""
# Do not persist the user yet so we pass commit=False
# (last argument)
user = super(UserAccountAdapter, self).save_user(request, user, form, commit=False)
user.age = form.cleaned_data.get('age')
user.save()
and you also need to define the following in settings:
ACCOUNT_ADAPTER = 'api.adapter.UserAccountAdapter'
This is also useful, if you have a custom SignupForm to create other models during user registration and you need to make an atomic transaction that would prevent any data from saving to the database unless all of them succeed.
The DefaultAdapter for django-allauth saves the user, so if you have an error in the save method of your custom SignupForm the user would still be persisted to the database.
So for anyone facing this issue, your CustomAdpater would look like this
class UserAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=False):
"""
This is called when saving user via allauth registration.
We override this to set additional data on user object.
"""
# Do not persist the user yet so we pass commit=False
# (last argument)
user = super(UserAccountAdapter, self).save_user(request, user, form, commit=commit)
user.age = form.cleaned_data.get('age')
# user.save() This would be called later in your custom SignupForm
Then you can decorate your custom SignupForm's with #transaction.atomic
#transaction.atomic
def save(self, request, user):
user.save() #save the user object first so you can use it for relationships
...
Side note
With Django 1.5 custom user model, the best practice is to use the get_user_model function:
from django.contrib.auth import get_user_model
# forms.py
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
age = forms.IntegerField(max_value=100)
class Meta:
model = get_user_model() # use this function for swapping user model
def save(self, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.age = self.cleaned_data['age']
user.save()
# settings.py
ACCOUNT_SIGNUP_FORM_CLASS = 'web.forms.SignupForm'
Maybe it's not related, but I thought it would be worth noticing.
i think you should define fields property in class Meta in SignupForm and set list of fields that contains age, like this :
class SignupForm(forms.Form):
...
class Meta:
model = MyUser
fields = ['first_name', 'last_name', 'age']
and if it's not worked, look at
this
I'm trying to get the Django Admin interface to display information about my profile. It displays all of my users but no profile information. I'm not quite sure how to get it to work.
I found this code after a quick google search:
from auth.models import UserProfile
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
admin.site.unregister(User)
class UserProfileInline(admin.StackedInline):
model = UserProfile
class UserProfileAdmin(UserAdmin):
inlines = [UserProfileInline]
admin.site.register(User, UserProfileAdmin)
However, I don't think that it worked. When I log into the admin page, I see Users, Groups, and Sites. I click Users and I see a list of all of my Users, but no indication of any profile. Clicking on a user shows me info about that user, but still no profile information.
If it will help, here is my model declaration:
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
company = models.CharField(max_length=30)
user = models.ForeignKey(User, unique=True)
And my registration code:
def register(request):
if request.method == 'POST':
uf = UserForm(request.POST)
upf = UserProfileForm(request.POST)
if uf.is_valid() and upf.is_valid():
user = uf.save()
userprofile = upf.save(commit=False)#need to get the user profile object first
userprofile.user = user #then set the user to user
userprofile.save() #then save to the database
return HttpResponseRedirect('/auth/login/')
else:
uf = UserForm()
upf = UserProfileForm()
return render_to_response('register.html', dict(userform=uf,userprofileform=upf),context_instance=RequestContext(request))
I can't see exactly what's wrong, but here's a slightly simpler example that I know works. Put this is any working admin.py. Try adding a trailing comma to your inline-- some things break without it.
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from accounts.models import UserProfile
admin.site.unregister(User)
class UserProfileInline(admin.StackedInline):
model = UserProfile
class UserProfileAdmin(UserAdmin):
inlines = [ UserProfileInline, ]
admin.site.register(User, UserProfileAdmin)
This is not exactly an answer to your question BUT, according to Django Admin documentation, you can display information from UserProfile in your User "table". And you can make it searchable.
That would look something like this (modifying answer from C. Alan Zoppa):
class UserProfileAdmin(UserAdmin):
inlines = [ UserProfileInline, ]
def company(self, obj):
try:
return obj.get_profile().company
except UserProfile.DoesNotExist:
return ''
list_display = UserAdmin.list_display + ('company',)
search_fields = UserAdmin.search_fields + ('userprofile__company',)
You might have an issue though with search if your profile class is no longer called UserProfile.
The missing comma shouldn't matter. I suspect the problem is that you added a new admin.py but the development server didn't recognize it. If you restart the development server, it'll see the new file.