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
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 😦
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 !
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))
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'
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"