Django Multi join query - django

I am new to django and need to do a query. I can do in sql but I can't seem to figure it out in django. Here it is in SQL. Can someone tell me how to do it in django.
select token from api_userprofile
join api_userdevice on api_userdevice.user_id=api_userprofile.user_id
join api_device on api_device.id=api_userdevice.device_id
where api_device.serial=3
My models look like this:
class UserDevice(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=False)
device = models.ForeignKey(Device, on_delete=models.PROTECT, null=False)
activation_date = models.DateTimeField(default=timezone.now, null=False)
friendly_name = models.CharField(max_length=20, null=True, blank=True)
is_owner = models.BooleanField(null=False, default=False)
is_admin = models.BooleanField(null=False, default=True)
class Device(models.Model):
serial = models.CharField(max_length=16, null=False, unique=True)
publickey = models.CharField(max_length=44, null=False)
class UserProfile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.PROTECT, null=False)
token = models.TextField(null=False, blank=True)

if i understand correctly, you can try:
users_pks = UserDevice.objects.filter(device__serial=3).values_list('user__pk')
qs = UserProfile.objects.filter(user__pk__in=users_pks).values('token')
for check sql query, you can print:
print (qs.query)

Related

get the related records in a serializer - django

I am trying to obtain in a query the data of a client and the contacts he has registered I tried to do it in the following way but it did not work.
class ClientReadOnlySerializer(serializers.ModelSerializer):
clientcontacts = ClientContactSerializer(many=True, read_only=True)
class Meta:
model = Client
fields = "__all__"
Is there any way to make this relationship nested?
these are my models
clients model
# Relations
from apps.misc.models import City, TypeIdentity, TypeCLient, CIIU
from apps.clients.api.models.broker.index import Broker
from apps.authentication.models import User
class Client(models.Model):
id = models.CharField(max_length=255, unique=True,primary_key=True, editable=False)
type_client = models.ForeignKey(TypeCLient, on_delete=models.CASCADE, blank=True)
type_identity = models.ForeignKey(TypeIdentity, on_delete=models.CASCADE, blank=True)
document_number = models.CharField(max_length=255, blank=True, unique=True)
first_name = models.CharField(max_length=255, blank=True, null=True)
last_name = models.CharField(max_length=255, blank=True, null=True)
social_reason = models.CharField(max_length=255, blank=True, null=True)
city = models.ForeignKey(City, on_delete=models.CASCADE, blank=True)
address = models.CharField(max_length=255, blank=True)
email = models.CharField(max_length=255, blank=True, unique=True)
phone_number = models.CharField(max_length=255, blank=True, unique=True)
ciiu = models.ForeignKey(CIIU, on_delete=models.CASCADE, blank=True)
broker = models.ForeignKey(Broker, on_delete=models.CASCADE, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
income = models.SmallIntegerField(blank=True, default=0)
state = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(null=True, default=None)
client contacts model
# Relations
from apps.clients.api.models.index import Client
class ClientContact(models.Model):
id = models.CharField(max_length=255, primary_key=True, unique=True, editable=False)
client = models.ForeignKey(Client, on_delete=models.CASCADE)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
phone_number = models.CharField(max_length=255)
state = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(null=True, blank=True, default=None)
after some research I came up with a solution that doesn't seem very practical, what I did was to create a method in the serializer that queries the client's contacts as follows
class ClientReadOnlySerializer(serializers.ModelSerializer):
contacts = serializers.SerializerMethodField(method_name='get_contacts')
class Meta:
model = Client
fields = "__all__"
extra_fields = ['contacts']
def get_contacts(self, obj):
contacts = ClientContact.objects.filter(client=obj)
serializer = ClientContactSerializer(contacts, many=True)
return serializer.data
using the model and the serializer of the contact model I made a query which I added in an extra field of the customer serializer, if someone knows how to make this process more optimal please reply.

Joining more than 2 tables for reports in django and extract all the fields from the joined table

I am joining the ClientDetails, AssignmentTable and CallDetails table to get a view as to which telecaller a particular client has been assigned to and get the latest call details as well. However I am unable to accomplish that using django ORM.
ISSUE:
I am trying to access the fields inside the assignment table and call table but I am getting only the ids and not the other fields.
Question:
How do I extract all the columns from the assignment and call details table which has the client id as 1?
This is the SQL Query that I am trying to come up with:
SELECT t1.uid, t1.phone_number, t1.client_name, t1.base, t1.location, t2.assigner, t2.bpo_agent, t2.cro_agent, t3.bpo_status_id, t3.cro_status_id, t3.agent_id_id
FROM public.bpo_app_clientdetails t1
LEFT JOIN public.bpo_app_assignmentdetails t2 ON t1.uid = t2.client_id_id
LEFT JOIN public.bpo_app_calldetails t3 ON t1.uid = t3.client_id_id;
Below is the model file:
class ClientDetails(models.Model):
uid = models.AutoField(primary_key=True)
phone_number = PhoneNumberField(unique=True)
client_name = models.CharField(max_length=50, blank=True, null=True)
base = models.CharField(max_length=50, blank=True, null=True)
location = models.CharField(max_length=50, blank=True, null=True)
class Meta:
verbose_name_plural = "Client Contact Detail Table"
def __str__(self):
return f"{self.phone_number}, {self.client_name}"
class AssignmentDetails(models.Model):
uid = models.AutoField(primary_key=True)
client_id = models.ForeignKey(
ClientDetails,
on_delete=models.PROTECT,
related_name='assignment_details'
)
date_and_time = models.DateTimeField(auto_now_add=True, blank=True)
assigner = models.ForeignKey(
User,on_delete=models.PROTECT,
related_name='AssignerAgent',
db_column='assigner',
)
bpo_agent = models.ForeignKey(
User,on_delete=models.PROTECT,
related_name='bpoAgent',
db_column='bpo_agent',
)
cro_agent = models.ForeignKey(
User,on_delete=models.PROTECT,
related_name='croAgent',
db_column='cro_agent',
)
class Meta:
verbose_name_plural = "Client Assignment Detail Table"
def __str__(self):
return f"{self.uid}"
class CallDetails(models.Model):
uid = models.AutoField(primary_key=True)
date_and_time = models.DateTimeField(auto_now_add=True, blank=True)
client_id = models.ForeignKey(
ClientDetails,
on_delete=models.PROTECT,
related_name='call_details'
)
agent_id = models.ForeignKey(EmployeeDetails_lk,on_delete=models.PROTECT)
bpo_status = models.ForeignKey(BpoStatus_lk,on_delete=models.PROTECT, blank=True, null=True)
cro_status = models.ForeignKey(CroStatus_lk,on_delete=models.PROTECT, blank=True, null=True)
required_loan_amt = models.CharField(max_length=50, blank=True, null=True)
remarks = models.CharField(max_length=500, blank=True, null=True)
loan_program = models.ForeignKey(LoanProgram_lk, on_delete=models.PROTECT, blank=True, null=True)
disbursement_bank = models.ForeignKey(Banks_lk, on_delete=models.PROTECT, limit_choices_to={'loan_disbursement_status': True}, blank=True, null=True)
class Meta:
verbose_name_plural = "Client Call Detail Table"
def __str__(self):
return f"{self.uid}"
>>> qry=ClientDetails.objects.values('assignment_details','call_details').filter(uid=1)
>>> qry
<QuerySet [{'assignment_details': 1, 'call_details': None}]>
>>> print(a.query)
SELECT "bpo_app_assignmentdetails"."uid", "bpo_app_calldetails"."uid" FROM "bpo_app_clientdetails" LEFT OUTER JOIN "bpo_app_assignmentdetails" ON ("bpo_app_clientdetails"."uid" = "bpo_app_assignmentdetails"."client_id_id") LEFT OUTER JOIN "bpo_app_calldetails" ON ("bpo_app_clientdetails"."uid" = "bpo_app_calldetails"."client_id_id") WHERE "bpo_app_clientdetails"."uid" = 1
You can use prefetch_related() to achieve this. I just use some sample models here for better understanding.
class Company(models.Model):
name = models.CharField(null=True, blank=True, max_length=100)
class Project(models.Model):
name = models.CharField(null=True, blank=True, max_length=100)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
class Employee(models.Model):
name = models.CharField(null=True, blank=True, max_length=100)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
In your views.py function write the below lines to get the desired results
companies = Company.objects.filter(id=1).prefetch_related('project_set', 'employee_set')
for company in companies:
print(company.project_set.values()) # This will print this company projects
print(company.employee_set.values()) # This will print this company employees
Note: If you use related_name in your ForeignKey relationship, make sure that you access with that name instead of model_set inside prefetch_related()

How do I get new Database changes since last login using Django and Python?

I need to get database changes for a user, but only the updates of the user since the last time they logged in. I will be passing in just an email. I have looked into session data but none of that looks very helpful to me. I am new to Python and Django and I have no idea where to start, any help would be appreciated. Here are my models:
class Device(models.Model):
serial = models.CharField(max_length=16, null=False, unique=True)
publickey = models.CharField(max_length=44, null=False)
def __str__(self):
return '%d: %s' % (self.id, self.serial)
class Pairing(models.Model):
device = models.OneToOneField(Device,on_delete=models.PROTECT,blank=False, null=False)
pairingcode = models.CharField(max_length=10, blank=False, null=False, unique=True)
def __str__(self):
return '%s: %s' % (self.device_id, self.pairingcode)
class UserDevice(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT,
null=False)
device = models.ForeignKey(Device, on_delete=models.PROTECT, null=False)
activation_date = models.DateTimeField(default=timezone.now, null=False)
friendly_name = models.CharField(max_length=20, null=True, blank=True)
is_owner = models.BooleanField(null=False, default=False)
is_admin = models.BooleanField(null=False, default=True)
is_alerts_enabled = models.BooleanField(null=False, default=True)
class Meta:
unique_together = ('user', 'device',)
def __str__(self):
return '%s => %s on %s' % (self.user.email, self.device.serial,
str(self.activation_date))
class Schedule(models.Model):
device = models.ForeignKey(Device, on_delete=models.PROTECT, null=False)
time = models.TimeField(null=False)
class Meta:
unique_together = ('device', 'time')
class PendingSchedule(models.Model):
device = models.ForeignKey(Device, on_delete=models.PROTECT, null=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.PROTECT, null=False)
time = models.TimeField(null=False)
class Meta:
unique_together = ('device', 'time')
class Tray(models.Model):
device = models.ForeignKey(Device, on_delete=models.PROTECT, null=False)
slot = models.IntegerField(null=False)
full = models.BooleanField(null=False)
time = models.DateTimeField(null=False)
class Meta:
unique_together = (('device', 'slot'), ('device', 'time'))
def __str__(self):
return 'Cup %s of %s %s' % (self.slot, str(self.device), "Full" if
self.full else "Empty")
class TrayStatus(models.Model):
device = models.ForeignKey(Device, on_delete=models.PROTECT, null=False)
slot = models.IntegerField(null=False)
reason = models.TextField(blank=False, null=False)
time = models.DateTimeField(null=False)
recorded = models.DateTimeField(auto_now_add=True, null=False)
expectedTime = models.DateTimeField(null=False)
class CheckIn(models.Model):
device = models.OneToOneField(Device, on_delete=models.PROTECT, null=False)
time = models.DateTimeField(null=False)
class UserProfile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.PROTECT, null=False)
token = models.TextField(null=False, blank=True)
first_name = models.TextField(null=True, blank=True)
last_name = models.TextField(null=True, blank=True)
You have no last_modified fields on any of the relevant models, so start there:
class UserDevice(models.Model):
# other fields...
created = models.DateTimeField(autonow_add=True, db_index=True)
last_modified = models.DateTimeField(autonow=True, db_index=True)
These will get filled automatically and don't appear in model forms (editable is forced to False). For each of the models you want to track for changes, you need to add the last_modified field and optional created field.
Now you can get the objects based on User.last_login:
# Get our user object based on email
user = User.objects.get(email=email)
# Get list of modified devices
modified_devices = UserDevice.objects.filter(last_modified__gte=user.last_login)
# ... and trays
modified_trays = Tray.objects.filter(last_modified__gte=user.last_login)
A Device will not change if a Tray linked to it is changed, so neither will the last_modified field of the Device.
The created field is for you to show the difference between modification and creation if you wish to do so:
new_devices = UserDevice.objects.filter(created__gte=user.last_login)
The challenge you face, if how to handle ownership changes of the device. You will probably need to keep track of that separately.
There are several third-party packages that allow you to do what you need, for instance django audit log and django reversion.
You just need to pick the one that fits better your needs, both of them are very easy-to-integrate and use.

Get data from two django models

My models look like this:
class UserDevice(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=False)
device = models.ForeignKey(Device, on_delete=models.PROTECT, null=False)
activation_date = models.DateTimeField(default=timezone.now, null=False)
friendly_name = models.CharField(max_length=20, null=True, blank=True)
is_owner = models.BooleanField(null=False, default=False)
is_admin = models.BooleanField(null=False, default=True)
is_alerts_enabled = models.BooleanField(null=False, default=True)
timeStamp = models.DateTimeField(auto_now = True, null=False)
class UserProfile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=False)
token = models.TextField(null=False, blank=True)
first_name = models.TextField(null=True, blank=True)
last_name = models.TextField(null=True, blank=True)
timeStamp = models.DateTimeField(auto_now = True, null=False)
I need to get device, user, is_alerts_enabled, is_admin, is_owner from UserDevice and first_name, last_name, token from userProfile. This is what I have so far and it gives me what I need from userdevice but I can't figure out how to add the userprofile stuff.
nonOwners = UserDevice.objects.filter(device=device, is_owner=False)
if nonOwners is None:
return errormsg('non nonOwners found for this device')
nonOwnersArray=[]
for nonOwner in nonOwners:
nonOwner_data = model_to_dict(nonOwner,
fieldlist=(
'device.serial',
'user.email',
'is_alerts_enabled',
'is_admin',
'is_owner',
),
rename={
'device.serial': 'serial',
'user.email': 'email'})
nonOwnersArray.append(nonOwner_data)
Any help would be greatly appreciated. Thanks!
The best way to get a dictionary of values is to use values().
UserDevice.objects.filter(device=device, is_owner=False).values('device', 'user', 'is_alerts_enabled', 'is_admin', 'is_owner')
The above line will give you the relevant fields of the UserDevice model as dictionary values.
If you add a foreign key to UserDevice (here I have changed the user field):
class UserDevice(models.Model):
user = models.ForeignKey(UserProfile, on_delete=models.PROTECT, null=False)
device = models.ForeignKey(Device, on_delete=models.PROTECT, null=False)
activation_date = models.DateTimeField(default=timezone.now, null=False)
friendly_name = models.CharField(max_length=20, null=True, blank=True)
is_owner = models.BooleanField(null=False, default=False)
is_admin = models.BooleanField(null=False, default=True)
is_alerts_enabled = models.BooleanField(null=False, default=True)
timeStamp = models.DateTimeField(auto_now = True, null=False)
You may do this:
UserDevice.objects.select_related('user').filter(device=device, is_owner=False).values('device', 'user', 'is_alerts_enabled', 'is_admin', 'is_owner', 'user__first_name', 'user__last_name', 'user__token')
I have used select_related() to avoid hitting the database unnecessarily.

Get data from 2 models with Django

I am new to Django and I need data from two models and would like to do it with one query. Here is what I have in sql that gives me exactly what I need. Can someone please show me how to do it in Django.
select api_userprofile.last_name, api_userprofile.first_name,
api_userdevice.is_admin,
api_userdevice.is_alerts_enabled,api_userdevice.is_owner from
api_userprofile
join api_userdevice on api_userdevice.user_id=api_userprofile.user_id
where api_userdevice.user_id=10 and api_userdevice.device_id=29
These are my 2 models:
class UserDevice(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=False)
device = models.ForeignKey(Device, on_delete=models.PROTECT, null=False)
activation_date = models.DateTimeField(default=timezone.now, null=False)
friendly_name = models.CharField(max_length=20, null=True, blank=True)
is_owner = models.BooleanField(null=False, default=False)
is_admin = models.BooleanField(null=False, default=True)
is_alerts_enabled = models.BooleanField(null=False, default=True)
class UserProfile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=False)
token = models.TextField(null=False, blank=True)
first_name = models.TextField(null=True, blank=True)
last_name = models.TextField(null=True, blank=True)
Any help would be greatly appreciated.
There is easy way to do that:
User.objects.filter(userdevice__id=29, id=10).values('userdevice__is_owner', 'userdevice__is_admin', 'userdevice__is_alerts_enabled', 'userprofile__first_name', 'userprofile__last_name')