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__
Related
So I have extended Django's Group model to add an extra field like so:
class MyModel(Group):
extra_field = models.TextField(null=True, blank=True)
On doing this, each instance of MyModel created, creates a Group instance as well.
If I add a user to the resulting group with user.groups.add(group), the group is added as expected.
However, the permissions from the MyModel group do not seem to have trickled down to the user i.e
Doing user.get_all_permissions(), get_group_permissions() or even testing a user.has_permission(mygroup_permission) returns nothing. Only permissions from pure Group instances(created before extending the model) are shown.
Is there anything I need to do for permissions on customised groups to be visible on the users?
TIA
When you take a look in the ModelBackend, the default django authentication backend, you can see this:
def _get_group_permissions(self, user_obj):
user_groups_field = get_user_model()._meta.get_field('groups')
user_groups_query = 'group__%s' % user_groups_field.related_query_name()
return Permission.objects.filter(**{user_groups_query: user_obj})
Its quite obvious, that it tries to determine the permissions from the field that represents the users groups, here groups. Because your MyModel is not tied to djangos user model, you will not get any permissions this way.
You can now:
Write a custom user model and substitute the relationship of groups
Write a custom authentication backend
Use a One-To-One-Relation from MyModel to Group
In my opinion, the easiest way is to extend the Group model with a new model in an One-To-One relationship. This way you can use djangos auth system furthermore and have additional data available.
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.
I am creating model class Car and I want to have in it two references to one foreign key.
class Car(models.Model):
owner = models.ForeignKey(User)
#and here I want to have owner email (which is already set in class User)
email = owner.email
But I don't know how to make reference to field of ForeignKey already used.
I get this error:
AttributeError: type object 'User' has no attribute 'email'
Is there any way to do it?
There are two things here... the first is to find out why you want to do this. Because maybe you shouldn't.
If you just want to access the owner's email address from a Car instance you don't need to add it as a field on the Car model, you can do:
my_car = Car.objects.get(owner=me)
my_email = my_car.owner.email
This does two seperate db queries, the first to get the Car and the second to get the owning User when you access the ForeignKey.
If you want to avoid this you can use select_related:
my_car = Car.objects.select_related().get(owner=me)
my_email = my_car.owner.email
Now it's only one query, Django knows to do a join in the underlying SQL.
But, assuming you know all this and you still really want to add the owner's email to the Car model. This is called 'denormalisation' and there can be valid performance reasons for doing it.
One problem that arises is how to keep the email address in sync between the User and Car models. If you are deliberately pursuing denormalisation in your Django app I highly recommend you consider using django-denorm. It installs triggers in the SQL db and provides a nice interface for specifying denormalised fields on your model.
You should really follow django's tutorial...
You can access the user email with car_instance.owner.email.
There is no need to add existing fields to another module. You should in principle avoid repeating data. Since the email and all relevant user info exist in the user model, then the foreign key is enough to access this data in relevance to a specific car record:
car = Car.objects.first()
email = car.owner.email
You can do the same with any field of the user model.
like http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/
but can anyone suggest how I can make an attribute of the child model required, if it is inheriting the User class without saving the user if the instance of the child model is not saved? eg.
class Customer(User):
organization = models.CharField(max_length=80, unique = True)
address = models.CharField(max_length=80)
.
..
objects = UserManager()
If in the admin.py, model Customer is registered, on execution, we get the user creation form, with password after saving it, we exit from the module. We are able to see that the user exists in the django Auth, even if the Customer is not yet created. How do I override the save of the User class. Also I need to create other users for the application the normal way. Please suggest
You're sure you're not adding an User, not a Customer. Here you are not transforming users into customers, just creating a new class. (I misread your post and thought you missed that ; I'll leave that here but anyways).
You probably don't want all users to be customers (For instance, you have staff).
Did you try removing the manager ?
Let me point out however that the Django developers themselves recommend using profiles not inheritance (See comments from James Benett in the blog article you linked).
I saw a couple of ways extending user information of users and decided to adopt the model inheritance method.
for instance, I have :
class Parent(User):
contact_means = models.IntegerField()
is_staff = False
objects = userManager()
Now it is done, I've downloaded django_registration to help me out with sending emails to new users. The thing is, instead of using registration forms to register new user, I want to to invoke the email sending/acitvation capability of django_registration. So my workflow is:
1. add new Parent object in admin page.
2. send email
My problem is, the django-registration creates a new registration profile together with a new user in the user table. how do I tweak this such that I am able to add the user entry into the custom user table.
I have tried to create a
modelAdmin
and alter the save_model method to launch the create_inactive_user from django_registration, however I do not how to save the user object generated from django_registration into my Parent table when I have using model inheritance and I do not have a Foreign key attribute in my parent model.
It's probably something like:
p = Parent()
p.user_ptr = user
p.contact_means = ...
p.save()
(Django creates the foreign key for you when doing model inheritance, plus the attribute ending in _ptr)