How do I join two models in Django by id? - django

I have two models as shown below with just a few fields:
class Query(models.Model):
query_text = models.TextField(blank=True, null=True)
variable = models.CharField(max_length=250, blank=True, null=True)
class Statistic(models.Model):
query = models.ForeignKey(Query, models.DO_NOTHING, blank=True, null=True)
processing_time = models.DateTimeField(blank=True, null=True)
module = models.CharField(max_length=500, blank=True, null=True)
My target is to perform a JOIN using the id of the two models. The SQL query equivalent to it would be :
SELECT * FROM statistic S JOIN query Q ON S.query_id = Q.id
I understand select_related or prefetch_related could do the trick? I don't know which one to use to perform the join.
I'd appreciate some help on that. Thanks. :)

You can use select_related for this. You can make sure that JOIN is using by calling queryset's query attribute, which show you raw SQL statement:
print(Statistic.obects.select_related("query").query)

You've to use select_related here as you've a ForeignKey relationship. (prefetch_related is for ManyToMany fields).
So,
some_id_value = 12
stats_queryset = Statistic.obects.select_related("query").filter(id=some_id_value)

Related

Django convert raw SQL query to Django ORM

I am using Django Rest Framework and I have this query in raw SQL but I want to do it in the Django ORM instead.
I have tried using the different Django tools but so far it has not given me the expected result.
select tt.id, tt.team_id, tt.team_role_id, tt.user_id from task_teammember tt
inner join task_projectteam tp on tp.team_id = tt.team_id
where tp.project_id = 1
models
class TeamMember(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
team_role = models.ForeignKey(TeamRole,on_delete=models.CASCADE)
state = models.IntegerField(default=1)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(default=None, null=True)
class ProjectTeam(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE, blank=True, null=True)
team = models.ForeignKey(Team, on_delete=models.CASCADE, blank=True, null=True)
state = models.IntegerField(default=1)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(default=None, null=True)
If you have a project object already then this should get you what I believe you want. Your TeamMember model has access to Team, which links to ProjectTeam, and to Project - the double-underscore accessor navigates through the relationships.
TeamMember.objects.filter(team__projectteam__project=project)
I would advise to span a ManyToManyField over the ProjectTeam, this will make queries simpler:
from django.conf import settings
class TeamMember(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
updated_at = models.DateTimeField(auto_now=True)
# …
class Team(models.Model):
projects = models.ManyToManyField(Project, through='ProjectTeam')
# …
class ProjectTeam(models.Model):
# …
updated_at = models.DateTimeField(auto_now=True)
Then you can easily filter with:
TeamMember.objects.filter(team__projects=project_id)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: Django's DateTimeField [Django-doc]
has a auto_now=… parameter [Django-doc]
to work with timestamps. This will automatically assign the current datetime
when updating the object, and mark it as non-editable (editable=False), such
that it does not appear in ModelForms by default.
I think it's goes like:
TeamMember.objects.filter(team__projectteam__project__id=1)
Django orm allow reverse foreginkey lookup

In django 2.2. select_related field is not working? How to Fix it

I tried to join two tables in Django orm one is sample 1 and another one is sample 2 table. I used select_related functionality. but It's throwing an error on how to fix it.
Models.py
class Sample1( models.Model )
a = models.Charfield(max_length=10)
b = models.Charfield(max_length=20)
sample3 = models.ForeignKey(Sample3, null=True, blank=True, on_delete=models.SET_NULL)
sample4 = models.ForeignKey(Sample4, null=True, blank=True, on_delete=models.SET_NULL)
class Sample2( modes.Model )
sample1= models.ForeignKey( Sample1,on_delete=models.CASCADE )
created = models.DateTimeField()
updated = models.DateTimeField()
Views.py
def sample_data(request):
sampl_data = Sample1.objects.select_related("sample2__sample1").filter( created__lte = '2018-11-01
00:00:00-05:00', updated__gte = '2013-10-31 00:00:00-05:00')
print(samp1_data)
When I tried to run this program It's show this error django.core.exceptions.FieldError: Invalid field name(s) given in select_related: 'sample2'. Choices are: sample3, sample4
select_related is used only to follow relationship on Foreign key ( in your case sample3 and sample4 )
Returns a QuerySet that will “follow” foreign-key relationships,
selecting additional related-object data when it executes its query.
Performance optimizations of what you are trying to achieve can be done using prefetch_related

How to use Count over multiple fields in django?

How to use Count over many fields in Django? How to count row only if given multiple columns are unique?
For example for a model below.
class ProductViewed(models.Model):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,
blank=True, null=True, related_name="viewed")
ip_address = models.CharField(max_length=255, blank=True, null=True)
product = models.ForeignKey(Product, on_delete=models.CASCADE,
related_name="views")
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{str(self.product)} viewed on {self.created_at}'
class Meta:
ordering = ('-created_at', )
verbose_name_plural = "ProductViewed"
I want to achieve followings. Count based on user_id, ip_address, created_at__day. Is there any way to do so?
Currently I could achieve the following
Product.objects.annotate(vcount=Count('views__ip_address'))
One way to do this is to join all the fields that you want to perform the unique count on together using Concat and then counting on the result. The Trunc function is good for extracting part of a datetime
from django.db.models.functions import Trunc, Concat
Product.objects.annotate(
view_day=Trunc('views__created_at', 'day')
).annotate(
user_ip_and_day=Concat('views__user_id', 'views__ip_address', 'view_day', output_field=models.CharField())
).annotate(
vcount=models.Count('user_ip_and_day')
)
Can't you just do this?
Product.objects.annotate(vcount=Count('views__ip_address')).annotate(ucount=Count('views__user_id')).annotate(ccount=Count('views__created_at__day'))

Django ORM simple Join

I want to perform simple join operation like this.
raw SQL : select * from risks r join sku_details s on r.sku_id = s.sku_id;
model Details:
class SkuDetails(models.Model):
sku_id = models.DecimalField(primary_key=True, max_digits=65535, decimal_places=65535)
sku_desc = models.TextField(blank=True, null=True)
category = models.TextField(blank=True, null=True)
class Risks(models.Model):
risk_id = models.DecimalField(primary_key=True, max_digits=65535, decimal_places=65535)
risk_group_short_desc = models.TextField(blank=True, null=True)
risk_group_desc = models.TextField(blank=True, null=True)
var = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True)
sku = models.ForeignKey(SkuDetails, models.DO_NOTHING, blank=True, null=True)
After joining I want all the column of both the table in flat structure through Django ORM...
In raw SQL I will get all the column ... But not getting from ORM
Please Help !!!
Getting all values in a list of dictionaries is quite easy with values():
Risks.objects.values(
'risk_id',
'risk_group_short_desc`,
# ... fields you need from Risks
'sku__sku_id',
# ... fields you need from SkuDetails
)
You can check out values_list() as well.
You can try this withselect_related. Relevant helping material As both model with foreign-key relation.

How can I filter a model in django based on a field of a many to many related object?

I have two models related to each other with a many to many. I want to filter for one, Message, based on a field on the other, User.created_at, compared to a field on the first, Message.edit_date.
class Message(Model):
content = CharField(max_length=512, blank=True, null=True)
created_at = models.DateTimeField(blank=True, null=True)
edit_date = models.DateTimeField(blank=True, null=True)
users = models.ManyToManyField('User', related_name='message_user')
class User(Model):
name = content = CharField(max_length=48)
created_at = models.DateTimeField(blank=True, null=True)
Right now I am achieving this by looping over the two models and comparing them in the loop, which is slow.
message_query = Message.objects.none()
for user_name, created_at_date in Users.objects.filter(name='Tina').values_list('id', 'created_at').iterator():
message_query.add(Q(
users=user_id,
edit_date__gte=created_at_date,
), Q.OR)
messages = Message.objects.filter(message_query)
Is there any way to create a filter for the items I'm attempting to filter for in a query?
You can filter on fields on the related model directly using F expressions. Something like this should work:
from django.db.models import F
# This will return all messages where one of the associated users
# was created_at before the message edit_date.
Message.objects.filter(
edit_date__gte=F('users__created_at')
).distinct()
Note that this will return duplicate results if more than one user matches this condition for any given message - hence the distinct() the end.