Django ORM Table Relationships - python

I have models defined like so:
class Games(models.Model):
title = models.CharField(max_length=50)
owned = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
class Votes(models.Model):
game = models.ForeignKey(Games)
created = models.DateTimeField(auto_now_add=True)
And i'm doing the following in my view:
wanted = Games.objects.filter(owned=0)
for game in wanted:
game.vote = Votes.objects.all().filter(game_id=game.id).count()
wanted = sorted(wanted, key=attrgetter('vote'), reverse=True)
It works fine but is there a more Django way of doing this?

from django.db.models import Count
Games.objects.filter(owned=0).annotate(vote=Count('votes')).order_by('-vote')
see Django aggregation for more information

Related

How can I fetch data by joining two tables in django?

Looking for help got stuck at a point, I am new to python and django. There ARE two payments corresponding to one order, one COLLECTION and multiple TRANSFER and i need the payment corresponding to an order whose direction is COLLECTION only NOT transfered yet so that i can initiate TRANSFER against that order
models.py
class Orders(models.Model):
id= models.AutoField(primary_key=True)
payment_gateway_code = models.CharField(max_length=20,choices=[('PAYTM','PAYTM')])
is_active = models.BooleanField(default=True)
class Payments(models.Model):
id = models.AutoField(primary_key=True)
orders = models.ForeignKey(Orders, on_delete=models.CASCADE)
direction = models.CharField(max_length=20,choices=[('COLLECTION','COLLECTION'),
('TRANSFER','TRANSFER')])
settlement_status = models.CharField(max_length=50,blank=True, null=True,choices=[('YES','YES'),
('NO','NO')])
is_active = models.BooleanField(default=True)
qualified_orders = Orders.objects.filter(payment_gateway_code='CASHFREE',
Exists(Payments.objects.filter(order=OuterRef('pk'), direction='COLLECTION',
settlement_status='YES')), ~Exists(Payments.objects.filter(order=OuterRef('pk'),
direction='TRANSFER')))
But above query is not working
What is OuterRef('pk')?
First, I'd suggest changing orders to order.
Then, the query you're trying to achieve will be something like this (Assuming order_id contains the ID of the order):
Paymen.objects.filter(order_id=order_id, direction="COLLECTION")
You can use views.py for that as follows
Models.py
class Orders(models.Model):
id= models.AutoField(primary_key=True)
payment_gateway_code = models.CharField(max_length=20,choices=[('PAYTM','PAYTM')])
is_active = models.BooleanField(default=True)
class Payments(models.Model):
id = models.AutoField(primary_key=True)
orders = models.ForeignKey(Orders, on_delete=models.CASCADE)
direction = models.CharField(max_length=20,related_name="direction",choices=[('COLLECTION','COLLECTION'),
('TRANSFER','TRANSFER')])
settlement_status = models.CharField(max_length=50,blank=True, null=True,choices=[('YES','YES'),
('NO','NO')])
is_active = models.BooleanField(default=True)
views.py
from App.models import orders, payments
#in case if you need objects of order this is for that
def orderfunc():
order = Orders.objects.all()
def paymentfunc():
payment = Payment.objects.all()
# from here you can check for what record you want using conditional operator
#if direction == COLLECTION:
#then do what you need

Django REST framework - filtering against query param with date Outside Views.py file

I created my "API" using REST framework, now trying to do filtering for it. That's how my models.py look for BookingStatement model.
class BookingStatement(BaseModel):
ticket_number = models.PositiveIntegerField(unique=True)
booking = models.OneToOneField(Booking, on_delete=models.PROTECT)
user_rate = AmountField()
agent_rate = AmountField()
total_amount = AmountField()
class Meta:
default_permissions = ()
def __str__(self):
return str(self.id)
Booking is One to One Key so the booking model has following Attributes.
class Booking(BaseModel):
bus_seat = models.ManyToManyField(Seat)
schedule = models.ForeignKey(Schedule, on_delete=models.PROTECT)
boarding_point = models.ForeignKey(
BoardingPoint,
on_delete=models.PROTECT,
null=True
)
remarks = models.JSONField(null=True, blank=True)
contact = PhoneNumberField(null=True)
booking_date_time = models.DateTimeField(auto_now_add=True)
class Meta:
default_permissions = ()
verbose_name = 'Booking'
verbose_name_plural = 'Bookings'
def __str__(self):
return '{}-{}'.format(self.user, self.customer_name)
I used generic ListAPIView in my views.py as following.
class BusCompanyTicketDetailView(generics.ListAPIView, BusCompanyMixin):
serializer_class = serializers.TicketDetailResponseSerializer
def get_queryset(self):
travel_date = (int(self.request.query_params.get('booking_date')))
print(booking_date)
return usecases.ListBusCompanyTicketUseCase(date=#'what should i pass?'#).execute()
I use usecases.py to filter booking_date_time with url as following.
http://127.0.0.1:8000/api/v1/ticket/list?booking_date=2021-1-29
So my usecase file to filter the Booking time is as following.
class ListBusCompanyTicketUseCase(BaseUseCase):
def __init__(self, date:datetime):
self._date = datetime
def execute(self):
self._factory()
return self._booking_statements
def _factory(self):
self._booking_statements = BookingStatement.objects.filter(booking__booking_date_time=?? need to get date from views.)
Problem is I don't know to how to get query params from url in my usecases to filter with booking date any help will be very helpful.
you should use django-filter to implement filtering on the viewsets. It's a bit of knowledge you have to build up, but once you understand it, you can do a lot of complex filtering logic with it. Trying to implement a filtering system yourself is always more difficult in the long run. For starting point, check out the official documentation: https://www.django-rest-framework.org/api-guide/filtering/. For the filter, check out the documentation on DRF integration: https://django-filter.readthedocs.io/en/stable/guide/rest_framework.html.

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)

Django - How to link tables

Hello to the stackoverflow team,
I have the following two django tables:
class StraightredFixture(models.Model):
fixtureid = models.IntegerField(primary_key=True)
soccerseason = models.IntegerField(db_column='soccerSeason') # Field name made lowercase.
hometeamid = models.IntegerField()
awayteamid = models.IntegerField()
fixturedate = models.DateTimeField()
fixturestatus = models.CharField(max_length=24)
fixturematchday = models.IntegerField()
hometeamscore = models.IntegerField()
awayteamscore = models.IntegerField()
class Meta:
managed = False
db_table = 'straightred_fixture'
class StraightredTeam(models.Model):
teamid = models.IntegerField(primary_key=True)
teamname = models.CharField(max_length=36)
teamcode = models.CharField(max_length=5)
teamshortname = models.CharField(max_length=24)
class Meta:
managed = False
db_table = 'straightred_team'
In the views.py I know I can put the following and it works perfectly:
def test(request):
fixture = StraightredFixture.objects.get(fixtureid=136697)
return render(request,'straightred/test.html',{'name':fixture.hometeamid})
As I mentioned above, this all works well but I am looking to return the teamname of the hometeamid which can be found in the StraightredTeam model.
After some looking around I have been nudged in the direction of "select_related" but I am unclear on how to implement it in my existing tables and also if it is the most efficient way for this type of query. It feels right.
Please note this code was created using "python manage.py inspectdb".
Any advice at this stage would be greatly appreciated. Many thanks, Alan.
See model relationships.
Django provides special model fields to manage table relationships.
The one suiting your needs is ForeignKey.
Instead of declaring:
hometeamid = models.IntegerField()
awayteamid = models.IntegerField()
which I guess is the result of python manage.py inspectdb, you would declare:
home_team = models.ForeignKey('<app_name>. StraightredTeam', db_column='hometeamid', related_name='home_fixtures')
away_team = models.ForeignKey('<app_name>. StraightredTeam', db_column='awayteamid', related_name='away_fixtures')
By doing this will, you tell the Django ORM to handle the relationship under the hood, which will allow you to do such things as:
fixture = StraightredFixture.objects.get(fixtureid=some_fixture_id)
fixture.home_team # Returns the associated StraightredTeam instance.
team = StraightredTeam.objects.get(team_id=some_team_id)
team.home_fixtures.all() # Return all at home fixtures for that team.
I am not sure if this makes sense for Managed=False, but I suppose the sane way of doing it in Django would be with
home_team = models.ForeignKey('StraightRedFixture', db_column='fixtureid'))
And then just using fixture.home_team instead of doing queries by hand.

list Box Django Forms

I'm using Django forms and need to create a list box.
What would be the equivalent of listbox in Django form fields?
I checked the documentation #
https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelchoicefield
but unable to find it.
Here is my code snippet,
Models.py
class Volunteer(models.Model):
NO_OF_HRS = (('1','1')
('2','2'))
datecreated = models.DateTimeField()
volposition = models.CharField(max_length=300)
roledesc = models.CharField(max_length=300)
Duration = models.CharField(choices=NO_OF_HRS,max_length=1)**
forms.py
class VolunteerForm(forms.ModelForm)
datecreated = forms.DateField(label=u'Creation Date')
volposition = forms.CharField(label=u'Position Name', max_length=300)
roledesc = forms.roledesc(label=u'Role description',max_length=5000)
Duration = forms.CharField(widget=forms.select(choices=NO_OF_HRS),max_length=2)
When I try to run, I get the following error,
NO_OF_HRS is not defined
Your NO_OF_HRS tuple is defined inside the model and not available to the form. It has to be imported in forms.py just like any other Python object. Try moving the tuple outside the model definition and import in your forms.py like this:
models.py
NO_OF_HRS = (('1','1')
('2','2'))
class Volunteer(models.Model):
# ...
duration = models.CharField(choices=NO_OF_HRS, max_length=1)
forms.py
from path.to.models import NO_OF_HRS
class VolunteerForm(forms.Form):
# ...
duration = forms.CharField(widget=forms.Select(choices=NO_OF_HRS), max_length=1)
It also looks like you want to use a ModelForm. In this case you don't need to add any field definitions to your VolunteerForm, simply set your model in the inner Meta class.
forms.py
from path.to.models Volunteer
class VolunteerForm(forms.ModelForm):
class Meta:
model = Volunteer

Categories