How to bind UserProfile to User and another model? - django

I have created a Company model and a profile model.
Every User belongs to one company and a company can belong to many users.
Which of the two is the correct way of modelling it?
class Company(models.Model):
company_name = models.CharField(max_length=50)
company_code = models.CharField(max_length=40)
company_email = models.EmailField()
def save(self, *args, **kwargs):
if not self.company_code:
self.company_code = uuid.uuid1()
super(Company, self).save(*args, **kwargs)
Now the UserProfile is defined like this:
class UserProfile(models.Model):
# This field is required.
user = models.OneToOneField(User)
# Other fields here
company = models.ManyToManyField(Company)
# !!!! OR !!!!
company = models.ForeignKey(Company)
Update:
class UserProfile(models.Model):
user = models.OneToOneField(User)
company = models.ForeignKey(Company)
def create_user_profile(self, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
I have now added this bit to UserProfile class also added AUTH_PROFILE_MODULE = 'MyApp.UserProfile' to the settings.
When I do syncdb I get an error message:
>> company = models.ForeignKey(Company)
NameError: name 'Company' is not defined

A ManyToManyField would allow a User to belong to multiple Companys. Based on your spec a ForeignKey would be appropriate.

Related

How do I best restrict by user and by data model using Django?

I'm using django-guardian and I encountered some issues with the default mixins. And I want to know if there's a better way to do this.
GitHub Link: https://github.com/iaggocapitanio1/django_homepage
Problem:
If I want to limit access at both the model and object levels, using these two mixins (PermissionRequiredMixin, PermissionListMixin) is not a very easy task. Because the permissions_required attribute is overridden. To get around this I had to create a new attribute "object_permission" and do the following:
Model Looks like:
# Create your models here.
from django.db import models
from localflavor.br import models as localModels
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
class Customer(models.Model):
user: User = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.user.first_name} {self.user.last_name}'
class Company(models.Model):
user: User = models.OneToOneField(User, on_delete=models.CASCADE)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='comapnies')
def __str__(self):
return f'{self.user.first_name} {self.user.last_name}'
class Project(models.Model):
name = models.CharField(max_length=100)
owner = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='projects')
class Meta:
permissions = (('read_project', 'Read Project'),)
def __str__(self):
return self.name
class House(models.Model):
rooms = models.IntegerField()
postal_code = localModels.BRPostalCodeField()
project = models.ForeignKey(Project, on_delete=models.CASCADE)
Here I needed to create a new attribute ("object_permission") to limit object-level access
in the View:
class ProjectsListView(PermissionRequiredMixin, PermissionListMixin, ListView):
template_name = 'home/projects.html'
model = models.Project
permission_required = ["homepage.view_project"]
object_permission = ["read_project"]
redirect_field_name = 'next'
login_url = 'login/'
get_objects_for_user_extra_kwargs = {}
def get_object_permission(self, request: HttpRequest = None) -> List[str]:
if isinstance(self.object_permission, str):
perms = [self.object_permission]
elif isinstance(self.object_permission, Iterable):
perms = [p for p in self.object_permission]
else:
raise ImproperlyConfigured("'PermissionRequiredMixin' requires "
"'permission_required' attribute to be set to "
"'<app_label>.<permission codename>' but is set to '%s' instead"
% self.permission_required)
return perms
def get_get_objects_for_user_kwargs(self, queryset):
return dict(user=self.request.user,
perms=self.get_object_permission(self.request),
klass=queryset,
**self.get_objects_for_user_extra_kwargs)
#receiver(post_save, sender=models.Project)
def project_post_save(sender, **kwargs):
"""
Create a Profile instance for all newly created User instances. We only
run on user creation to avoid having to check for existence on each call
to User.save.
"""
project: models.Project = kwargs["instance"]
created: bool = kwargs["created"]
if created:
user = models.User.objects.get(pk=project.owner.user.id)
assign_perm("read_project", user, project)
Am I using the right approach to filter data relative to each user? How do I combine both the page access limitation and the relative data of each user in a class model view?

Unique together in models

I have a model called Company.
In a second model which is Branch, I use Company as a foreign key.
class Branch(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
Now in some other model, I want to set a property(name) unique together with the Company but I use the branch as a foreign key.
class ABC(models.Model):
name = models.CharField()
branch = models.ForeignKey(Branch, on_delete=models.CASCADE)
class Meta:
unique_together = (
('branch__company', 'name'),
)
Can I do something like the above? It gives me an error that the field is nonexistent. Or can I use both company and branch in my model as foreign key?
class ABC(models.Model):
name = models.CharField()
branch = models.ForeignKey(Branch, on_delete=models.CASCADE)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
class Meta:
unique_together = (
('company', 'name'),
)
I want to attach ABC object with a branch but if once added it should be unique to that company (other branches of that company can not have the same name).
Read about the circular error and was thinking of the same here.
Unique together will be depreciated in the future but I'm not thinking about this right now.
Any advice?
I suggest you to perform validation in the clean method (without a database constraint):
from django.core.exceptions import ValidationError
class ABC(models.Model):
name = models.CharField()
branch = models.ForeignKey(Branch, on_delete=models.CASCADE)
def clean(self):
super().clean()
if ABC.objects.filter(name=self.name, branch__company=self.branch.company).exists():
raise ValidationError('Error message')
def save(self, *args, **kwargs):
# Forces the clean method to be called
self.full_clean()
super().save(*args, **kwargs)

In django-forms, how can I make the values of 'name' column of foreign database visible in the selection choices

My Django site has a database model named Status with:
class Status(models.Model):
x = models.ForeignKey(Person, on_delete=models.SET_NULL, null = True)
The Person database model referenced here contains attributes such as name, profile_pic etc.
In forms.py, I have:
class StatusForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StatusForm, self).__init__(*args, **kwargs)
class Meta:
model = Status
fields = ['x']
Now, when running the site, I get:
I want the name attribute of the Person to be shown in the choices instead of being shown Person object(1), Person object (2).....
How can I make that happen?
From Other model instance methods[Django-doc]
Most notably, to display an object in the Django admin site and as the value inserted into a template when it displays an object
In your Person model use str()[Django-doc] as
class Person(models.Model):
name = models.charField(max_length=100)
.....
def __str__(self):
return self.name
In your Person model just add the str method as following:
class Person(models.Model):
# Your fields here
def __str__(self):
return self.name
That will return the name for every instance in the choicefield.

Create a separate user type

I am working on an intranet web application which needs two types of users. Normal users that can be setup from django admin and specific type of users -
Employees.
I have the following model for Employee type user.
class Employee(models.Model):
emp_name = models.CharField(max_length=500)
slug = models.SlugField(unique=True, default='')
location = models.CharField(max_length=200)
email = models.EmailField()
experience = models.TextField(blank=True)
primary_skill = models.ManyToManyField(PrimarySkill)
secondary_skill = models.ManyToManyField(SecondarySkill)
I tried having a OneToOneField like this as per the official doc and
this article:
user = models.OneToOneField(User, blank=True, null=True, on_delete=models.CASCADE)
#receiver(post_save, sender=User)
def create_employee(sender, instance, created, **kwargs):
if created:
Employee.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_employee(sender, instance, **kwargs):
instance.employee.save()
I realized that this is the opposite of what I want. Every time a User
is created from the admin, there was an entry created in the
app_employee table.
What I want is this:
Every time an Employee is created, I need a User created.
An Employee can be created using a separate signup form, say emp_signup
How do I approach this scenario?
I have achieved this using a custom user based on AbstractUser inspired by this article.
class CustomUser(AbstractUser):
pass
class Employee(CustomUser):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
# other fields
In settings.py, I then add the following key:
AUTH_USER_MODEL = 'myapp.CustomUser'
And wherever I need to refer the User class, I use get_user_model(), which will substitute our custom user, in views and forms as follows:
views.py
from django.contrib.auth import get_user_model
def user_profile(request):
User = get_user_model()
user = get_object_or_404(User, username=request.user.username)
return render(request, 'user/user_profile.html', {
'site_user': user
})
forms.py
class SignUpForm(UserCreationForm):
class Meta:
model = get_user_model()
fields = ('username', 'email', 'password1', 'password2',)

How to create user profile with django-registration?

In django 1.7 I use django-registration to handle user registration and I'd like to automatically create a UserProfile instance for each newly registered user.
So in models I have:
class UserProfile(models.Model):
username = models.OneToOneField(User)
name = models.CharField(max_length=30)
occupation = models.CharField(max_length=50)
city = models.CharField(max_length=30)
#models.permalink
def get_absolute_url(self):
return ('view_pirate', None, {'username': self.account.user})
def __unicode__(self):
return unicode(self.username)
#**this line is supposed to create user profile****
User.profile = property(lambda u:UserProfile.objects.get_or_create(username=u)[0])
But when I check the database no new row in userprofile_userprofile is created after a new user registration. What is wrong here?
If the line is in wrong place, where should it be?