Django Model Inheritance versus OneToOne field - python

EDIT: Advantages and disadvantages of both methods.
SO,
I have three models: Person, Client, Member
Person is a base model, Client and Member are profiles for Person.
class Person(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
verbose_name=_('email address'),
max_length=255,
unique=True,
)
class Client(User): #or maybe models.Model and explicit OneToField
first_name = models.CharField(verbose_name=_('first name'), max_length=30)
last_name = models.CharField(verbose_name=_('last name'), max_length=30)
class Member(User): #or maybe models.Model and explicit OneToField
description = models.CharField(verbose_name=_('first name'), max_length=255)
# other stuff
So, what I want?
In admin, when we add client or member, I want to fill field email (fields from base class) as if it was in derived class. For example, in admin list_display = ('email', 'first_name'). Not select boxes for user.
Person class can be instantiated separately and the "attached" to the created profiel. Like person=Person(email="test#gmail.com"), client = Client(person=person,first_name="test"...). Especially, this should work in forms. When user (person) is authenticated I want to give ability to fill in profile (client) form and attach person to this form.
One person can have both accounts. If I delete client/member, corresponding person should not be deleted.
3rd option actually is not necessary, but it is useful for me to know how to do it.
So, option 1 is perfectly solved by inheritance, Person is User, but this approach fails when option 2 is implemented. Since Person and Client are considered as one whole, I can't attach user, duplicate key error.
Option 2 is resolved by extending models.Model and appending person=models.OnetoOneField(Person,primary_key=True). However, admin is broken (1st option), because Client don't have fields like email.
So, what approach to take in order to solve above issues?
Are there simple ways?
If there are no simple ways, is there advanced way, like overriding metaclass, object descriptors or writing custom OneToOne field?
Any suggestions are welcomed.
Thank you.

You can use the post_delete signal for this.
So you would:
Register the post_delete signals on both the models
One delet, check if the other object exists, and delete.
There are lots of examples on the implementation of post_delete signals on StackOverflow, so i will leave that part out.

Related

Django - Team/User relationships

I'm at a loss... I'm just learning Django and I am really rather confused about how to make a field work the way I would like it to.
I understand that Django has a native "Groups" model. However, I am looking to build my own teams model for customization and practice.
Here is my models.py file for my Users app:
from django.db import models
from django.contrib.auth.models import User
class Team(models.Model):
members = models.ManyToManyField(User)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
admin = models.BooleanField("Admin Status")
Here's where I'm confused. I would like to be able to call the team that the user is part of directly from User.Profile. So, I want to add a field to my Profile class that will automatically populate with the team name when a user is added to a team.
A potential problem I can see is that, currently, I can assign a user to multiple teams. This doesn't bother me, perhaps I can have a Profile model field that automatically populates with a list of all the teams that the user is associated with. Regardless, I can't figure out what type of field I would need to use, or how to do this.
Does that make sense?
A potential problem I can see is that, currently, I can assign a user to multiple teams.
Indeed, you can however easily retrieve the Teams the myprofile object is a member of with:
Team.objects.filter(members__profile=myprofile)
You thus can make a property for the Profile model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
admin = models.BooleanField("Admin Status")
#property
def teams(self):
return Team.objects.filter(
members__profile=self
)
Then you thus access the Teams of a myprofile with myprofile.teams.
So, I want to add a field to my Profile class that will automatically populate with the team name when a user is added to a team.
From my limited knowledge of database, you can add a name field to your Team model.
Keeping in mind your requirement as mentioned in question, i would suggest you to use django reverse relations to get all the teams the profile is associated with
user_teams = User.objects.get(id='user_id').profile_set.all()[0].team_set.all()
to know more about django ORM reverse relation, here is a very short article

Use new subclass with already in use database in django

I'm trying to create a customized User class inheriting from django User. Problem is I already have some users in database which can not be deleted whatsoever and also I have another class (let's say reports) which have a foreignkey to User. My question is: Is there any way to create my new User class and keep the old data too?
thanks in advance.
You can create related model that links back to User. This is a common approach if you have different types of users, but there are also other use cases.
class SpecialUserProfile(models.Model):
user = models.OneToOneField(User, null=True, blank=True, on_delete=models.SET_NULL)
special_feature = models.CharField(max_length=100, null=True, blank=True)
etc.
What you also need to do is create this profile, when new user is added to User. You can do this with post_save signal.
#receiver(post_save, sender=User)
def create_special_user_profile(sender, instance, created, **kwargs):
if created:
SpecialUserProfile.objects.create(user=instance)
You create profiles for existing user with a command or write and run a temporary function that does that for existing users in User.
Now you can use ORM in the sense of user.specialuserprofile.special_feature.
This way you'll keep using User model as a base, it won't mess with build-in user related functionalities, won't have think about new and old users and you can use this new model for any additional information about users.

How to separate users (models) by admins and customers on Django

I would like to separate users of my Django app in two classes :
- Admin (users that use Django admin) - inherit from AbstractUser
- User (customers users) - inherit from AbstractBaseUser
I want to separate this two kinds of users because all fields of AbstractUser (is_staff, is_superuser, groups, permissions) are useless for my customer users and for permissions and group, I just want to implement something different. That why, I want to use AbstractBaseUser.
But for django admin users, AbstractUser class, it's just perfect and particularly with permissions feature.
class Admin(AbstractUser):
pass
class Customer(AbstractBaseUser):
pass
But now, is there a way to precise the User model used Admin for the django admin only?
And use the Customer model for the rest of my apps.
Did I have to implement this from scratch :
class MyUser(AbstractBaseUser):
username = models.CharField(max_length=30, unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
is_active = models.BooleanField(default=False)
class Admin(MyUser, PermissionsMixin):
is_staff = models.BooleanField(default=True)
class Customer(MyUser):
# specific fields
pass
With this implementation, if I set AUTH_USER_MODEL to User, permissions will not work because User has no permissions, is_superuser and is_staff fields.
And if a set it to Admin, I will not be able to authenticate Customers with django.contrib.auth.
So guys do you have a solution to this issue?
The way Django offers to you seems to be much more flexible and future-adapted.
You have a built-in User model, which you can override. Anyway, that model has permissions, groups, etc.
If you need different field sets for different kinds of users, you create a OneToOne profile models.
The separation point between your admins (actually, staff users) and regular customers is a User.is_staff attribute.
This way you gain a bunch of cool stuff (compared to two completely different user models):
Everything works out of the box: contrib.auth and contrib.admin modules.
Easy-customisable separation point: just override the admin_site.has_permission() and here you go.
You have the ability (but not obligation) to create users which are either customers and admins.
You can assign groups and permissions (different from your admins' ones) to your customers. Even you don't need it now, who knows.
As for drawbacks. The only one you've pointed out so far: your customers will be having (unused for now) permissions. Well, as they (as well as groups) are just separate tables, your customer data will have no performance of storage overhead.
That is to say, the overhead is negligeable compared to the benefits. I'd strongly recommend staying with Django's default User model and extending it if necessary.

Uncertain how to design django apps - best practices

In every django app I've previously developed I used only one app, which had all models, forms etc included.
It was OK for small apps, but now it's time to do it right way :)
I want to have auth module which will cover such things like user signups, login, welcome email sending etc.
So... I've created app called 'auth'. In auth.models module I have something like this:
class BaseModel(models.Model):
uuid = UUIDField()
time_created = models.DateTimeField(auto_now_add=True, null=True)
time_modified = models.DateTimeField(auto_now=True, null=True)
class Meta:
abstract = True
class Team(BaseModel):
title = models.CharField(_('Team title'), max_length=64)
class Member(BaseModel):
user = models.OneToOneField(User)
team = models.ForeignKey(Team)
USER_ROLES = (
('admin',_('Administrator')),
('member',_('Team member')),
)
role = models.CharField(_('User role'), max_length=6, choices=USER_ROLES, default='member')
Here comes my first trouble. Every user in my app will have one-to-one Member object. Member object stores some additional data about the user. Every single user must be assigned to group (which I call Team)... But wait... Should't be Team class moved to separated app which will manage Teams???
Also, I like to inherit all model classes from BaseModel. Do I have to add this same BaseModel class in every app again? Isn't a breaking "don't repeat yourself" rule?
Also, during registration of new user I need to create new Team for him. Where should I put code for this? Its related both to user management and team management.
I would need some guidance, how to fix these design issues.
Apps are intended to be self contained "modules" of code that could be reused in other django projects in the future. Ask yourself whether you'd ever want to re-use this code in another project.
Your extended user model, with teams would strike me as being part of a single app. You're not defining site-specific behaviour with respect to teams or members. I could also see many cases where you'd re-use the above code.

How I can make an attribute of the child model required, if it is inheriting the User class without saving?

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).

Categories