Django get max length value from same model with Coalesce - django

I'm trying to write the following raw query with the ORM. I'm not sure is it possible or not.
select first_name,
middle_name,
COALESCE(middle_name, (
select middle_name
from contacts c2
where c2.first_name = c1.first_name
and c2.last_name = c1.last_name
and c2.middle_name is not null
order by length(c2.middle_name) desc
limit 1
)
) expected,
last_name
from contacts c1
The expected result is like the following, if middle_name is null, get the middle name from another record that has the same first_name and last_name.
id| first_name | middle_name | expected | last_name
1 | ahmet | <NULL> | burak | ozyurt
2 | ahmet | burak | burak | ozyurt
class Contact(models.Model):
first_name = models.CharField(max_length=250)
last_name = models.CharField(max_length=250, null=True, blank=True)
middle_name = models.CharField(max_length=250, null=True, blank=True)
DB: Postgres
Django Version: 3.12

By using the django ORM, you can perform the same query by using the following code
from django.db import models
from django.db.models.functions import Coalesce, Length
matched_middle_name_queryset = Contact.objects.filter(
first_name=models.OuterRef("first_name"),
last_name=models.OuterRef("last_name"),
middle_name__isnull=False,
).annotate(
middle_name_len=Length("middle_name")
).order_by("-middle_name_len").values("middle_name")[:1]
result = Contact.objects.annotate(
matched_middle_name=models.Subquery(matched_middle_name_queryset)
expected=Coalesce(
models.F("middle_name")
models.F("matched_middle_name"),
).values("id", "first_name", "middle_name", "expected", "last_name")
)
Explanations
models.OuterRef is used for referring to the field from the parent query of a subquery.
- prefix in the order_by("-middle_name_len") is for descending order
.values("middle_name") is for selecting only middle_name values.
the slicing [:1] is for limiting the result from the subquery to be one.
Tips
You can use result.query to inspect what query the ORM will generate for you.

Related

get attribut from other model with foreign key

I've a model with different fields.
I. E.
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
DB i.E.
id | first_name | Last_name | Instrument
1 | Stefan | Oslay | Bass
2 | Richard | Wagner | Klavier
Than I have a second model. I want to reference with foreign key the Musician. And then i want also a new custom field which contains also a attribut from Musician.
class MusikWerk(models.Model):
Interpret = models.ForeignKey(Musician, on_delete=models.CASCADE, null=FALSE, blank=False)
Title = models.CharField(max_length=50)
def instrument_wert(self)
return '%s '%(self.instrument)
instrument = instrument_wert(Musician)
DB i.E.
id | Interpretid | Title | Instrument
1 | 2 | TitelWagner | Klavier
But with my code i don't create the column Instrument in database and can't save the foreign field in them 😦

How to add list_display fields from a reverse relationship in django admin

I'm pretty new to django and the admin module. I'm looking for a way to add on a admin class
some fields that i query through a reverse relationship.
I can currently retrieve the interesting fields and put them in one column thanks to a specific function
using list_diplay, but i cannot manage to create a list_display field BY returned query object:
as example, now I get as column:
|Inventory_id| Mousqueton1 |
| 22 | foo1,foo2 |
and i would like to have this kind of output, to easily create filters:
|Inventory_id| Mousqueton1 | Mousqueton2 |
| 22 | foo1 | foo2 |
Here's my current models.py
class Kit(models.Model):
inventory_id = models.CharField(max_length=20,unique=True)
description = models.TextField(null=True)
creation_date = models.DateField(auto_now_add=True)
last_update = models.DateTimeField(auto_now=True)
class Mousquetons(models.Model):
inventory_id = models.CharField(max_length=20,unique=True)
serial = models.IntegerField(unique=False)
description = models.TextField(null=True)
creation_date = models.DateField(auto_now_add=True)
last_update = models.DateTimeField(auto_now=True)
kit = models.ForeignKey(Kit,on_delete=models.PROTECT,null=True)
and admin.py
#admin.register(Kit)
class KitAdmin(admin.ModelAdmin):
list_display= ['inventory_id','m']
def get_queryset(self, obj):
qs = super(KitAdmin, self).get_queryset(obj)
return qs.prefetch_related('mousquetons_set')
def m(self, obj):
return list(obj.mousquetons_set.all())
Maybe my data modeling is not the right way to perform this kind of operation, Any advice would be great.
Thanks !

Calling related name models in query (Q) "Django"

I have profile model like :
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
first_name = models.CharField(max_length=30, blank=True, null=True)
last_name = models.CharField(max_length=30, blank=True, null=True)
and I have a search view like :
def users_search_view(request):
query = request.GET.get('q')
users = User.objects.filter(Q(email__icontains=query)|Q(username__icontains=query),is_superuser=False,is_staff=False)
ctx = {'users':users,
'q':query}
return render(request,"staff/users_search.html", ctx)
I want to have search by first name and also last name from Profile model, in my search, How can I make something like Q(email__icontains=query) for related modals ?
You can .filter(…) [Django-doc] with:
User.objects.filter(Q(profile__first_name__icontains=query) | Q(profile__last_name__icontains=query) | Q(email__icontains=query) | Q(username__icontains=query),is_superuser=False,is_staff=False)
One can use double underscores (__) to look "through" relations. This will result in a LEFT OUTER JOIN where we thus filter on the related Profile record for that User.
Just keep adding Q objects to your filter and use dunder (__) to access related fields.
E.g. User.objects.filter(Q(username__icontains=q) | Q(profile__first_name__icontains=q))

Correct Django Foreignkey setup with legacy database

I used python manage.py inspectdb > models.py to create models from my old SQL database. Workout.user_id is a foreign key to users table, but inspectdb lost this linking by making it just models.IntegerField().
Database looks like this:
Users
+----+------------+
| id | name |
+----+------------+
| 1 | Bob |
| 2 | Alice |
| 3 | Tom |
+----+------------+
Workout
+---------+------------+------------+
| user_id | workout_id | date |
+---------+------------+------------+
| 2 | 1 | 2021-02-18 |
| 2 | 2 | 2021-02-20 |
| 3 | 3 | 2021-02-21 |
+---------+------------+------------+
And models.py comes out like this:
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models
class Users(models.Model):
id = models.AutoField(primary_key=True)
name = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'users'
class Workout(models.Model):
user_id = models.IntegerField()
workout_id = models.AutoField(primary_key=True)
date = models.DateField(blank=True, null=True)
class Meta:
managed = False
db_table = 'workout'
Workout.user_id is a foreign key to users table, but inspectdb lost this linking by making it just models.IntegerField().
I tried setting ForeignKey like this:
user_id = models.ForeignKey('Users', on_delete=models.CASCADE)
but it gives an error:
django.db.utils.OperationalError: (1054, "Unknown column 'workout.user_id_id' in 'field list'")
Can this kind of ForeignKey linking be achieved or how should I proceed?
Django automatically adds _id to the ForeignKey for the name of the table column, you thus name it user. You can also specify with the db_column=… parameter [Django-doc] the name of the database column, although that is not necessary here:
class Workout(models.Model):
user = models.ForeignKey(
'Users',
db_column='user_id'
)
workout_id = models.AutoField(primary_key=True)
date = models.DateField(blank=True, null=True)
class Meta:
managed = False
db_table = 'workout'

Django Model Creating with Verbose Name

When creating a model object, can you use the verbose name to create? I have a model that has a dot in the name and I cannot have dot in a variable name.
file.xls
|---------|-----------------------|
| Start | ExonicFunc.UHNCLGGene |
|---------|-----------------------|
| 2488153 | nonsynonymous SNV |
| 3301721 | synonymous SNV |
|---------|-----------------------|
models.py
class Variant(models.Model):
start = models.CharField(max_length=10, null=True)
exonic_function = models.CharField(verbose_name="ExonicFunc.UHNCLGGene", max_length=20, null=True)
view.py
def upload(request):
# reads file.xls
raw_data = pandas.read_excel(request.FILES.get("file"))
for _, row in raw_data.iterrows():
variant = Variant.objects.create(**row)
verbose_name is just a label for your field in admin panel. You probably want to use db_column
exonic_function = models.CharField(db_column="ExonicFunc.UHNCLGGene", max_length=20, null=True)
and they assign value in your view as usual
variant.exonic_function = "bla"