Queryset containing related Object with same foreignkey - python

e.g. I've a person with an address
class Persons(models.Model):
adress = models.ForeignKey(Adress, on_delete=models.DO_NOTHING, blank=True,
null=True)
class Adress(models.Model):
some_data = models.IntegerField()
and i have another related data in antoher model like this
class Places(models.Model):
adress = models.ForeignKey(Adress, on_delete=models.DO_NOTHING)
how can i get a queryset now of both persons and places if adress is set in persons?

You can obtain the Places of a Persons object with:
Places.object.filter(adress__persons=myperson)
If you want to do this in bulk, you can work with:
qs = Persons.objects.select_related('adress').prefetch_related('adress__places_set')
this will load the related Places in bulk, so you can fetch these with:
for person in qs:
print(person.adress.places_set.all())

You can do somethings like this but first you need to add related_name in your Persons and Places Foreign key
class Persons(models.Model):
adress = models.ForeignKey(Adress,related_name = "persons_adress",on_delete=models.DO_NOTHING, blank=True,null=True)
class Adress(models.Model):
some_data = models.IntegerField()
class Places(models.Model):
adress = models.ForeignKey(Adress, related_name = "places_addres",on_delete=models.DO_NOTHING)
After this alteration you can now query
Adress.objects.filter(persons_adress__some_data = some_data,places_addres__some_data = some_data)

Related

Not able to access related model data using foreign key in Django

models.py
class products(models.Model):
name = models.CharField(max_length=100)
sku = models.CharField(max_length=50)
vendor = models.CharField(max_length=50)
brand = models.CharField(max_length=50)
price = models.FloatField()
product_status = models.BooleanField()
quantity = models.IntegerField()
def __str__(self):
return self.name
# categories
class categories(models.Model):
category_name = models.CharField(max_length=50)
parent_id = models.IntegerField()
# product categories
class product_categories(models.Model):
product = models.ForeignKey(products, on_delete=models.CASCADE)
category = models.ForeignKey(categories, on_delete=models.CASCADE)
def __str__(self):
return self.category
I can access 'category' table data(inside django shell) using
data = products.objects.all()
data.values('product_categories__category__category_name')
output: <QuerySet [{'product_categories__category__category_name': 'xxxx'}}]>
If I put this(inside django shell)
data.product_categories.category
output: 'QuerySet' object has no attribute 'product_categories'
How do I get a queryset(can be passed to html) which includes data from "categories" table along with the data of "products" table
There are a couple of issues happening here. First, data is a queryset, which is kind of like a list of objects, even though here there's just one object in the list. What you want is to get an attribute off of the item in the list, so you need something like a data.first() to get to that object before you start dotting into its attributes.
Secondly, the way Django handles reverse FK relationships requires that you refer to the FK by the standard name of, in your case, product_categories_set, OR you set your own related_name attribute on the FK. Something like:
# product categories
class product_categories(models.Model):
product = models.ForeignKey(products, on_delete=models.CASCADE, related_name='product_categories')
category = models.ForeignKey(categories, on_delete=models.CASCADE, related_name='product_categories')
def __str__(self):
return self.category
so that you can refer to your product_categories model from both the product and categories using just data.product_categories.
Thirdly, when accessing a reverse FK relationship, just like in point (1) above, you will get a related manager, from which you can get a queryset of items. Thus, to get the category name, you need to indicate which item you want the category name for. Assuming it's just the first item for everything, it would look something like:
data = products.objects.all()
product_category = data.product_categories.all()
category_name = product_category.category.category_name
Of course once you have more data, you'll not always want to just pick the first item, so you'll need to add filtering logic into the query to make sure you get the item you're looking for.
ETA, I do agree with the comment by Jorge above - a MTM would make this a bit simpler and would, in essence, create your product_categories table for you.

What is the "instance" being passed to the to_representation function of my ListSerializer?

The goal of this project is to create an API that refreshes hourly with the most up to date betting odds for a list of games that I'll be scraping hourly from the internet. The goal structure for the JSON returned will be each game as the parent object and the nested children will be the top 1 record for each of linesmakers being scraped by updated date. My understanding is that the best way to accomplish this is to modify the to_representation function within the ListSerializer to return the appropriate queryset.
Because I need the game_id of the parent element to grab the children of the appropriate game, I've attempted to pull the game_id out of the data that gets passed. The issue is that this line looks to be populated correctly when I see what it contains through an exception, but when I let the full code run, I get a list index is out of range exception.
For ex.
class OddsMakerListSerializer(serializers.ListSerializer):
def to_representation(self, data):
game = data.all()[0].game_id
#if I put this here it evaluates to 1 which should run the raw sql below correctly
raise Exception(game)
data = OddsMaker.objects.filter(odds_id__in = RawSQL(''' SELECT o.odds_id
FROM gamesbackend_oddsmaker o
INNER JOIN (
SELECT game_id
, oddsmaker
, max(updated_datetime) as last_updated
FROM gamesbackend_oddsmaker
WHERE game_id = %s
GROUP BY game_id
, oddsmaker
) l on o.game_id = l.game_id
and o.oddsmaker = l.oddsmaker
and o.updated_datetime = l.last_updated
''', [game]))
#if I put this here the data appears to be populated correctly and contain the right data
raise Exception(data)
data = [game for game in data]
return data
Now, if I remove these raise Exceptions, I get the list index is out of range. My initial thought was that there's something else that depends on "data" being returned as a list, so I created the list comprehension snippet, but that doesn't resolve the issue.
So, my question is 1) Is there an easier way to accomplish what I'm going for? I'm not using a postgres backend so distinct on isn't available to me. and 2) If not, its not clear to me what instance is that's being passed in or what is expected to be returned. I've consulted the documentation and it looks as though it expects a dictionary and that might be part of the issue, but again the error message references a list. https://www.django-rest-framework.org/api-guide/serializers/#overriding-serialization-and-deserialization-behavior
I appreciate any help in understanding what is going on here in advance.
Edit:
The rest of the serializers:
class OddsMakerSerializer(serializers.ModelSerializer):
class Meta:
list_serializer_class = OddsMakerListSerializer
model = OddsMaker
fields = ('odds_id','game_id','oddsmaker','home_ml',
'away_ml','home_spread','home_spread_odds',
'away_spread_odds','total','total_over_odds',
'total_under_odds','updated_datetime')
class GameSerializer(serializers.ModelSerializer):
oddsmaker_set = OddsMakerSerializer(many=True, read_only=True)
class Meta:
model = Game
fields = ('game_id','date','sport', 'home_team',
'away_team','home_score', 'away_score',
'home_win','away_win', 'game_completed',
'oddsmaker_set')
models.py:
class Game(models.Model):
game_id = models.AutoField(primary_key=True)
date = models.DateTimeField(null=True)
sport=models.CharField(max_length=256, null=True)
home_team = models.CharField(max_length=256, null=True)
away_team = models.CharField(max_length=256, null=True)
home_score = models.IntegerField(default=0, null=True)
away_score = models.IntegerField(default=0, null=True)
home_win = models.BooleanField(default=0, null=True)
away_win = models.BooleanField(default=0, null=True)
game_completed = models.BooleanField(default=0, null=True)
class OddsMaker(models.Model):
odds_id = models.AutoField(primary_key=True)
game = models.ForeignKey('Game', on_delete = models.CASCADE)
oddsmaker = models.CharField(max_length=256)
home_ml = models.IntegerField(default=999999)
away_ml = models.IntegerField(default=999999)
home_spread = models.FloatField(default=999)
home_spread_odds = models.IntegerField(default=9999)
away_spread_odds = models.IntegerField(default=9999)
total = models.FloatField(default=999)
total_over_odds = models.IntegerField(default=999)
total_under_odds = models.IntegerField(default=999)
updated_datetime = models.DateTimeField(auto_now=True)
views.py:
class GameView(viewsets.ModelViewSet):
queryset = Game.objects.all()
serializer_class = GameSerializer
Thanks
To answer the question in the title:
The instance being passed to the Serializer.to_representation() is the instance you pass when initializing the serializer
queryset = MyModel.objects.all()
Serializer(queryset, many=True)
instance = MyModel.objects.all().first()
Serializer(data)
Usually you don't have to inherit from ListSerializer per se. You can inherit from BaseSerializer and whenever you pass many=True during initialization, it will automatically 'becomeaListSerializer`. You can see this in action here
To answer your problem
from django.db.models import Max
class OddsMakerListSerializer(serializers.ListSerializer):
def to_representation(self, data): # data passed is a queryset of oddsmaker
# Do your filtering here
latest_date = data.aggregate(
latest_date=Max('updated_datetime')
).get('latest_date').date()
latest_records = data.filter(
updated_date_time__year=latest_date.year,
updated_date_time__month=latest_date.month,
updated_date_time__day=latest_date.day
)
return super().to_representation(latest_records)

Query through intermediate models that returns multiple results, using Django Q object

How to follow an intermediate 'through' model, in a Q object?
I'm trying to get a list of the ids of all Property objects that a User is a member of, in one query.
A User is a member of a BusinessUnit through BusinessUnitMember.
How do I make a filter with a Q object that finds all (multiple) BusinessUnitMembers for a particular User, and then finds all (multiple) BusinessUnits for those BusinessUnitMembers?
class Property(BaseModel):
"""Physical Things belonging to various Business Units"""
category = models.CharField(max_length=255, choices=CATEGORY_CHOICES)
objects = PropertyQuerySet.as_manager()
#property
def asset(self):
asset_maps = {
'typea': 'type_a_asset',
'typeb': 'type_b_asset',
}
if self.category not in asset_maps:
return None
return getattr(self, asset_maps.get(self.category), None)
class AssetBase(models.Model):
"""Abstract Base Asset model."""
business_unit = models.ForeignKey('BusinessUnit', null=True, on_delete=models.SET_NULL)
class TypeAAsset(AssetBase):
"""Type A Assets"""
property = models.OneToOneField('Property', null=True, on_delete=models.CASCADE,
related_name='type_a_asset')
class TypeBAsset(AssetBase):
"""Type B Assets"""
property = models.OneToOneField('Property', null=True, on_delete=models.CASCADE,
related_name='type_b_asset')
class BusinessUnit(models.Model):
name = models.CharField(max_length=255)
owner = models.ForeignKey('core.User', null=True,
on_delete=models.SET_NULL,
related_name='owned_business_units')
class BusinessUnitMember(models.Model):
""" ``through`` model for BU and members. """
business_unit = models.ForeignKey('BusinessUnit', on_delete=models.CASCADE)
member = models.ForeignKey('core.User', on_delete=models.CASCADE)
I need to find the ids of all Property objects that the current User is a member of.
I'm thinking of a filter like:
available_ids = Property.objects.filter(Q(type_a_asset__business_unit__in=user.businessunitmember.all().businessunit_set.all())).values_list('id', flat=True)
but the
businessunitmember_set.all().businessunit_set.all()
part doesn't work because businessunitmember_set.all() returns a queryset object which then won't have the attribute 'businessunit_set'. Using businessunitmember_set.get() doesn't work either, as it would return 3 results instead of 1.
I already got it working through iterating over the individual models, but of course those create a ton of queries.

How do you get a field related by OneToOneField and ManyToManyField in Django?

How do you get a field related by OneToOneField and ManyToManyField in Django?
For example,
class A(models.Model):
myfield = models.CharField()
as = models.ManyToManyField('self')
class B(models.Model):
a = models.OneToOneField(A)
If I want to get a 'myfield' and all associated 'as' using class B, given a 'myfield' equal to a string like 'example', how is it done?
Models.py
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
def __str__(self): # __unicode__ on Python 2
return "%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
primary_key=True,
)
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
def __str__(self): # __unicode__ on Python 2
return "%s the restaurant" % self.place.name
Let create a place instance.
p1 = Place.objects.create(name='Demon Dogs', address='944 W. Fullerton')
Then create a restaurant object.
r = Restaurant.objects.create(place=p1, serves_hot_dogs=True, serves_pizza=False)
Now, to access place from Restaurant:
>>> r.place
<Place: Demon Dogs the place>
vice-versa to access Restaurant from place
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>
I did not understand the many-to-many field part can you please elaborate?
First you get an instance of B say b and you can easily access myfield and as through the a attribute of b
b.a.myfield
b.a.as.all()
Furthermore, CharField requires a max_length attribute as follows:
class A(models.Model):
myfield = models.CharField(max_length=128)
as = models.ManyToManyField('self')
class B(models.Model):
a = models.OneToOneField(A)
A general point, give more descriptive names to your models and their attributes, or at the very least, add comments explaining what these models represent

Reference multiple foreign keys in Django Model

I'm making a program that helps log missions in a game. In each of these missions I would like to be able to select a number of astronauts that will go along with it out of the astronauts table. This is fine when I only need one, but how could I approach multiple foreign keys in a field?
I currently use a 'binary' string that specifies which astronauts are to be associated with the mission (1 refers to Jeb, but not Bill, Bob, or Val and 0001 means only Val), with the first digit specifying the astronaut with id 1 and so forth. This works, but it feels quite clunky.
Here's the model.py for the two tables in question.
class astronauts(models.Model):
name = models.CharField(max_length=200)
adddate = models.IntegerField(default=0)
experience = models.IntegerField(default=0)
career = models.CharField(max_length=9, blank=True, null=True)
alive = models.BooleanField(default=True)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "Kerbals"
class missions(models.Model):
# mission details
programid = models.ForeignKey(programs, on_delete=models.SET("Unknown"))
missionid = models.IntegerField(default=0)
status = models.ForeignKey(
missionstatuses, on_delete=models.SET("Unknown"))
plan = models.CharField(max_length=1000)
# launch
launchdate = models.IntegerField(default=0)
crewmembers = models.IntegerField(default=0)
# recovery
summary = models.CharField(max_length=1000, blank=True)
recdate = models.IntegerField(default=0)
def __str__(self):
return str(self.programid) + '-' + str(self.missionid)
class Meta:
verbose_name_plural = "Missions"
I saw a post about an 'intermediate linking table' to store the crew list but that also isn't ideal.
Thanks!
This is the use case for Django's ManyToManyField. Change the appropriate field on the missions:
class missions(models.Model):
crewmembers = models.ManyToManyField('astronauts')
You can access this from the Astronaut model side like so:
jeb = astronaut.objects.get(name='Jebediah Kerman')
crewed_missions = jeb.missions_set.all()
Or from the mission side like so:
mission = missions.objects.order_by('?')[0]
crew = mission.crewmembers.all()
This creates another table in the database, in case that is somehow a problem for you.

Categories