Django: Automatically selecting related model UserProfile when retrieving User model - python

When showing {{ user }} in a Django template, the default behavior is to show the username, i.e. user.username.
I'm changing this to show the user's initials instead, which are stored in a separate (OneToOneField) UserProfile model.
So in customsignup/models.py I've overridden the __unicode__ function successfully, with the desired result:
# __unicode__-function overridden.
def show_userprofile_initials(self):
return self.userprofile.initials
User.__unicode__ = show_userprofile_initials
But of course, the database is hit again because it needs to independently select the UserProfile model every time a user object is asked to show itself as a string. So even though this works, it escalates the number of database hits quite a bit.
So what I'd like to do, is to automatically use select_related('userprofile') whenever a User model is called from the database, seeing that I will essentially always want the profile when dealing with the user in the first place.
In more technical terms, I'm attempting to override the model manager of an existing model. So I'm in no control over the User model definition itself, since that's in an imported library.
So I've tried overriding the objects member of the User model in the same way that I overrode the __unicode__ function, like so:
# A model manager for automatically selecting the related userprofile-table
# when selecting from user-table.
class UserManager(models.Manager):
def get_queryset(self):
# Testing indicates that code here will NOT run.
return super(UserManager, self).get_queryset().select_related('userprofile')
User.objects = UserManager()
Is this supposed to work? If so, what am I getting wrong?
(I will mark an answer as correct if it can show that this is not supposed to work in the first place.)
A similar question I've found is here, but it's approached from the other end:
Automatically select related for OneToOne field

No, User.objects = MyManger() is not supposed to work. According to the docs, there are just two supported methods for extending the provided auth User model, either a profile model, as you are doing, or a proxy model, which probably doesn't fit your case. From the docs (emphasis added):
There are two ways to extend the default User model without substituting your own model. If the changes you need are purely behavioral, and don’t require any change to what is stored in the database, you can create a proxy model based on User. This allows for any of the features offered by proxy models including default ordering, custom managers, or custom model methods.
If you wish to store information related to User, you can use a OneToOneField to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.
As an alternative to extending the provided auth User model, you can provide your own custom User model. Then you will have complete control over its managers.
Instead, consider simply replacing {{ user }} with {{ user.profile.initials }}. Creating the OneToOne field on your profile model also creates a reverse accessor for instances of the related model. You can specify the reverse accessor name by the related_name keyword argument on the profile model field. For example...
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model)
user = models.OneToOneField('auth.User', related_name='profile')
initials = models.CharField(max_length=6)
some_user = User.objects.first()
# assuming there is already a profile related to this user
some_user.profile.initials = 'S.P.Y.'
You could also make a __str__ method for your profile model like
def __str__(self):
return self.initials
Then when you do {{ user.profile }} in a template, the initials will be shown.

Related

Querysets on views or on Model Managers DJANGO

Some days ago a guy explained me that on ruby on rails the queries are done on models. Because it gets already saved at your data before be requested on views and the query.
By the way I've learned and had been working until now, I'm setting the query on views.py and passing, through a context variable. So I started to read about Model.Manager and still didn't find a answer to which way is better:
queries made on views
queries made by simple functions on models
queries made on models.Manager class for each model
Use custom QuerySets and ModelManagers in your models
call your model's ModelManager custom methods from within your views
pass the returned values (querysets, instances or whatever) to the templates (or JSON serializer etc)
It's a matter of separation of concerns:
the template (or json serializer or whatever) doesn't have to know where data come from nor they were obtained - only what the data structure is
views don't have to know about how the query is implemented, only on how to get relevant data
only the model layer should know about it's implementation (encapsulation 101)
Every model is associated with a Manager (default one is objects)
>>> from django.contrib.auth.models import User
>>> user = User.objects.all()
>>> type(user)
<class 'django.db.models.query.QuerySet'>
When you need to make any queries to the model you need a manager for this. In the above example user doing a query on objects manger of User Model.
3-queries made on models.Manager class for each model - correct interpretation
Click here for Documentation
Ref.
By default, Django adds a Manager with the name objects to every Django model class.
If u have specific business logic, you can make use of managers to override built-in model methods like save() and delete() to add business logic to default database behaviour or you can specifically design some query logic.
file name ---- > models.py
from .managers import ModelNameManager
class ModelName(Base):
title = models.CharField(max_length=255, blank=True, null=True)
headline = models.CharField(max_length=255, blank=True, null=True)
objects = ModelNameManager()
create a file managers.py file in your application
file name ---- > managers.py
class ModelNameQuerySet(models.QuerySet):
def by_name(self, id):
return self.filter(id=id)
class ModelNameManager(models.Manager):
def get_queryset(self):
return ModelNameQuerySet(self.model, using=self._db)
def by_name(self, ad):
return self.get_queryset().by_name(id)
in Views.py or any services file file your query will be
import ModelName
obj = ModelName.get_queryset(id)
obj.title
this will return the object based on the query written in managers.
I hope this is helpful.
According to the Django idioms want to add some advice that can be useful:
model managers it's a place for most common queries, not for all queries. Managers describe basic methods for working with models. If you have a widely-used logic for your model with queryset - put it to the manager's method. For example, my users split by domains, so I want to get the user by name and domain, so add the get_by_name_and_domain method to the user model manager. Also, you can access your model by the model attribute in the manager.
models are the part of MTV (Model-View-Template) model on Django and the main purpose of the 'model' itself is to describe db-object but not related business logic. So put your custom queries to views and remember about DRY principle.

difference between ForeignKey and extending the User class/model in Django

Django: When extending User, better to use OneToOneField(User) or ForeignKey(User, unique=True)?
I went through this thread and found that ForeignKey(with unique=True) is better than OneToOneField, but what about extending the class itself, I.e. here is the example
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
# some additional fields
OR
class UserProfile(User):
# some additional fields
Difference between these two approaches and pros/cons and which one should I use?
EDIT:
I can use AbstractUser as well
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
phone_no = models.CharField(max_length=10, blank=True)
and then mentioned AUTH_USER_MODEL = 'myapp.User' in settings.py
main concern is, what approach should I use, extending the class or ForeignKey ?
Duplicates:
What's the difference between OneToOne and Subclassing a model in Django
Django Model Inheritance versus OneToOne field
MORE EDIT
Forget about ForeginKey or OneToOne, assume only one of these two exist, now compare that with extending/subclassing approach
First, it is good to know there currently are several options how to extend the Django user model. Each has its purpose (but there is some overlap as well). Django docs are a bit confusing as it seems from this there are two options, i.e. proxy or OneToOneField. However this relates to the existing user model, as further on in the docs is dealt with custom user models.
So in practice there are four (common) ways to deal with extending the user model:
Proxy model (no new databasefields, just to change user model behavior, i.e. new ordering, new methods, etc.).
OneToOneField (extra datafields needed within existing Djang user model).
Custom user model with AbstractBaseUser (extra datafields
needed, and specific requirements regarding authenticaton process,
e.g. using emailaddress als id token instead of username).
Custom user model with AbstractUser (extra datafields needed, no
change to authentication).
Implementing option 3 and 4 a fresh database migration is needed, so these are only suitable for new projects.
This is a good link for more detail on this. I think option 2 and 4 are closest as both only want to extend to add more datafields. Writer seems in favor of option 2, but when starting a new project option 4 seems easier to me. Somewhere in the comments writer mentions risk of not being able to upgrade to new Django versions for option 3 and 4. Seems far-fetched to me, but I can't tell really.
There is no better way to do, the thing is if you do extend AbstractUser you need to redefine some functions so it may be longer but you have more control on what you wanna do with your user.
Make a OneToOne field on django default user is faster and also allow you to add your own user custom fields but you can use directly User default field in your custom object, and your custom field on the user :
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User)
department = models.CharField(max_length=100)
You can do :
>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department
So it really depends on what you want to do. You can do your User for example if you want to take the mail adress as the identification token (it's a common exmaple but you can do much more things :p).
Here is a good explanation (I place it on user but you can read the whole page it's pretty interesting when you dive into User and authentication into Django).
Hope it help.
I am skeptical about the benefits of a unique FK verses one-to-one, you could achieve a similar thing in the admin by using fieldsets so I would prefer to have an explicit one-to-one field on the model, making the nature of the relation more obvious.
The duplicate questions you linked to aren't specific to the auth User model and discuss one-to-one vs model inheritance generally. Technically they are both the same (i.e. model inheritance uses a one-to-one field)
So ultimately the choice comes down to semantics: is your related model a 'subclass' of the other, or just a link to further related info?
In the case of auth User you would ask yourself then: are there some extra fields that should be present for all users (eg gender, facebook id etc)? or some fields you want to omit from the Django User model (eg to use unique email address as username)?
In this case the obvious choice is to extend AbstractUser. If you can't imagine specifying null=True on your user profile model you should consider extending AbstractUser.
On the other hand there may be some data that is more analogous to the old UserProfile model (have a look how things were in old versions of Django before extending AbstractUser was supported: https://docs.djangoproject.com/en/1.4/topics/auth/#storing-additional-information-about-users)
Perhaps for example you have different types of users who may or may not have certain extra sets of fields. In this case it may make sense to have a one-to-one link to one or more 'profile' models.

What's the proper way to use multiple AUTH_USER_MODEL in Django 1.5?

I want to use two different models for django.contrib.auth module. The first one is the default User model provided by Django which is completely suitable for admin access (groups, permissions etc.) but the other one is customer model which has a lot of different attributes (city, locale, address etc.) compared to default User model. These user groups must use different tables and mustn't have any relation.
I created a Customer model inherited from AbstractBaseUser and a middleware class called ChangeBaseUser like this:
class ChangeBaseUser(object):
def process_request(self, request):
match = resolve(request.path)
if match.app_name == "myapp":
settings.AUTH_USER_MODEL = 'myapp.Customer'
else:
settings.AUTH_USER_MODEL = 'auth.User'
It's working but I'm not sure whether this is the proper way to do it because in documentation there is a section (link) that implies the convenient way is to assign a static value for default user model.
If this is not the proper way, do you have any suggestions for having multiple user models per module basis?
If your requirement is to keep admin users and customers separate, I don't see anything wrong with having multiple user models. At this point, the customer model is like any model, except it is very similar to the user model and that is perfectly fine if that works for you. The only disadvantage is that you will have to possibly duplicate many helpers django gives you for the Django user model such as auth backend or sessions for users. If you are willing to do all that, this seems perfectly fine.
If you wish however to utilize many of the django helpers you might want to create a very basic user model which will serve as a base for both admins and customers:
class User(AbstractBaseUser):
# use this for auth and sessions
class Admin(models.Model):
user = models.OneToOneField(UserBase, related_name='admins')
# ... other admin-specific fields
class Customer(models.Model):
user = models.OneToOneField(UserBase, related_name='admins')
# ... other customer-specific fields
This will allow you to reuse many of the things Django provides out of the box however it will incur some additional db overhead since more joins will have to be calculated. But then you can cache things for customers so you can get some of the performance back.

How to fields to the User model

I want to be able to save a picture to a user and maybe even a description, these can be optional, but how to you add to a model. I don't want to go into the souce code of django to change it, can I overide a model to add fields, if so how?
how do i access these accounts.UserProfile and accounts.UserProfile.image in my views.py file
I have django-registration installed
From the docs:
If you'd like to store additional information related to your users, Django provides a method to specify a site-specific related model -- termed a "user profile" -- for this purpose.
To make use of this feature, define a model with fields for the additional information you'd like to store, or additional methods you'd like to have available, and also add a OneToOneField named user from your model to the User model. This will ensure only one instance of your model can be created for each User. For example:
from django.contrib.auth.models import User
class UserProfile(models.Model):
# This field is required.
user = models.OneToOneField(User)
# Other fields here
accepted_eula = models.BooleanField()
favorite_animal = models.CharField(max_length=20, default="Dragons.")
To indicate that this model is the user profile model for a given site, fill in the setting AUTH_PROFILE_MODULE with a string consisting of the following items, separated by a dot:
The name of the application (case sensitive) in which the user profile model is defined (in other words, the name which was passed to manage.py startapp to create the application).
The name of the model (not case sensitive) class.
For example, if the profile model was a class named UserProfile and was defined inside an application named accounts, the appropriate setting would be:
AUTH_PROFILE_MODULE = 'accounts.UserProfile'

django authentication backend

I followed the method of extending the User class for implementing custom users in my application.
As mentioned in the link, custom authentication backend needs to be written in order to return the appropriate custom user class rather than User.
However I have more than one custom users class, namely Student, Teacher,Parent.
Is there any better way than checking Student->Teacher->Parent tables to return the correct custom user?
The only solution I could think of is to actually change the User model that django uses and add a content_type field that would tell you what type of user the actual user object is. Then you could directly query on that one. You'd still need 2 queries every time to fetch the correct user object.
Alternatively you could have a model that inherits from User that encompasses all of the functionality required by your three classes, call it SuperUser for example, with a special field identifying if it is a Student, Teacher or a Parent.
Then fetch the SuperUser object for a user, thus containing all of the required data. By using the special field identifying which user type they are, you could have a proxy model that you have for each type of user (ProxyStudent, ProxyTeacher, etc) that would make it behave as it should.
This would mean you only ever have 2 database hits regardless, but you get to store the data as specified as long as you use the proxy model to access them.
class SuperUser(User):
type = models.IntegerField(choices=[(0, 'Student'), (1, 'Teacher'), (2, 'Parent')]
# insert all the data for all 3 seperate classes here
class ProxyStudent(SuperUser):
class Meta:
proxy = True
def special_student_method(self):
pass
fetch request.user
and make request.user an instance of SuperUser
student = ProxyStudent()
student.__dict__ = request.user.__dict__

Categories