Django queryset for many-to-many field with intermediate table - django

Here's my setup
Models
class League(models.Model):
league_name = models.CharField(max_length=60)
class Team(models.Model):
league = models.ForeignKey('League')
team_name = models.CharField(max_length=60)
class Game(models.Model):
league = models.ForeignKey('League')
teams = models.ManyToManyField(Team, through="GameTeams")
game_heading = models.CharField(max_length=40, null=True, blank=True)
class GameTeams(models.Model):
game = models.ForeignKey(Game)
team = models.ForeignKey(Team, null=True, blank=True)
How do I get all the (game, team) pairs associated with a particular league? This is what I tried:
league = League.objects.get(pk=1) #This obviously works
league.game_set.gameteams_set.all() #This doesn't work
The resultant set should be something something along these lines:
league_id | game_id | team_id
1 | 1 | 1
1 | 1 | 2
1 | 1 | 3
1 | 2 | 2
1 | 2 | NULL

you can do it like:
league = League.objects.get(pk=1)
gameteams = GameTeams.objects.filter(game__league=league, team__league=league)

Related

Django - Annotate field

I have 3 models:
1.
class Calendar(models.Model):
...
supplier = models.CharField(...)
subassort = models.CharField(..., null=True)
Data example:
supplier | subassort
----------------------
12345 | subassort1
67890 | subassort2
class SupplierProductAssortments(models.Model):
subassort = models.CharField(...)
supplier = models.CharField(...)
ui2_code = models.CharField(...)
Data example:
supplier | subassort | ui2_code
---------------------------------
12345 | subassort1 | ui-1
12345 | subassort1 | ui-2
67890 | subassort2 | ui-3
class UI2(models.Model):
code = models.CharField(...)
name = models.CharField(...)
Data example:
code | name
-------------
ui-1 | milk
ui-2 | cheese
ui-3 | meat
I need to annotate ui2 field to queryset of Calendar. For better understanding below I wrote a function that demonstrates it:
def get_calendars_with_ui2s():
calendars = Calendar.objects.all() # need to annotate ui2 names
for calendar in calendars:
if subassort := calendar.subassort:
ui2_codes = SupplierProductAssortments.objects\
.filter(supplier=calendar.supplier, subassort=subassort)\
.values_list('ui2_code', flat=True)
ui2 = UI2.objects.filter(code__in=ui2_codes).values_list('name', flat=True)
else:
ui2 = []
calendar.ui2 = ui2
return calendars
Expected output:
supplier | subassort | ui2
---------------------------------------
12345 | subassort1 | [milk, cheese]
67890 | subassort2 | [meat]
If when filtering ui2_codes was by one field I guess it could be solved by OuterRef. But there are filtering by two fields.
How can I annotate field ui2?
P.S. No FK

How do I return a list in a Django Annotation?

Right now I have the following, very slow but working code:
crossover_list = {}
for song_id in song_ids:
crossover_set = list(dance_occurrences.filter(
song_id=song_id).values_list('dance_name_id', flat=True).distinct())
crossover_list[song_id] = crossover_set
It returns a dictionary where a song ID is used as a dictionary key, and a list of integer values is used as the value. The first three keys are the following:
crossover_list = {
1:[38,37],
2:[38],
....
}
Does anyone here know of a succinct way to wrap this up into a single query? The data exists in a single table that has three columns where each song_id can be associated with multiple dance_ids.
song_id | playlist_id | dance_id
1 1 38
1 2 37
2 1 38
Ideally, what I am trying to figure out how to return is:
<QuerySet[{'song_id':1, [{'dance_id':38, 'dance_id':37}]}, {'song_id':2, [{'dance_id':38}]}]>
Any ideas or help is appreciated.
Edit: As requested, here are the models in question:
# This model helps us do analysis on music/dance crossover density, song popularity within-genre,
# playlist viability within-genre (based on song occurrence counts per song within each playlist), etc.
class SongOccurrences(models.Model):
song = models.ForeignKey(
'Songs',
on_delete=models.CASCADE,
)
playlist = models.ForeignKey(
'Playlists',
on_delete=models.CASCADE,
)
dance_name = models.ForeignKey(
'DanceMasterTable',
on_delete=models.CASCADE,
)
class Meta:
constraints = [
models.UniqueConstraint(fields=['song', 'playlist'], name='unique occurrence')
]
# This model contains relevant data from Spotify about each playlist
class Playlists(models.Model):
spotify_playlist_uri = models.CharField(max_length=200, unique=True)
spotify_playlist_owner = models.CharField(max_length=200)
spotify_playlist_name = models.CharField(max_length=200)
current_song_count = models.IntegerField()
previous_song_count = models.IntegerField()
dance_name = models.ForeignKey(
'DanceMasterTable',
on_delete=models.CASCADE,
)
# This model contains all relavent data from Spotify about each song
class Songs(models.Model):
title = models.CharField(max_length=200)
first_artist = models.CharField(max_length=200)
all_artists = models.CharField(max_length=200)
album = models.CharField(max_length=200)
release_date = models.DateField('Release Date', blank=True)
genres = models.CharField(max_length=1000, blank=True)
popularity = models.FloatField(blank=True) # This value changes often
explicit = models.BooleanField(blank=True)
uri = models.CharField(max_length=200, unique=True)
tempo = models.FloatField(blank=True)
time_signature = models.IntegerField()
energy = models.FloatField(blank=True)
danceability = models.FloatField(blank=True)
duration_ms = models.IntegerField()
tonic = models.IntegerField(blank=True)
mode = models.IntegerField(blank=True)
acousticness = models.FloatField(blank=True)
instrumentalness = models.FloatField(blank=True)
liveness = models.FloatField(blank=True)
loudness = models.FloatField(blank=True)
speechiness = models.FloatField(blank=True)
valence = models.FloatField(blank=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['title', 'first_artist', 'all_artists'], name='unique song')
]
# This model contains the (static) master list of partner dances to be analyzed.
class DanceMasterTable(models.Model):
dance_name = models.CharField(max_length=200, unique=True)
You are running queries inside a loop and hence, it's slow.
You can filter dance_occurrences by all the song_ids before hand and finally loop over the values to append dance ids to their respective song ids.
Example:
song_dance_occurrences = dance_occurrences.filter(
song_id__in=song_ids
).values_list('song_id', 'dance_id').distinct()
crossover_dict = {}
for song_id, dance_id in song_dance_occurrences:
crossover_dict[song_id] = crossover_dict.get(song_id, [])
crossover_dict[song_id].append(dance_id)

get unique values from query_set Django

I'm trying to get unique results from s query_set.
Example.
ID | NOMBRE | CLASIFICACION
1 Escoba Limpieza
2 Trapeador Limpieza
3 Tornillo Herramienta
I want to get "Limpieza and Herramienta" only. currently I'm getting "Limpieza, Limpieza and Herramineta"
my views.py:
class ItemListView(ListView):
model= Items
def get_context_data(self,**kwargs):
context = super(ItemListView, self).get_context_data(**kwargs)
context['clasificacion'] =Items.objects.order_by('nombre').distinct('clasificacion')
return context
my models.py
from django.db import models
# Create your models here.
class Items(models.Model):
nombre = models.CharField(max_length=250)
descripcion = models.CharField(max_length=250)
codigo_proveedor = models.CharField(max_length=250)
clasificacion = models.CharField(max_length=250)
c_minima = models.IntegerField()
c_actual = models.IntegerField()
proveedor = models.ForeignKey('Proveedores',on_delete=models.CASCADE)
active = models.BooleanField()
def __str__(self):
return self.nombre + ' ----- ' +
self.clasificacion + ' ----- ' +
str(self.c_actual)
class Proveedores(models.Model):
nombre = models.CharField(max_length=250)
telefono = models.CharField(max_length=250)
direccion1 = models.CharField(max_length=250)
direccion2 = models.CharField(max_length=250, null=True)
active = models.BooleanField()
def __str__(self):
return self.nombre
Thanks for the help!
Will's response is correct, but I think you want to do the order_by() operation first to ensure you are getting the correct results.
context['clasificacion'] =(Items
.objects
.order_by('clasification', 'nombre')
.distinct('clasificacion'))
context['clasificacion'] = Items.objects.order_by('clasificacion', 'nombre').distinct('clasificacion')
distinct() must match the leftmost order_by(). So making the column you use in distinct() the first column in the order_by() makes it work.

django query datetime filed issue

i have tried to apply filter datetime filed in django query,but i got result zero
class Customers(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30, null=True)
customer_code = models.CharField(max_length=30)
reference = models.CharField(max_length=30, null=True)
phone_office = models.CharField(max_length=250, null=True)
phone_residential = models.CharField(max_length=250, null=True)
contact_person_first_name = models.CharField(max_length=30)
mobile = models.BigIntegerField()
email = models.EmailField(null=True)
fax = models.CharField(max_length=15, null=True)
cr_limit = models.DecimalField(max_digits=10, decimal_places=2, null=True)
gstin = models.CharField(max_length=10, null=True)
state_code = models.CharField(max_length=10, null=True)
country = models.CharField(max_length=250, null=True)
opening_balance = models.DecimalField(max_digits=10, decimal_places=2, null=True)
opening_balance_date = models.DateTimeField(null=True)
payments_terms = models.ForeignKey(PaymentTerms, related_name='customer_terms', null=True, blank=True)
address_flag = models.BooleanField(default=False)
created_by = models.ForeignKey(SAUser, related_name='+')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
the below code i have tried:
Customers.objects.filter(created_at__month=1).count()
but i have look at the query also
Customers.objects.filter(created_at__month=1).query
it seems like that
SELECT `customer_customers`.`id`, `customer_customers`.`first_name`, `customer_customers`.`last_name`, `customer_customers`.`customer_code`, `customer_customers`.`reference`, `customer_customers`.`phone_office`, `customer_customers`.`phone_residential`, `customer_customers`.`contact_person_first_name`, `customer_customers`.`mobile`, `customer_customers`.`email`, `customer_customers`.`fax`, `customer_customers`.`cr_limit`, `customer_customers`.`gstin`, `customer_customers`.`state_code`, `customer_customers`.`country`, `customer_customers`.`opening_balance`, `customer_customers`.`opening_balance_date`, `customer_customers`.`payments_terms_id`, `customer_customers`.`address_flag`, `customer_customers`.`created_by_id`, `customer_customers`.`created_at`, `customer_customers`.`updated_at` FROM `customer_customers` WHERE EXTRACT(MONTH FROM CONVERT_TZ(`customer_customers`.`created_at`, 'UTC', IST)) = 1
The manual query in mysql
select id,created_at from customer_customers;
+----+----------------------------+
| id | created_at |
+----+----------------------------+
| 1 | 2017-12-24 06:54:41.264756 |
| 2 | 2017-12-24 07:05:37.317395 |
| 3 | 2017-12-24 10:05:29.957158 |
| 4 | 2017-12-29 13:30:21.572926 |
| 5 | 2017-12-29 13:58:59.137774 |
| 6 | 2017-12-31 08:46:13.239080 |
| 7 | 2017-12-31 09:04:34.695830 |
| 8 | 2017-12-31 12:27:05.253016 |
| 9 | 2018-01-27 12:28:16.809840 |
| 10 | 2018-02-14 07:27:18.847884 |
| 11 | 2018-02-14 10:45:33.323448
Expected result should be 2
"When USE_TZ is True, datetime fields are converted to the current
time zone before filtering. This requires time zone definitions in the
database."
Add this to your settings file.
USE_TZ = False

django: getting foreign key information

I created a model with couple of classes and with foreign key and I was able to save it on the database.
I have the following models:
class Player_Bios(models.Model):
my_id = models.SlugField(unique=True)
player_id = models.IntegerField(max_length=50, unique=True)
name = models.CharField(max_length=50)
last = models.CharField(max_length=50)
middle = models.CharField(max_length=50, blank=True)
class BatStat (models.Model):
player_id = models.ForeignKey('Player_Bios')
team_id = models.ForeignKey('Team')
bat_stat_id = models.CharField(max_length=50, unique=True)
sport_code = models.CharField(max_length=50, blank=True)
ab = models.IntegerField(max_length=50, null=True)
class Team (models.Model):
team_id = models.IntegerField(max_length=50, unique=True)
team_short = models.CharField(max_length=50, blank=True)
team_full = models.CharField(max_length=50, blank=True)
When I save it to the database I can see that the team_id on the Team table is the same as the team_id on the BatStat table, but the player_id on the BatStat is different that the player_id on the Player_Bios table. This is how I save the data to the database:
p_id = Player_Bios.objects.get(player_id=st['player_id'])
t_id = Team.objects.get(team_id=st['team_id']) #I get the team_id from the Team Class
bat_id = str(st['season'])+ str(st['team_seq'])
bat_id = str(p_id.player_id) + bat_id
c = BatStat(player_id = p_id,team_id=t_id, bat_stat_id=bat_id, sport_code =st["sport_code"],ab=st['ab'])
c.save()
st['player_id'] is a dictionary. I did a print and it show the right player_id number
In BatStat you are storing the key to Player_Bios, which is not player_id
class Player_Bios(models.Model):
...
player_id = models.IntegerField(max_length=50, unique=True)
class BatStat (models.Model):
...
player_id = models.ForeignKey('Player_Bios')
I'm not sure why your team_id is the same, however, it seems like you already have the ids. You could avoid looking up the Player_Bios and Team by setting the id directly.
Django: Set foreign key using integer?
class Player_Bios(models.Model):
...
player_id = models.IntegerField(primary_key=True, max_length=50)
class Team (models.Model):
...
team_id = models.IntegerField(primary_key=True, max_length=50)
class BatStat (models.Model):
...
player = models.ForeignKey('Player_Bios') # notice i renamed this to not have '_id'
team = models.ForeignKey('Team') # notice i renamed this to not have '_id'
c = BatStat(bat_stat_id=bat_id,
sport_code =st["sport_code"],
ab=st['ab'])
c.player_id = st['player_id'], # notice that this has '_id'
c.team_id = st['team_id'], # notice this has '_id'
c.save()