How to create relationships between django models - django

I have the two models, One is the User model and the other is a Contact model.
The Contact model is as follows:
class Contact(models.Model):
pass
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1, null=True, on_delete=models.CASCADE, verbose_name=_('User'), related_name="user")
contact = models.ForeignKey(settings.AUTH_USER_MODEL, default=1, null=True, on_delete=models.CASCADE, verbose_name=_('Contact'), related_name="contact")
created_at = models.DateTimeField(auto_now=False, auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True, auto_now_add=False)
is_contact = models.BooleanField(default=False)
Basically, what is does is create a contact for a user. Kind of a friend request and accept simple model. So the loggedIn user(request.user) could be either the contact.user or contact.contact.
And the settings.AUTH_USER_MODEL is a CustomUser model:
class CustomUser(AbstractUser):
pass
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=20, default="", blank=True)
last_name = models.CharField(max_length=20, default="", blank=True)
How can I create a relationship, where I can get a users contact by doing something like this:
// get user contacts user=CustomUser.objects.get(pk=1)
user.contacts.objects.all()

Typically you query reverse relations using the related_name keyword, which is contact_set by default:
user.contact_set.all()
If you want to change the name of this reverse relationship, you need to do so in the Contact model using related_name:
class Contact(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="contacts", ...)
user.contacts.all()

Related

How to make a query across multiple models in Django

I'm using Django and I want to know how to get objects through 3 models
These are my models
class Participant(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
is_leader = models.BooleanField(default=False)
team = models.ForeignKey(Team, on_delete=models.CASCADE, null=True, related_name="participants")
application_date = models.DateField(auto_now_add=True, null=True)
resolution_date = models.DateField(null=True, blank=True)
accepted = models.BooleanField(default=False)
class Team(models.Model):
name = models.TextField(default="")
is_public = models.BooleanField(default=False)
institution = models.ForeignKey(Institution, on_delete=models.CASCADE, null=True, related_name='teams')
campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE, null=True, related_name='teams')
class Campaign(models.Model):
name = models.TextField(default="")
description = models.TextField(default="")
initial_date = models.DateTimeField(auto_now_add=False, null=True, blank=True)
end_date = models.DateTimeField(auto_now_add=False, null=True, blank=True)
qr_step_enabled = models.BooleanField(default=True)
image_resolution = models.IntegerField(default=800)
sponsor = models.ForeignKey(Sponsor, on_delete=models.CASCADE, null=True, related_name='campaigns')
I have the user through a request, and I want to get all campaigns of that user.
I tried doing it with for loops but I want to do it with queries
this is what I had:
user = request.user
participants = user.participant_set.all()
for participant in participants:
participant.team.campaign.name
is there a way to make a query through these models and for all participants?
A user can have many participants, and each participant has a Team, each team has a campaign
The best way is to merge the two modela Team and Campaign in one model.
Something as simple as this should work:
Campaign.objects.filter(team__participant__user=request.user)
The Django ORM is smart enough to follow foreign key relationships in both directions.
Thanks to Daniel W. Steinbrook to guide me to the answer, I had to do this to get the query:
Campaign.objects.filter(teams__participants__user__exact=request.user)

how to write a signal to change a field in a model after another filed from another model is changed

I have a user model that has to go through several tasks such as completing their information, taking some tests, and interviews. So I added a progress level field that shows the user status at the moment. this is my model:
class User(AbstractUser):
id = models.AutoField(primary_key=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
isPreRegistered = models.BooleanField(default=False)
role = models.CharField(max_length=25, null=True, choices=USER_ROLE_CHOICES, default=USER_ROLE_CHOICES[0][0])
role_id = models.CharField(max_length=10, default='applicant')
username = models.CharField(unique=True, max_length=13)
first_name = models.CharField(max_length=32, null=True, default=None)
last_name = models.CharField(max_length=64, null=True, default=None)
gender = models.CharField(max_length=10, null=True)
personalInfo = models.OneToOneField(PersonalInfo, on_delete=models.CASCADE, null=True)
contactInfo = models.OneToOneField(ContactInfo, on_delete=models.Case, null=True)
eliteInfo = models.OneToOneField(EliteInfo, on_delete=models.CASCADE, null=True)
progress_level = models.CharField(max_length=25, null=True, choices=USER_PROGRESS_LEVELS, default=USER_PROGRESS_LEVELS[0][0])
and there are multiple models which are connected to the user model using a foreign key relation.
this is one of the models I added here for instance:
class PsychologicInfo(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
final_assessment = models.TextField(null=True, blank=True)
is_approved = models.CharField(max_length=64, blank=True)
is_interviewed = models.BooleanField(default=False)
I want to write a signal or a save method that does something like this:
if the is_interviewed field was True, change that progress_level to USER_ROLE_CHOICES[1][0]
I have no idea how to do this so thanks for the tips
You can override the .save() method of PsychologicInfo
class PsychologicInfo:
...
def save(self):
super().save()
if self.is_interviewed:
self.user.role = USER_ROLE_CHOICES[1][0] # or enter value here
self.user.save()
or you can use Django signals.

Django: Get objects of first model by comparing attribute of second OneToOne related model

I have two models, User and Card. One user has one card. I need to get objects of User if the 'card_written' of Card model is False in a view.
class User(model.Model):
phone = PhoneNumberField(null=False, blank=False, unique=True)
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class Card(models.Model):
user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
card_number = models.CharField(max_length=200)
card_written = models.BooleanField(default=False, null=False)
card_write_date = models.DateTimeField(null=True, blank=True)
card_delivered = models.BooleanField(default=False, null=False)
You can query on related models quite easily in Django by separating related fields by double underscores.
A query like this should work for you:
User.objects.filter(card__card_written=False)
Here card is the related name since you haven't specified it in the Card model if you want it to be something else you need to specify like so in the Card model:
user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE, related_name='something_else')

Django: reference model fields from another model

I have two models. What i need is to reference the name and the email field from the Users model to the Customer model fields. Is the following way correct?
class Users(AbstractBaseUser):
name = models.CharField(max_length=200)
email = models.CharField(max_length=200)
from users.models import Users
class Customer(models.Model):
user = models.OneToOneField(
Users, on_delete=models.CASCADE, null=True, blank=True)
name = models.OneToOneField(
Users.name, on_delete=models.CASCADE, null=True, blank=True)
email = models.OneToOneField(
Users.email, on_delete=models.CASCADE, null=True, blank=True)
class Users(AbstractBaseUser):
name = models.CharField(max_length=200)
email = models.CharField(max_length=200)
class Customer(models.Model):
user = models.OneToOneField(
Users, on_delete=models.CASCADE, null=True, blank=True)
That's all. Then you can do for example
Customer.objects.get(pk=1).user.name

Using two database tables for one control in Django Admin

I am creating a small application to help our Training Department manage their cirriculum using Django. When we talk about students we have two type; Employee and Customer.
Since all of the employees will be in auth_user I would rather not have to populate another table with that data. The behavior that I want is when a Class is displayed in the Django Admin I would like one control to be filled with data from two tables for the student list. Is this even possible in Django. My suspicion is that it is not. This is what I am messing around with:
from django.db import models
from django.contrib.auth.models import User
class Student(models.Model):
cell_phone = models.CharField(max_length=14)
class Meta:
abstract = True
class EmployeeStudent(models.Model, Student):
user = models.OneToOneField(User)
extension = models.CharField(max_length=4, null=True, blank=True, default=None)
class CustomerStudent(models.Model, Student):
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
email = models.CharField(max_length=25)
However, just thinking about it does it make more sense to do:
from django.db import models
from django.contrib.auth.models import User
class Student(models.Model):
user = models.ForeignKey(User, null=True, blank=True, default=None)
first_name = models.CharField(max_length=25, null=True, blank=True, default=None)
last_name = models.CharField(max_length=25, null=True, blank=True, default=None)
email = models.CharField(max_length=25, null=True, blank=True, default=None)
extension = models.CharField(max_length=4, null=True, blank=True, default=None)
I cringe at the thought of a blank record.
Any recommendations? Does it make sense to add everything to auth_user and leave the staff flag to false then just use a one to one field to map an auth_user to a Student? I do not really want to do that if I don't have to because I am not going to give anyone else access to the auth_user table so all additions to this table would need to be done by me.
You could try to use model inheritance (i.e User model inheritance : https://docs.djangoproject.com/en/dev/topics/auth/customizing/#extending-user) for your Student model, or mixing Student(models.Model) with User in your EmployeeStudent and CustomerStudent models :
class Student(models.Model):
cell_phone = models.CharField(max_length=14)
class Meta:
abstract = True
class EmployeeStudent(User, Student):
user = models.OneToOneField(User)
extension = models.CharField(max_length=4, null=True, blank=True, default=None)
class CustomerStudent(User, Student):
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
email = models.CharField(max_length=25)
or :
class Student(User):
cell_phone = models.CharField(max_length=14)
class Meta:
abstract = True
class EmployeeStudent(models.Model):
user = models.OneToOneField(Student)
extension = models.CharField(max_length=4, null=True, blank=True, default=None)
class CustomerStudent(models.Model):
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
email = models.CharField(max_length=25)
If I understand correctly, you'd then want to display Customers as well as Employees in the same changelist in admin ? Using Student(User) with Employee / Customer as inlines might solve your problem.
Hope this helps,
Regards
This is a fairly common use-case (i.e. different types of userprofiles) in Django. In your case, I think the approach below would suit your scenario:
from django.db import models
from django.contrib.auth.models import User
class Student(models.Model):
STUDENT_TYPES = (
('E', 'EmployeeStudent'),
('C', 'CustomerStudent'),
)
user = models.OneToOneField(User, null=True, blank=True, default=None)
user_type = models.CharField(max_length=1, choices=STUDENT_TYPES)
class EmployeeDetails(models.Model):
student = models.OneToOneField(Student)
extension = models.CharField(max_length=4, null=True, blank=True, default=None)
class StudentDetails(models.Model):
student = models.OneToOneField(Student)
# BTW: all the fields below are redundant since they are already in User
first_name = models.CharField(max_length=25, null=True, blank=True, default=None)
last_name = models.CharField(max_length=25, null=True, blank=True, default=None)
email = models.CharField(max_length=25, null=True, blank=True, default=None)
This way, you can check the student.user_type and infer if you need to get EmployeeDetails or StudentDetails
NOTE:: Even though this is a recommended approach, it is not quite easy to enter data using the default admin interface in this manner. You might want to see how profile inlines are done to show the user profile fields in the admin as well.