Django best practice - referencing custom user or profile model - python

Based on Django's recommendation that information should be stored in a separate model if it's not directly related to authentication, I've created both a custom user model and a profile model in my app.
Something like:
class User(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True
)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
location = models.ForeignKey(Location)
date_of_birth = models.DateField()
date_joined = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'location', 'date_of_birth']
class Profile(models.Model):
user = models.OneToOneField(User)
picture = models.ImageField(upload_to='profile_pictures/',
default='default.jpg')
bio = models.TextField(blank=True)
sex = models.CharField(max_length=10,
choices=(('Male', 'Male'),
('Female', 'Female'),
('No Comment', 'No Comment')),
default="No Comment")
occupation = models.CharField(max_length=100, blank=True)
What's the best practice for having other models refer to a user? For example, my app has a messaging system. In the Message model, is it best to have a foreignkey relation to Profile as opposed to User? Should the User model only be used for the purpose of authentication?

I think you can relate the models to User and use related name to access profile.
This makes your models not depend directly on custom profile.

Related

django assign User to a model

I want to assign a User to a Model in django, I created a custom User model and sign-up/sign-in Forms but now I want to Assign a User model to another model named Customer whenever a new user is Created Here he the Customer model
class Customer(models.Model):
id = models.AutoField(primary_key=True)
User = models.OneToOneField(
Account, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=200, null=True)
email = models.CharField(max_length=200, null=True)
phone = models.CharField(max_length=200, default='0', null=True, blank=True)
address = models.CharField(
max_length=200, default=' ', null=True, blank=True)
city = models.CharField(max_length=200, default=' ', null=True, blank=True)
def __str__(self):
if self.name == None:
return "ERROR-CUSTOMER NAME IS NULL"
return self.name
Note: I can assign the User manually in the Database and It lists All the Users but I want it to do it itself when a new user is created
I think it would be better to extend the User model, and add more fields rather than creating a new model (which has a User onetoonefiled in it).
Something like this:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
This is the kind of approach I use in my projects.
Here you have the default User model fields:
User model default fields
You don't need to add these in your Profile class.
I based this on this article: How to extend User Django Model
don't forget to add to the admin.py:
from django.contrib import admin
from .models import Profile
# Register your models here.
admin.site.register(Profile)
to see the Profiles in the admin page
Got it fixed by setting the user in the Customer model when a user is created
Customer.objects.create(user=request.user, name=username, email=email, phone=phone)

create django user based on existing model object

I'm working on Property Management django app where my base model is Tenant with all basic info like name, surname, email etc. Willing tenants will be able to create user account so they can log in and book Gym/Cinema, but not all Tenants will need to have user account. My problem is:
How can I create new user accounts based on existing Tenant objects? Obviously user will have Tenant ForeignKey but how can I extract Tenant.name, Tenant.surname etc to more than 1 field in my user model?
ForeignKey only gives me reference to object but can I somehow access certain fields of that object during creation of new user so I make sure that Tenant.email is the same as user.email?
Edit
tenancy = (('Tenant', 'Tenant'),('Owner', 'Owner'), ('Other', 'Other'))
class Tenant(models.Model):
name = models.CharField(max_length=15, null=True, blank=False)
surname = models.CharField(max_length=30, null=True, blank=False)
email = models.EmailField(max_length=50, unique=True)
phone_number = models.CharField(max_length=20, null=True, blank=True, unique=True)
flat = models.ForeignKey(Flat, on_delete=models.PROTECT)
status = models.CharField(max_length=10, choices=tenancy, null=True, blank=False)
stay_length = models.CharField(max_length=20, null=True, blank=False)
pet_licence = models.CharField(max_length=20, null=True, blank=False)
additional_notes= models.TextField(max_length=300, blank=True)
date_added = models.DateField(auto_now_add=True, null=True)
moved_out = models.BooleanField(default=False)
date_moved_out= models.DateField(auto_now_add=False, null=True)
class Meta:
unique_together = ('name', 'surname',)
def __str__(self):
return f'{self.name} {self.surname}'
Now I'd like to create user account model where name, surname, email, phone_number and flat will be ForeignKeys of Tenant model. Is it even possible to have 4 ForeignKeys from 1 object populating new model?
I've tried playing around with ForeignKey.limit_choices_to, ForeignKey.related_name, ForeignKey.to_field (this was close but field related to has to be unique which doesn't work for my case) but everything gives errors. I just want to find out if it's even possible that more than 1 ForeignKey of 1 object can be directed to multiple different fields of new model object.
I would approach it in a way that the foreign key is in Tenant, instead of User, and define it as a nullable one-to-one. This way you keep your User model free of foreign keys:
class Tenant(models.Model):
user = models.OneToOneField(
User,
related_name='tenant',
on_delete=models.CASCADE,
null=True,
blank=True,
default=None,
)
Then to create the related user, you can add a method in your Tenant model like so:
class Tenant(models.Model):
...
def create_user(self):
if not self.user:
user = User.objects.create(
first_name=self.name,
last_name=self.surname,
...
)
self.user = user
self.save()
Have a look at the example from the docs here.
You can use to_field to create fkeys to non-pk fields of another model, however, those fields need to have a unique constraint (i.e. unique=True) - which seems unlikely for name/surname.
It sounds like you want a transparent access from the User model to the Tenant models fields, and that is not possible.
You can create a fkey from User to Tenant:
class User(models.Model):
tenant = models.OneToOneField(Tenant, null=True, blank=True, related_name='user')
...
and then access the fields with
user = User.objects.get(...)
user.tenant.surname
to keep the fields in sync you can override the save() method:
class Tenant(...)
...
def save(self, *args, **kwargs):
if self.user:
self.user.last_name = self.surname
...
self.user.save()
super().save(*args, **kwargs)
aside: null=True indicates that the database should allow null in the field, blank=True says that the field can be empty in the admin interface. You should probably have null=True, blank=True in most cases.

create a django user while creating a records for model

models.py as below,
from django.db import models
from django.contrib.auth.models import User
class members(models.Model):
auto_id = models.AutoField(primary_key=True)
member_name = models.OneToOneField(User)
owner = models.ForeignKey('auth.User', related_name='webapi', on_delete=models.CASCADE)
father_name = models.CharField(max_length=25, blank=False, default='')
wife_name = models.CharField(max_length=25, blank=False, default='')
number_of_child = models.IntegerField(blank=True)
address_line_1 = models.CharField(max_length=40, blank=False, default='')
address_line_2 = models.CharField(max_length=40, blank=True, default='')
city = models.CharField(max_length=25, blank=False, default='')
class Meta:
ordering = ('member_name',)
Now The above has been linked with django auth Users table and in steriliser it shows the existing Django users and I have to choose one during the submit.
But my requirement is as a admin user I login and then provide the member_name manually which should automatically create a django user also
django-annoying has a feature for this, specifically AutoOneToOneField.
Sample from their github:
from annoying.fields import AutoOneToOneField
class MyProfile(models.Model):
user = AutoOneToOneField(User, primary_key=True)
This should automatically create a User.

Django custom user model with unique_together on the email

I'm trying to create a custom User model in my Django app, the problem is I get an error saying email must be unique (fair enough!), however, I need email and company together to be unique, as I may have the same email but registered to a different company.
I get the following error:
ERRORS:
site.SiteUser: (auth.E003) 'SiteUser.email' must be unique because it is named as the 'USERNAME_FIELD'.
Here is my model:
class SiteUser(models.Model):
company = models.ForeignKey(Company)
email = models.EmailField(max_length=254)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(auto_now=False, auto_now_add=True)
objects = SiteUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
unique_together = ('company', 'email',)
You're missing the unique=True in your email field definition.
The filed that is used in the USERNAME_FIELD should have this argument as explained in the django doc on USERNAME_FIELD.
It should be like this:
email = models.EmailField(max_length=254, unique=True)
Add auth.E003 to the SILENCED_SYSTEM_CHECKS setting. This will allow manage.py to run. And I think you should add W004 warning to this list too:
SILENCED_SYSTEM_CHECKS = ['auth.E003', 'auth.W004']

Using Django ORM to get a related model in a method

Giving the following models...
class ProjectComment(RewardBase):
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True)
class User (AbstractBaseUser):
email = models.EmailField()
class Profile(models.Model):
bio = models.CharField(max_length=80)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, unique=True)
From project I want to get the Users Profile bio, this is how I'm doing it....
def get_profile_bio(self):
return Profile.objects.get(user=self.user)
I now print a list of all projects I can get a profile bio, but is this the right way to do it? I'm worried that for every project it makes a new SQL call to the DB, is this correct?
class ProjectComment(RewardBase):
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="projects")
class User (AbstractBaseUser):
email = models.EmailField()
class Profile(models.Model):
bio = models.CharField(max_length=80)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, unique=True, related_name="profile")
Then you can fetch the users and the profiles:
projects = ProjectComment.select_related('user', 'user__profile').exclude(user__isnull=True).all()
for project in projects:
users = [user for user in project.user.all()]
And then:
for user in users:
profiles = [profile for profile in user.profile.all()]
Why do you have a unique constrain in your ForeignKey? if you need Uniquness create a OneToOneField

Categories