implementing multiple profiles with django-all-auth - django

Django noob here - I was recently pointed to django-all-auth for registering/handling users, which I'm finding awesome in its near instant setup.
However I'm stumbling at trying to implement multiple user profile models. In reading other answers I've found this to be the closest answer thus far, but not really what I need.
When coding my own basic registration the user would select an account type (basic, pro, elite for example - each being their own profile model). Depending on the link selected the signup form would display both the generic User registration form as well as the profile form of the type chosen by the user.
I know I can go so far as to completely customize all-auth and make something like this work, but I'm hoping to be pointed in a direction that involves less wrecking of the original app. I've thought about having user redirected after signup to choose a profile type, but that seems to be a lot of extra steps.
Thanks for the help!

To extend the basic user class, just subclass AbstractUser. You can find that in the docs. With this you can add the fields your basic user is missing.
Now, you want several types of profiles with different fields, or perhaps the same fields but adding new fields every time.
You can create something like:
class ProfileBase(models.Model):
user=models.OneToOneField(User)
class ProfilePro(ProfileBase):
pro_field=models.SomeField(Foo)
#You can extend ProfilePro here if you want it to include the pro_field
class ProfileElite(ProfileBase):
elite_field=models.someField(Bar)
Once you have these models creating the forms should be easy.
Be aware, when you subclass this way django creates 1 table per model, including on the subclass table only the new fields. This makes necessary a join per level of inheritance so try not to abuse that.
There is a second way to use inheritance:
class ProfileBase(models.Model):
user=models.OneToOneField(User)
class Meta:
abstract=True
If you define the base class as abstract you won't have a table for it, so you can't create objects for that model, but each of your subclasses will be on it's own table. If you do it this way you may need extra logic to handle cases when user changes of type of profile (i.e. went from pro to elite).
To keep this separated from django-allauth, just finish the registration (create a form and in your settings define ACCOUNT_SIGNUP_FORM_CLASS to override all-auth default with your basic info + pick a profile type form) and once your user is logged in, redirect them to their profile to finish the process.

Related

Create fields based on another model

I have a User model with some fields. Some of them will require feedback, are they correctly filled (if not, specific message will be displayed on user profile).
The problem is, how to represent 'invalid' fields in database. My idea is to create another model (call it ExtUser) with OneToOneField to User. And ExtUser should have same fields' names as User, but their types will be all boolean, determining whether field is filled in correctly. For example, if User has a field called email:
email = models.CharField(max_length=100)
ExtUser would have following field:
email = models.BooleanField(default=False)
Here's a problem with this approach. How am I supposed to create fields in ExtUser? Of course I can create them manually, but that would be breaking of DRY principle, and I'm not going to do that. The question is, can I add fields to model dynamically, and have them in database (so I assume it would require to be called before migrate)?
I have django 1.8 and I don't want to use any external modules/libraries.
If someone has an another idea of how to represent that data in database, please add comment, not a reply - as this question is about creating fields dynamically.
You will need to do this manually.
Python does not disallow this behavior; you can take a look at this SO response on dynamically created classes, but Django will not be able to interpret the output. In particular, Django relies on the models to create the SQL tables for the application, and there is essentially no way for this to occur if you model is not statically defined.
In this case, I don't think you have to worry much about DRY; if you need a separate model with fields which happen to be related to, but different from, another model, I think it's probably ok.
Finally, I'm unsure what your goal is, but you could probably define some functions which can determine how "correct" the fields of the user are. This is how I would recommend solving this problem (if it applies).

Django app where you can send application to authorities

I am currently working to write a web app where people fill out the necessary information, and apply to their mentors.
So, at this point, mentors have a model class that is pretty much like the applicant's, so that they can correct the applicant's info without affecting the applicant's original profile.
I will appreciate any helpful comments. Specifically, I am looking for:
-A similar per-exisiting django app that does more or less so I can browse the source.
-Any special Django feature that allows this that I can not aware of.
-General info on how things like these are done in general.
Thank you.
Ad general info)
You would benefit from doing this in a single model (say ApplicationModel), with fields in pairs - field_name_applicant, field_name_mentor.
Then use a CreateView with its fields property set to only the *_applicant fields for the applicant to fill in the applications initially, and an UpdateView with its fields set to the *_mentor fields for the mentor to correct the applicant fields.
Have ApplicationModel.clean() copy all *_applicant field values to their *_mentor counterpart if the later is not set.
Now you have all your business logic in the model where it belongs; quoting a headline in the introduction of Two Scoops of Django:
Fat Models, Helper Modules, Thin Views, Stupid Templates

Creating Dynamic Forms in Django

I'm working on a project that involves a form with some standard fields and some custom field users define later. The standard forms are defined on a model in models.py. For example:
class Order(models.model):
number = models.TextField()
date = models.DateField()
I then use this model to create a simple model form to make a way to fill in the information. That's pretty standard Django.
The tricky thing is that my users want to be able to add arbitrary fields to the form. They would like to be able to use the Admin interface to basically modify the form and add values to it at run time.
So, they might want a new text field called "Tracking Number" or something like that. The trick is that they only need it sometimes and they want to be able to add it dynamically without rebuilding the whole database.
I can create a fairly simple model to represent the custom fields like so:
class CustomField(models.Model):
type = models.CharField(choices=FIELD_TYPES)
required = models.BooleanField()
I think I can then take the ModelForm for the Order class and extend it to add these custom fields. What I am unsure of is how to link the custom field values back to the Order.
I know this all might sound odd, but in practice it makes sense. Each user has slightly different needs for the form and want to tweak it. If I have to hard code the models to have their specific fields, then I will have to have a fork for each user. That simply doesn't scale. If instead they can simply add the fields through the admin interface, then things are much simpler.
I feel like this is something that is perhaps already solved by someone out there. I simply cannot find a solution. I can't be the only one who has gotten this kind of request right?

What is better way to have multiple type of member profiles in django

I see another question on stackoverflow.com whose title seems similar but that doesnot fulfil my requirements and my users are very different so only different roles will may be not work well. I have scenario of job portal where one type of user is a company which have different attributes different functionality while other is candidate who can show his profile and resume e.t.c., they will have different URLs. So they are totally different but common thing is that they are both Registered users. They will use login forms, they will have change password and I intend to use User class for that purpose.
Actual problem I am facing is about UserProfile class usage. UserProfile is use for profiles but in my case these two users are totally different and need many different things in profile. Also I will may be add more user types in system in future. While in django, we tell about profile is by adding this single model in settings.py
AUTH_PROFILE_MODULE = ‘accounts.userprofile’
So is there a way to do this by using some sort of inheritance or abstract class in django or some other way so that I can get intended functionality and can use django Profiles?
EDIT: Ok, upon further inspection, my previous answer clearly will not work.
I had advocated abstracting the UserProfile class. However, if you do that, you cannot instantiate it, so you're back to square one.
However, you can use Multi-table inheritance to achieve what you want. See this in the docs and this Quora thread that provided the inspiration.
The code I posted before remains largely unchanged, save for the exclusion of the Meta sub-class and the abstract variable.
class UserProfile(models.Model):
# Some Stuff
class CompanyProfile(UserProfile):
# Some more stuff
class CandidateProfile(UserProfile):
# Even more stuff

what type of model design should I use to add roles to Users in such a way that it works in Django and in the Django admin

I am using Djangos default authentication system (django.contrib.auth) and I would like to add 'roles' to my users in such a way that Django Admin can work with it.
An example:
A user can be a staffmember, teacher, student and/or parent
If the user has a role assigned, he will gain permissions (eg. staffmembers may sign in to the Django admin)
Some roles might have some extra fields (eg. parent has a relation with at least one student and each student has a field with it's classgroup
Not every role has extra fields
A parent can be a teacher or staffmember and vise versa
A student can not have another role
There are all sorts of (conventional) ways to accomplish the above within a model. Django supports a lot of them, but the Django admin does not. The Django admin has a lot of good features so I would like to use it (but I am getting more and more afraid that it will not be possible).
The following model is what I thought of at first:
class ExtendedUser(contrib.auth.models.User):
"""
For the ease of use I inherit from User. I might
want to add methods later
"""
pass
class StaffMember(models.Model):
"""
A staffmember is a co-worker of the organisation
and has permissions to make changes to (parts of)
the system.
For now the staffmember only has methods
"""
user = models.OneToOneField(ExtendedUser, related_name="staff_member")
class Student(models.Model):
"""
A student can participate in some courses
"""
user = models.OneToOneField(ExtendedUser, related_name="student")
class Teacher(models.Model):
user = models.OneToOneField(ExtendedUser, related_name="teacher")
subjects = models.ManyToManyField(..)
class Parent(models.Model):
user = models.OneToOneField(ExtendedUser, related_name="parent")
children = models.ManyToManyField(Student, related_name=parents")
This works in Django (and in a lot of other MVC-based frameworks). But I can't find a proper way to display the above in the admin.
Ideally I would like to add a User and then within the User-changeview add different roles. At first I thought I could use Inlines:
class StudentInlineAdmin(admin.StackedInline):
model = Student
extra = 0
template = 'accounts/admin/role.html'
I then make some slight changes to the inline template to present the editing user button with a caption 'Add Student role'. Once we hit the button, we display the form and a User role is added. Not ideal, but it works.
Too bad, for Staffmembers there are no fields to add to the inline form. This way it is not possible to trigger the 'has_changed' property for inlines forms. This results in the new role not being saved to the database.
To solve this last problem, I hacked around a bit and added a 'dummy' formfield to the empty user-roles and then hide this field using JS. This did trigger the has_changed.
Still this would not work for somehow none of my inline-models are saved during some tests later on.
So I think I am just doing it the wrong way. I did a lot of Googling and found a lot of people hassling with the same sorts of problems. The one that suited my situation the most was http://www.mail-archive.com/django-users#googlegroups.com/msg52867.html. But still this solution does not give me an easy way to implement the admin.
I also thought about using the built-in groups but in that case I have no idea how I should add the different fields.
Another thing I thought of was trying to 'Add a student' instead of adding a User and assigning a role to him. This works pretty well in the admin if you just inherit the user:
class StudentUser(auth.models.User):
pass
But two problems here. At first it is not possible to be a staffmember and a teacher. Second it is not really working in the rest of Django for the request object return a User object for which it is impossible to request the Student, Parent, Staffmember object. The only way to get one of these is to instantiate a new Student object bases on the User object.
So here is the question: what type of model design should I use to add roles to Users in such a way that it works in Django and in the Django admin?
Friendly Regards,
Wout
I'm assuming in the following that you do not want to alter the admin, or make a copy of django.contrib.admin and hack it as desired.
A user can be a staffmember, teacher, student and/or parent
You could store this in a user profile. Inheriting from User will work, too, but instead of using User.get_profile(), you'll need to manually map from User to ExtendedUser.
If the user has a role assigned, he will gain permissions (eg. staffmembers may sign in to the Django admin)
In that specific case, you can't use automatic role-based assignment. Whether or not a person can access the admin is determined by the is_staff field in their User record.
The most automatic way I can think of is to create an "Update Permissions" admin command, which will update admin fields like is_staff and the permissions based on the role set in the user's profile. BTW, even though this is not fully "automatic", it is a denormalization that can improve performance.
Some roles might have some extra fields (eg. parent has a relation with at least one student and each student has a field with it's classgroup
Not every role has extra fields
A parent can be a teacher or staffmember and vise versa
A student can not have another role
Read up on form validation. That's where you can enforce these rules.
In your model, I'd recommend that you alter the related names of your one-to-one fields:
class StaffMember(models.Model):
user = models.OneToOneField(ExtendedUser, related_name="as_staff_member")
class Student(models.Model):
user = models.OneToOneField(ExtendedUser, related_name="as_student")
class Teacher(models.Model):
user = models.OneToOneField(ExtendedUser, related_name="as_teacher")
class Parent(models.Model):
user = models.OneToOneField(ExtendedUser, related_name="as_parent")
Since theUser.as_teacher is a lot clearer than theUser.teacher (which I would read as "the user's teacher").
This works in Django (and in a lot of other MVC-based frameworks). But I can't find a proper way to display the above in the admin.
You're going to have one table in the admin per role. There's no fancy "bottom half of the edit page will redraw itself when you change roles" feature. If you want that, you will need to write your own admin.
Django's admin is great, but it's not trying to be everything to everyone. I have a role-based setup like yours (except the roles themselves are stored in a table), and the admin works fine if a little clunky. The general idea is that if the admin isn't good enough, then you should be writing your own views.
I also thought about using the built-in groups but in that case I have no idea how I should add the different fields.
The built-in groups are not what you're looking for.
Another thing I thought of was trying to 'Add a student' instead of adding a User and assigning a role to him. [...] At first it is not possible to be a staffmember and a teacher.
"Subclassing" is a more restrictive one-to-one. I think your initial model is better.
Second it is not really working in the rest of Django for the request object return a User object for which it is impossible to request the Student, Parent, Staffmember object. The only way to get one of these is to instantiate a new Student object bases on the User object.
No, you instead find the Student object using the auto-generated id from the User object:
try:
theStudent = Student.objects.get(user_ptr_id=theUser.id)
except Student.DoesNotExist:
# That user isn't a student; deal with it here.
If you're going to use the admin, I think you're going to have to live with a two-step process of adding an ExtendedUser, then adding Student or whatever entries for them.
So it comes down to a tradeoff: a little extra work using the built-in admin, or writing your own user management views. WHich route is best really depends on how much this interface will be used: If it's just you, then the admin should be fine, even with its warts. If a lot of people will be using it on a routine basis, then you should just write your own views to handle things.