Better way to query this Django Model - django

I am new to Django. I have a model and now I need to query the model.
The model is this:
from django.db import models
import datetime
class Position(models.Model):
website_position = models.IntegerField()
category = models.ForeignKey('Category')
directorio = models.ForeignKey('Directorio')
class Category(models.Model):
n_category = models.CharField(max_length=100)
category_position = models.IntegerField()
date_inserted = models.DateTimeField(auto_now_add=True)
date_last_update = models.DateTimeField(auto_now_add=True)
class Directorio(models.Model):
website_name = models.CharField(max_length=200)
website_url = models.URLField()
activated = models.BooleanField()
date_inserted = models.DateTimeField(auto_now_add=True)
date_last_update = models.DateTimeField(auto_now=True)
categories = models.ManyToManyField(Category, through=Position)
Now, I need to query the database in this way:
select dc.n_category, dp.website_position, dd.website_name, dd.website_url, dd.activated
from directorio_category as dc
join directorio_position as dp on dc.id = dp.category_id
join directorio_directorio as dd on dp.directorio_id = dd.id
where dd.activated = 'true'
Question: Should I use the Model Query API or should I use RAW SQL?
Best Regards,

You can do it with the model query api, but if you already have the raw sql written that's going to be more efficient in the long term if you have to scale to enormous heights.
Pro of raw SQL: More efficiently hit the database.
Pro of query api: Non SQL django people will be able to maintian and extend your code in the future.
I've been interacting with databases via django's orm so long that I'm struggling to figure out what your query even means.
#I think this gets what you want
positions = Position.objects.filter(directorio__activated=True).order_by('category__n_catgory', 'website_position')
for position in positions:
print position.category.n_category, position.website_position, position.directorio.website_name, position.website.website_url, position.website.activated
The key to migrating from SQL to django's ORM is starting to think in terms of the primary object(s) you want, then walking the relationships to get the related data. All data related to an object is available to you via object/dot notation, and thanks to the django ORM's laziness, most of it isn't retrieved until you ask for it.
The above code gets every position by category, then walks the relationships to get the other data. Another version of the code might get every active website then show its categories and positions:
websites = Directorio.objects.filter(activated=True).order_by('website_name')
for website in websites:
print website.website_name, website.website_url, website.activated
for position in website.position_set.all().order_by('category__n_category'):
print " -- ", position.category.n_category, position.website_position

Related

multiple joins on django queryset

For the below sample schema
# schema sameple
class A(models.Model):
n = models.ForeignKey(N, on_delete=models.CASCADE)
d = models.ForeignKey(D, on_delete=models.PROTECT)
class N(models.Model):
id = models.AutoField(primary_key=True, editable=False)
d = models.ForeignKey(D, on_delete=models.PROTECT)
class D(models.Model):
dsid = models.CharField(max_length=255, primary_key=True)
class P(models.Model):
id = models.AutoField(primary_key=True, editable=False)
name = models.CharField(max_length=255)
n = models.ForeignKey(N, on_delete=models.CASCADE)
# raw query for the result I want
# SELECT P.name
# FROM P, N, A
# WHERE (P.n_id = N.id
# AND A.n_id = N.id
# AND A.d_id = \'MY_DSID\'
# AND P.name = \'MY_NAME\')
What am I trying to achieve?
Well, I’m trying to find a way somehow be able to write a single queryset which does the same as what the above raw query does. So far I was able to do it by writing two queryset, and use the result from one queryset and then using that queryset I wrote the second one, to get the final DB records. However that’s 2 hits to the DB, and I want to optimize it by just doing everything in one DB hit.
What will be the queryset for this kinda raw query ? or is there a better way to do it ?
Above code is here https://dpaste.org/DZg2
You can archive it using related_name attribute and functions like select_related and prefetch_related.
Assuming the related name for each model will be the model's name and _items, but it is better to have proper model names and then provided meaningful related names. Related name is how you access the model in backward.
This way, you can use this query to get all models in a single DB hit:
A.objects.all().select_related("n", "d", "n__d").prefetch_related("n__p_items")
I edited the code in the pasted site, however, it will expire soon.

How can I query embedded records in a Djongo ArrayField?

I'm using Django 3.0.6 with Django Rest Framework 3.11.1. I'm connecting to a MongoDB database using the djongo connector. One of my models has an ArrayField that contains embedded records. I would like to know how I can retrieve these embedded fields using a django query.
Each Person can have many different sub records. Here is a sample model to illustrate what I'm working on
Models:
from djongo import models
class SubRecords(models.Model):
status = models.CharField(max_length=20)
startTime = models.CharField(max_length=20)
identifier = models.CharField(max_length=20)
job_title = models.CharField(max_length=20)
class Meta:
abstract = True
class Person(models.Model):
_id = ObjectIdField()
workplace = models.CharField(max_length=120)
subject = models.CharField(max_length=120)
records = models.ArrayField(model_container=SubRecords)
I would like to query the Person model and get
all Person.records objects and
Person.records objects that match some criteria
I have tried to do this
>>> Person.objects.filter(records__exact={'job_title': 'HR'})
Now the problem I'm facing is that the result isn't limited to subrecords where the job title is HR, instead if a Person object contains a sub record that matches the criteria, the whole Person object and associated sub records are returned.
I want to be able to get a list of all the subrecords and only the subrecords that match the criteria I specify. How can I do this?
You don't need __exact:
Person.objects.filter(records={'job_title':'HR'})
I suppose you can do it like this
Person.objects.filter(records__in={'job_title':'HR'})
You can add .all() at the end of query if you want.

Django Model - JOIN Tables without adding models

I have two databases to connect to for my Django App. I have only read access for one of the database, from where I want to pull only a few columns for my model into the other database where I have read and write access. It does not make sense to 'inspectdb' the read access databases with 100+ tables and the 100+ columns for just a few columns.
How do I add the column from another database to my existing model in the simplest manner using a join? Can I use a raw sql query with parameters to add the field into my model?
class APPNAME(models.Model):
class Meta:
db_table = 'APPNAME'
item= models.PositiveIntegerField(db_column='Item')
issue = models.CharField(max_length=200, db_column='Issue')
detail = models.TextField(db_column='Detail')
created_at = models.DateTimeField(auto_now_add=True, db_column='Create Datetime')
updated_at = models.DateTimeField(auto_now=True, db_column='Update Datetime')
external_column = How to join this column???
Here is a simple SQL Query to showcase what I am trying to do with the Django model.
SELECT
APP.[Item]
,APP.[Issue]
,APP.[Detail]
,APP.[Create Datetime]
,APP.[Update Datetime]
,BIG.[External Column]
FROM DEVDB.dbo.APPNAME APP
INNER JOIN PRODDB.dbo.BIGTABLE BIG ON APP.[Item] = BIG.[Item]

Django Introduce a foreign key from a model CharField

I have migrated over 10,000 records from my old mySQL database to Django/sqlite. In my old mysql schema's Song table, the artist field was not a 1 to many field but was just a mysql varchar field. In my new Django model, I converted the artist field to a ForeignKey and used temp_artist to temporarily store the artist's name from the old database.
How do I create each Song instance's artist foreignkey based on the temp_artist field? I'm assuming I should use the manager's get_or_create method but where and how do I write the code?
my model below:
class Artist (models.Model):
name = models.CharField(max_length=100)
class Song (models.Model):
artist = models.ForeignKey(Artist, blank=True, null=True, on_delete=models.CASCADE, verbose_name="Artist")
temp_artist = models.CharField(null=True, blank=True, max_length=100)
title = models.CharField(max_length=100, verbose_name="Title")
duration = models.DurationField(null=True, blank=True, verbose_name="Duration")
You can write a custom management command that performs this logic for you. The docs provide good instructions on how to set it up. Your command code would look something like this:
# e.g., migrateauthors.py
from django.core.management.base import BaseCommand
from myapp import models
class Command(BaseCommand):
help = 'Migrate authors from old schema'
def handle(self, *args, **options):
for song in myapp.models.Song.objects.all():
song.artist, _ = models.Artist.objects.get_or_create(name=song.temp_artist)
song.save()
Then you simply run the management command with manage.py migrateauthors. Once this is done and verified you can remove the temporary field from your model.
Since you don't have a usable foreign key at the moment you would have to dig down to raw_sql. If you were still on mysql you could have used the UPDATE JOIN syntax. But unfortunately Sqlite does not support UPDATE JOIN.
Luckily for you you have only a few thousand rows and that makes it possible to iterate through them and update each row individually.
raw_query = '''SELECT s.*, a.id as fkid
FROM myapp_song s
INNER JOIN myapp_artist a on s.temp_artist = a.name'''
for song in Song.objects.raw(raw_query)
song.artist_id = s.fkid
song.save()
This might take a few minutes to complete because you don't have an index on temp_artist and name. Take care to replace myapp with the actual name of your app.
Edit1:
Though Sqlite doesn't have update JOIN, it does allow you to SET a value with a subquery. So this will also work.
UPDATE myapp_song set artist_id =
(SELECT id from myapp_artist WHERE name = myapp_song.temp_artist)
type it in the sqlite console or GUI. Make sure to replace myapp with your own app name. This will be very quick because it's a single query. All other solutions including my alternative solution in this answer involve 10,000 queries.
Edit 2
If your Artist table is empty at the moment, before you do all this you will have to populate it, here is an easy query that does it
INSERT INTO stackoverflow_artist(name)
SELECT distinct temp_artist from stackoverflow_song
note that you should have a unique index on Artist.name

Basic relations in django (built on top of a legacy db)

I've googled on and on, and I just don't seem to get it.
How do I recreate simple join queries in django?
in models.py (Fylker is county, Dagensrepresentanter is persons)
class Fylker(models.Model):
id = models.CharField(max_length=6, primary_key=True)
navn = models.CharField(max_length=300)
def __unicode__(self):
return self.navn
class Meta:
db_table = u'fylker'
class Dagensrepresentanter(models.Model):
id = models.CharField(max_length=33, primary_key=True)
etternavn = models.CharField(max_length=300, blank=True)
fornavn = models.CharField(max_length=300, blank=True)
fylke = models.ForeignKey(Fylker, db_column='id')
def __unicode__(self):
return u'%s %s' % (self.fornavn, self.etternavn)
class Meta:
ordering = ['etternavn'] # sette default ordering
db_table = u'dagensrepresentanter'
Since the models are auto-created by django, I have added the ForeignKey and tried to connect it to the county. The id fields are inherited from the db I'm trying to integrate into this django project.
By querying
Dagensrepresentanter.objects.all()
I get all the people, but without their county.
By querying
Dagensrepresentanter.objects.all().select_related()
I get a join on Dagensrepresentanter.id and Fylker.id, but I want thet join to be on fylke, aka
SELECT * FROM dagensrepresentanter d , fylker f WHERE d.fylke = f.id
This way I'd get the county name (Fylke navn) in the same resultset as all the persons.
Additional request:
I've read over the django docs and quite a few questions here at stackoverflow, but I can't seem to get my head around this ORM thing. It's the queries that hurt. Do you have any good resources (blogposts with experiences/explanations, etc.) for people accustomed to think of databases as an SQL-thing, that needs to start thinking in django ORM terms?
Your legacy database may not have foreign key constraints (for example, if it is using MyISAM then foreign keys aren't even supported).
You have two choices:
Add foreign key constraints to your tables (would involve upgrading to Innodb if you are on MyISAM). Then run ./manage inspectdb again and the relationships should appear.
Use the tables as is (i.e., with no explicit relationships between them) and compose queries manually (e.g., Mytable.objects.get(other_table_id=23)) either at the object level or through writing your own SQL queries. Either way, you lose much of the benefit of python's ORM query language.