Django User Type Definition - python

In Django : From a Python OO perspective if I want different types of users shouldn't I have a "Teacher" Object Type AND "Student" Object" type extended from AbstractUser?
Looks like all the solutions mention to just extend with all the fields required for both users and only use the fields required for a Teacher or Student at Form time.
Just trying to stay with the OO method which makes sense to me from a newbie perspective.
Having different object types would allow me to test the object for the type instead of looking at an attribute and have only the needed fields for each type defined in the object.
Is there a correct way to do this so I have users of different object types?
Could I just embed an object of type Teacher or Student as a field in the User Model? Just a guess?
Let me know what you can and thanks for your expert polite response and patience.
Is this a good solution?
https://github.com/mishbahr/django-users2
This is the recurring theme I see on the internets...
django best approach for creating multiple type users

This is a bit like assigning a profile to a user. The way I'd do it would be a type field on a custom user model which then created a related model of either Teacher or Student depending on that type.
Looking at the age of the code in django-users2 there, I wouldn't use that with it being 6 or 7 years old. You might read through it & adapt it's concepts, but I wouldn't use it as it is.
The way I create profiles associated with a user is through a signal like this;
from django.contrib.auth import get_user_model
from django.db.models.signals import post_save
from django.dispatch import receiver
from ..models import Profile
#receiver(post_save, sender=get_user_model())
def auto_create_profile(instance, **kwargs):
"""
Anytime a User object is created, create the associated Profile object
alongside it
"""
if kwargs.get('created', False):
Profile.objects.create(
user=instance
)
So you might adapt that to check the type as well;
if kwargs.get('created', False):
if instance.type == 'teacher':
Teacher.objects.create(
user=instance
)
Then your models just need a link to the user, which makes sense as a OneToOneField;
class Profile(models.Model):
"""
Profile model
"""
class Meta:
"""
Metadata
"""
app_label = 'accounts'
verbose_name = _('User profile')
verbose_name_plural = _('User profiles')
user = models.OneToOneField(
verbose_name=_('User'),
to=settings.AUTH_USER_MODEL,
related_name='profile',
on_delete=models.CASCADE
)

Related

New ManyToManyField object is not adding

I am building a BlogApp AND i made a feature of Favorite Users in the ManyToManyField.
It means user_1 can add multiple favorite users in one field. I build a view to add users.
BUT when i create an instance to store favorite users from Admin before adding favorite users from site AND then if i add favorite users from site then they are adding. BUT if there is not already a instance of request.user's FavouriteUsers in Admin then it is not adding and creating a new object.
So, A new object for storing favorite users of request.user is not adding, AND if object is already there then they are adding.
models.py
class FavoriteUsers(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE)
favorite_users = models.ManyToManyField(User, related_name='favorite_users', blank=True)
views.py
def AddFavUsers(request,user_id):
obj = FavoriteUsers.objects.filter(user=request.user)
user = get_object_or_404(User,id=user_id)
for ob in obj:
ob.favorite_users.add(user)
ob.user.add(request.user)
ob.save()
return redirect('home')
Image of already created object
When object is already , manually created then favorite users are adding BUT if it is not then new object is not creating.
Any help would be much Appreciated.
Thank You in Advance.
Your view is basically adding the user to all FavoriteUsers that are linked to the User. But it is not said at all that there are such FavoriteUsers.
We thus can construct a FavroiteObject with:
def AddFavUsers(request,user_id):
obj = FavoriteUsers.objects.create(
user=request.user
)
obj.favorite_users.add(user_id)
# …
the modeling is also a bit odd, since we here create a third table, it makes more sense to construct a model Favorite with two ForeignKeys that thus acts as a junction table. So this should look like:
from django.conf import settings
class Favorite(models.Model):
user_to = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name='favorites_from'
)
user_from = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name='favorites_to'
)
class Meta:
constraints = [
models.UniqueConstraint(fields=['user_from', 'user_to'], name='favorite_only_once')
]
If you then want to make a user_id a favorite of the logged in user, you can work with:
from django.contrib.auth.decorators import login_required
#login_required
def AddFavUsers(request, user_id):
Favorite.objects.create(user_from=request.user, user_to_id=user_id)
# …
You can then obtain the favorite users of a user with:
User.objects.filter(favorites_from__user_from=my_user)
where my_user is the user from which you want to obtain the favorite users.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
The ManyToMany field already handles this. You can add a favorite_users property to your User model, which will point to the User model and Django will create the connecting table. Beware of the symmetrical parameter, if you leave it out it'll be a bidirectional relation.
class MyUser(models.Model):
...
favorite_users = models.ManyToManyField("MyUser", symmetrical=False, blank=True)
If you still want to write the connecting model yourself, check out the through parameter.

How to check if user is in a certain table

I have my user table in django, and to differ all the users I created two tables, (Teacher and Student).
Both tables are getting an fk from user
So, in order to make authorization how do I check if one's user is in a certain table.
I need to check it this way
def test_func(self):
return self.request.user.check..if..it..exists..in..table
My models are like this.
class Teacher(models.Model):
User = models.OneToOneField(settings.AUTH_USER_MODEL)
This depends on how your models are set up.
If your Teacher model looks something like this;
class Teacher(models.Model):
user = models.ForeignKey(User)
Then you should be able to check if the user is a teacher by using the implicit backref;
self.request.user.teacher_set.exists()
As the question has been updated to show that the model is slightly different than I anticipated, here is an update.
class Teacher(models.Model):
user = models.OneToOneField(User)
Which means that the backref will be a little different.
hasattr(self.request.user, "teacher")
As you've mentioned that you are doing this inside a django template, I'm pretty sure that the following will work:
{% if user.teacher %}
Since you haven't posted your models, I am giving you a rough idea how to do it.
in your views.py -
from .models import Teacher,Student
def test_func(request):
user = request.user
if (Teacher.objects.filter(user=user).count() > 0) or (Student.objects.filter(user=user).count > 0):
#do your stuffs here..
One way is to query both tables:
teacher = Teacher.objects.filter(user=self.request.user)
student = Student.objects.filter(user=self.request.user)
if teacher or student:
# do what you want.
If you put in your relation the argument "related_name" you can do it using inverse relationship
class SomeTable(models.Model):
user = models.ForeignKey(
User, #Your user model or Django one
verbose_name = "User",
related_name = "inverse_relation_name"
)
Then you have to call using keyword arguments for the filters:
SomeTable.inverse_relation_name.filter(id=self.request.user.id) #You will get a queryset
Or
SomeTable.inverse_relation_name.get(id=self.request.user.id) # You will get the object or a exception

Login form for custom users in django

I have created a custom User SignUp Class.
class SignUp(models.Model):
userId = models.CharField(max_length=8, blank=False, unique=True)
Name = models.CharField(max_length=200)
VehicleNumber= models.CharField(max_length=12)
ContactNum = models.IntegerField(default=0)
def __unicode__(self):
return smart_unicode(self.Name)
I have used this to create a Sign up form. Now, I am not getting a way for creating user login. Note: I can't use django in-built users because they don't have a field for images.
You can extend the built-in django user, by adding OneToOneField.
YourCustomUser(models.Model):
user = models.OneToOneField(User,related_name='profile')
image = models.ImageField()
//put here your others attributes
If you have YourCustomUser instance and want to access to User built-in instance
your_custom_instance.user
If you have User built-in instance and want to retrieve e.i the image
user.profile.image
You can use and extend the built-in model User. The shortest path is
from django.contrib.auth.models import User
from django.db import models
class UserWithPhoto(User):
image = models.ImageField()
but is better practice to use User Profiles, you can read about it in: User authentication in Django
You can read about this in Django user profile and here Extending the User model with custom fields in Django.
Why is better user profile over extending the User model directly?
If you extend the User model you will have the changes applied to all you users. Think about this, you could have Administrators, Developers, Reviwers, even Guests, all of them might have some field that others don't, so a different profile for each one is the better solution than subclassing User for each one, or even worst create a single class with all fields you need for all kinds of users (only think about it hurts ).

Conditional OneToOne in Django

Currently I have 4 classes in a model:
User(models.Model):
....
class Meta:
abstract=True
Sender(User):
....
Owner(User):
....
Report(models.Model):
sender = models.OneToOneField(Sender)
owner = models.OneToOneField(Owner)
The Sender/Owner extends the abstract base class User. I need a one to one relationship between a sender, a report, and an owner.
The problem is that I cannot create the Sender or Owner models because they have no differences in their fields (all the data they need is created in the abstract User model, and I created the subclasses for ease of representation). The solution I have come up with is this:
User(models.Model):
....
user_type = models.CharField(max_length=200)
class Meta:
abstract=True
Report(models.Model):
sender = models.OneToOne(User)
owner = models.OneToOne(User)
However I want to ensure that there is one sender and one owner per report. Is there a way to specify that the User MUST have a user_type of 'sender' or something along those lines? Or is there a better solution to this in general?
I am not a veteran user, so forgive me if I'm in the wrong here, but I don't know that it's good practice to define a new model called User when django.contrib.auth.models provides a base user model also named User. It seems to me like this would set you up for potential conflicts.
Since you want to have two different "types" of user, I would use the old approach of linking back from a related model.
from django.contrib.auth.models import User
from django.db import models
class Sender(models.Model):
user = models.OneToOneField(User, related_name='sender')
is_active = models.BooleanField(default=True)
class Owner(models.Model):
user = models.OneToOneField(User, related_name='owner')
is_active = models.BooleanField(default=True)
Say you create a new User with id=1, then create a new Sender called s keyed to that User. Remember, s is the Sender object, not the User object.
>>s.user
<user: username> #can vary with your own unicode definitions
>>s.user.id
1
>>print hasattr(s.user, 'sender')
True
>>print hasattr(s.user, 'owner')
False
>>print s.is_active
True
>>u = User.objects.get(id=1)
>>u.sender
<Sender: username> #again, what actually displays is controlled by your own 'def __unicode__'
>>print u.sender.is_active
True
>>print hasattr(u, 'sender')
True
>>print hasattr(u, 'owner')
False
Then for the report model:
class Report(models.Model):
sender = models.ForeignKey(Sender, related_name='report_sender')
owner = models.ForeignKey(Owner, related_name='report_owner')
Edit to answer your other question: how do you make sure a User has only a Sender or Owner model, but not both?
I don't know how to accomplish that on the model level. In practice, the easiest way I can think of to prevent this from being an issue would be through some custom form validation when a new Sender/Owner is created. If you're creating a new Sender, as part of validation make sure that the User you're tying it to doesn't already have an Owner model tied to it - and vice versa. Or create both the user and appropriate profile model at the same time.
You could alter your User registration form to have an extra field:
class UserRegistrationForm(UserCreationForm):
CHOICES = (
('s', 'Sender'),
('o', 'Owner'),
)
type = models.ChoiceField(max_length=2, choices=CHOICES)
class Meta: ...
Then when you're processing the form in your view:
...
if form.is_valid():
u = form.save()
type = form.cleaned_data['type']
if type == 's':
Sender.objects.create(user=u)
elif type == 'o':
Owner.objects.create(user=u)
(I apologize, but I am not somewhere I can test this. Please let me know if it doesn't work for you.)
I think the most straightforward way to resolve this is to override the save() method of the Report model and insert (before a super() call) a validation check to enforce your requirements.

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.

Categories

Resources