Optimizing serialization in Django - python

I've got the following models:
class Match(models.Model):
objects = BulkUpdateOrCreateQuerySet.as_manager()
id = models.AutoField(primary_key=True)
betsapi_id = models.IntegerField(unique=True, null=False)
competition:Competition = models.ForeignKey(Competition, on_delete=models.CASCADE, related_name='matches')
season:Season = models.ForeignKey(Season, on_delete=models.CASCADE, related_name='season_matches', null=True, default=None)
home:Team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='home_matches')
away:Team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='away_matches')
minute = models.IntegerField(default=None, null=True)
period = models.CharField(max_length=25, default=None, null=True)
datetime = models.DateTimeField()
status = models.IntegerField()
opta_match = models.OneToOneField(OptaMatch, on_delete=models.CASCADE, related_name='match', default=None, null=True)
updated_at = models.DateTimeField(auto_now=True)
created_at = models.DateTimeField(auto_now_add=True)
class Event(models.Model):
objects = BulkUpdateOrCreateQuerySet.as_manager()
id = models.AutoField(primary_key=True)
betsapi_id = models.IntegerField(unique=True)
name = models.CharField(max_length=255)
minute = models.IntegerField()
player_name = models.CharField(max_length=255, null=True, default=None)
extra_player_name = models.CharField(max_length=255, null=True, default=None)
period = models.CharField(max_length=255)
team:Team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='events')
match:Match = models.ForeignKey(Match, on_delete=models.CASCADE, related_name='events')
updated_at = models.DateTimeField(auto_now=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self) -> str:
return f"{self.betsapi_id} | {self.name} | {self.minute}"
And the following snippet of my serializer:
class TestMatchSerializer(serializers.Serializer):
home = TeamSerializer(many=False, read_only=True)
away = TeamSerializer(many=False, read_only=True)
competition = CompetitionSerializer(many=False, read_only=True)
events = serializers.SerializerMethodField()
class Meta:
model = Match
fields = ['id', 'home', 'away', 'competition', 'status']
def get_events(self, instance : Match):
events = instance.events.filter(name__in=["Corner", "Goal", "Substitution", "Yellow Card", "Red Card"]).order_by('-id')
return EventSerializer(events, many=True).data
When I serialize 25 objects using the following code including the events it takes about 0.72s.
all_matches_qs = Match.objects.all().prefetch_related('statistics').select_related('home', 'away', 'competition', 'competition__country')
TestMatchSerializer(all_matches_qs[:25], many=True, read_only=False).data
However when I change prefetch the events and the nested relationship events__team the serialization speed remains the same (0.72s). I used the following code to do so:
all_matches_qs = Match.objects.all().prefetch_related('events', 'events__team').select_related('home', 'away', 'competition', 'competition__country')
Now when I change the events variable in the serializer to events = EventSerializer(many=True, read_only=True). The time to serialize is reduced.
However I would like to filter the events before serializing it, how can I reliaze that?

Related

Django Relational managers

I was trying to delete my Apllication model:
class Application(models.Model):
app_type = models.ForeignKey(ApplicationCategory, on_delete=models.CASCADE, related_name='applications')
fio = models.CharField(max_length=40)
phone_number = models.CharField(max_length=90)
organisation_name = models.CharField(max_length=100, null=True, blank=True)
aid_amount = models.PositiveIntegerField()
pay_type = models.CharField(max_length=1, choices=PAY_CHOICES, default=PAY_CHOICES[0][0])
status = models.ForeignKey(AppStatus, on_delete=models.CASCADE, related_name='applications', null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
benefactor = models.ForeignKey(Benefactor, on_delete=models.CASCADE, related_name='applications', null=True)
def __str__(self):
return f"id={self.id} li {self.fio} ning mablag\'i!"
and this was my Benefactor model:
class Benefactor(models.Model):
fio = models.CharField(max_length=255)
phone_number = models.CharField(max_length=9)
image = models.ImageField(upload_to='media/')
sponsory_money = models.IntegerField()
organisation_name = models.CharField(max_length=55, null=True, blank=True)
def __str__(self):
return f"{self.fio}"
But I got the below message on superAdmin Panel:
TypeError at /admin/api/benefactor/
create_reverse_many_to_one_manager.\<locals\>.RelatedManager.__call__() missing 1 required keyword-only argument: 'manager'
I would expect delete smoothly!!
Your Benefactor model has several ForeignKey relationships that share the related_name. Give each a unique name and rerun your migrations.

Django - Getting an object from an object

I'm trying to get the object "Book" from prommotion. Book is a ForeignKey in "prommotion", and I filtered all the prommotions that are active. I need to get the "Book" object from the Prommotion if its active and return it.
(And I know promotion is spelled wrong)
Views:
class Book_PrommotionViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Prommotion.objects.filter(active=True)
serializer = PrommotionSerializer(queryset, many=True)
return Response(serializer.data, HTTP_200_OK)
Prommotion Model:
class Prommotion(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
precent = models.DecimalField(decimal_places=2, max_digits=255, null=True, blank=True)
active = models.BooleanField(default=False)
date_from = models.DateField()
date_to = models.DateField()
book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True, blank=True)
country = models.ForeignKey(Country, on_delete=models.SET_NULL, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'Prommotion'
verbose_name_plural = 'Prommotions'
Book Model:
class Book(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=255, null=True, blank=True)
author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True, blank=True)
price = models.DecimalField(decimal_places=2, max_digits=255)
published = models.DateField()
edition = models.CharField(max_length=255)
isbn_code = models.CharField(max_length=255)
pages = models.IntegerField(blank=True, null=True, default=0)
description = models.TextField(null=True, blank=True)
cover = models.CharField(max_length=30, choices=Cover.choices(), default=None, null=True, blank=True)
genre = models.CharField(max_length=30, choices=Genre.choices(), default=None, null=True, blank=True)
language = models.CharField(max_length=30, choices=Language.choices(), default=None, null=True, blank=True)
format = models.CharField(max_length=30, choices=Format.choices(), default=None, null=True, blank=True)
publisher = models.CharField(max_length=30, choices=Publisher.choices(), default=None, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class Meta:
verbose_name = 'Book'
verbose_name_plural = 'Books'
The first way to get all Books that are related to your active promotions is to extract the book ids from the queryset and pass it to a Book filter
active_promotions = Prommotion.objects.filter(active=True)
Book.objects.filter(id__in=active_promotions.values('book_id'))
Or simply filter books with active promotions by using the double underscore syntax to follow relationships
Book.objects.filter(prommotion__active=True).distinct()

Django: Query relational models in serializer

I have a Project and Memberships model where each project has a list of members. I need to find out if the current logged in user is listed as a member on the project from the project serializer, but I can't seem to get the request correct in my get_i_am_member method. I need to pass the project id from the Project model to the Membership model to filter the data, then check if the user in the filtered memberships model matches the user making the request. Can someone please assist? Here is my code:
######################################################
# Serializer
######################################################
class ProjectSerializer(LightWeightSerializer):
id = Field()
name = Field()
slug = Field()
description = Field()
created_date = Field()
modified_date = Field()
owner = MethodField()
members = MethodField()
is_private = Field()
anon_permissions = Field()
public_permissions = Field()
is_looking_for_people = Field()
looking_for_people_note = Field()
i_am_member = MethodField()
i_am_admin = MethodField()
my_permissions = MethodField()
def get_members(self, project):
members = Membership.objects.filter(project_id=project.id).select_related()
return MembershipSerializer(members, many=True, context=self.context).data
def get_i_am_member(self, request):
members_list = Membership.objects.filter(project_id=request.project.id).select_related('user')
for member in members_list:
if member.user == request.username:
print(member.user)
print("True")
return True
else:
print(member.user)
print("False")
return False
######################################################
# Models
######################################################
class Project(models.Model):
name = models.CharField(max_length=250, null=False, blank=False,
verbose_name=_("name"))
slug = models.SlugField(max_length=250, unique=True, null=False, blank=True,
verbose_name=_("slug"))
description = models.TextField(null=False, blank=False,
verbose_name=_("description"))
logo = models.FileField(upload_to=get_project_logo_file_path,
max_length=500, null=True, blank=True,
verbose_name=_("logo"))
created_date = models.DateTimeField(null=False, blank=False,
verbose_name=_("created date"),
default=timezone.now)
modified_date = models.DateTimeField(null=False, blank=False,
verbose_name=_("modified date"))
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
related_name="owned_projects", verbose_name=_("owner"), on_delete=models.CASCADE)
members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="projects",
through="Membership", verbose_name=_("members"),
through_fields=("project", "user"))
is_private = models.BooleanField(default=True, null=False, blank=True,
verbose_name=_("is private"))
anon_permissions = ChoiceArrayField(
models.TextField(null=False, blank=False, choices=ANON_PERMISSIONS),
null=True,
blank=True,
default=list,
verbose_name=_("anonymous permissions")
)
public_permissions = ChoiceArrayField(models.TextField(null=False, blank=False, choices=MEMBERS_PERMISSIONS),
null=True, blank=True, default=list, verbose_name=_("user permissions"))
is_featured = models.BooleanField(default=False, null=False, blank=True,
verbose_name=_("is featured"))
class Meta:
db_table = "projects"
verbose_name = "project"
verbose_name_plural = "projects"
ordering = ["name", "id"]
index_together = [
["name", "id"],
]
class Membership(models.Model):
# This model stores all project memberships. Also
# stores invitations to memberships that do not have
# assigned user.
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None,
related_name="memberships", on_delete=models.CASCADE)
project = models.ForeignKey(Project, null=False, blank=False,
related_name="memberships", on_delete=models.CASCADE)
role = models.ForeignKey('core.Role', null=False, blank=False,
related_name="memberships", on_delete=models.CASCADE)
is_admin = models.BooleanField(default=False, null=False, blank=False)
user_order = models.BigIntegerField(default=timestamp_ms, null=False, blank=False,
verbose_name=_("user order"))
class Meta:
db_table = "memberships"
verbose_name = "membership"
verbose_name_plural = "memberships"
unique_together = ("user", "project",)
ordering = ["project", "user__full_name", "user__username", "user__email"]
def get_related_people(self):
related_people = get_user_model().objects.filter(id=self.user.id)
return related_people
def clean(self):
# TODO: Review and do it more robust
memberships = Membership.objects.filter(user=self.user, project=self.project)
if self.user and memberships.count() > 0 and memberships[0].id != self.id:
raise ValidationError(_('The user is already member of the project'))
By default drf passes request object using context, so modification should be:
def get_i_am_member(self, project):
members_list = Membership.objects.filter(project_id=project.id).select_related('user')
for member in members_list:
if member.user == self.context['request'].user:
print(member.user)
print("True")
return True
else:
print(member.user)
print("False")
return False
But in case you manually call serializer, you need to pass it:
ProjectSerializer(instance=project, context={'request': request})

django - Runpython function to turn charfield into foreignkey

I've been strugglin to relate a csv imported data model with a spatial data model based on a CharField.
I've created both models and now im trying to transfer the data from one field to a new one to be the ForeignKey field. I made a Runpython funtion to apply on the migration but it goives the an error:
ValueError: Cannot assign "'921-5'":
"D2015ccccccc.rol_fk" must be a "D_Base_Roles" instance.
Here are the models:
class D_Base_Roles(models.Model):
predio = models.CharField(max_length=254)
dest = models.CharField(max_length=254)
dir = models.CharField(max_length=254)
rol = models.CharField(primary_key=True, max_length=254)
vlr_tot = models.FloatField()
ub_x2 = models.FloatField()
ub_y2 = models.FloatField()
instrum = models.CharField(max_length=254)
codzona = models.CharField(max_length=254)
nomzona = models.CharField(max_length=254)
geom = models.MultiPointField(srid=32719)
def __str__(self):
return str(self.rol)
class Meta():
verbose_name_plural = "Roles"
class D2015ccccccc(models.Model):
id = models.CharField(primary_key=True, max_length=80)
nombre_archivo = models.CharField(max_length=180, blank=True, null=True)
derechos = models.CharField(max_length=120, blank=True, null=True)
dir_calle = models.CharField(max_length=120, blank=True, null=True)
dir_numero = models.CharField(max_length=120, blank=True, null=True)
fecha_certificado = models.CharField(max_length=50, blank=True, null=True)
numero_certificado = models.CharField(max_length=50, blank=True, null=True)
numero_solicitud = models.CharField(max_length=50, blank=True, null=True)
rol_sii = models.CharField(max_length=50, blank=True, null=True)
zona_prc = models.CharField(max_length=120, blank=True, null=True)
##NEW EMPTY FOREIGNKEY FIELD
rol_fk = models.ForeignKey(D_Base_Roles, on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return str(self.numero_certificado)
class Meta:
managed = True
#db_table = 'domperm2015cip'
verbose_name_plural = "2015 Certificados Informaciones Previas"
ordering = ['numero_certificado']
The Runpython function:
def pop_rol(apps, schema_editor):
roles = apps.get_model('b_dom_edificacion', 'D2015ccccccc')
for r in roles.objects.all():
rol = roles
r.rol_fk = r.rol_sii
r.save()
D_Base_Roles.rol values are all unique, and 921-5 is one of those values.
What am I missing?
You probably need to assign an object, not a string. Change the line
r.rol_fk = r.rol_sii
to
r.rol_fk = D_Base_Roles.objects.get(rol=r.rol_sii)
Maybe adjust to whatever the correct field for looking up D_Base_Roles instances is.
Note: this will make a database query for every iteration of the for-loop

Django: get the max count of a foreign key based on other foreign key

I have this Model:
class Complaint(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=1)
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
name = models.CharField(max_length=255, unique=True)
definition = models.TextField(blank=False, default="")
is_violent = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
class Meta:
ordering = ['name']
def get_absolute_url(self):
return reverse('complaint-details', kwargs={'pk': self.pk})
class Service(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=1)
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
name = models.CharField(max_length=255, unique=True)
definition = models.TextField(blank=True, default="")
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('service-details', kwargs={'pk': self.pk})
class Location(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=1)
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
location_name = models.CharField(max_length=255, unique=True)
loc_lat = models.DecimalField(max_digits=9, decimal_places=6)
loc_long = models.DecimalField(max_digits=9, decimal_places=6)
pop = models.PositiveIntegerField(default=500)
is_AOR = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.location_name
class Blotter(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=1)
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True)#default=timezone.now().date()
date = models.DateField(blank=True)
time = models.TimeField(blank=True)
entry_number = models.CharField(max_length=255, unique=True,validators=[RegexValidator(r'^\d{1,255}$')])
complaints = models.ForeignKey(Complaint, on_delete=models.CASCADE, null=True, blank=True)
service = models.ForeignKey(Service, on_delete=models.CASCADE, null=True, blank=True)
information = models.TextField(blank=False, default="")
location = models.ForeignKey(Location, on_delete=models.CASCADE, null=True, blank=True)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ("date_created",)
def __str__(self):
return (self.entry_number)
def get_absolute_url(self):
return reverse('details-blotter', kwargs={'pk': self.pk})
And I have this serializer:
class APILocationListSerializer(serializers.Serializer):
address = serializers.CharField()
latitude = serializers.DecimalField(max_digits=9, decimal_places=5)
longitude = serializers.DecimalField(max_digits=9, decimal_places=5)
population= serializers.IntegerField()
crime_count=serializers.IntegerField()
crime_rate=serializers.DecimalField(max_digits=4, decimal_places=3)
is_aor = serializers.BooleanField()
class Meta:
model = Blotter
fields= [
'address',
'latitude',
'longitude',
'population',
'crime_count',
'crime_rate'
'is_aor',
]
def to_representation(self, value):
context = {
value['address']:
{
'coordinates':[value['latitude'],value['longitude']],
'Population': value['population'],
'Crime-Count': value['crime_count'],
'Crime-Rate': value['crime_rate'],
'Area-Of-Responsibility': value['is_aor'],
}
}
return context
And ListApiView:
class APILocationList(generics.ListAPIView):
serializer_class = APILocationListSerializer
def get_queryset(self):
q=Blotter.objects.values('location__location_name').annotate(
address=F('location__location_name'),
latitude=F('location__loc_lat'),
longitude=F('location__loc_long'),
population=F('location__pop'),
crime_count=Count('complaints', filter=Q(complaints__is_active=True) and Q(complaints__isnull=False)),
crime_rate=(Cast(F('crime_count'), FloatField())/Cast(F('population'), FloatField()))*100000,
is_aor=F('location__is_AOR')
)
q1 = q.filter(location__is_AOR=True).order_by('address')
query_search = self.request.GET.get("q")
if query_search:
q1 = q.filter(Q(location__is_AOR=True) and Q(location__location_name__icontains=query_search)).order_by('address')
return q1
I'm new to django and DRF. I want to achieve a result like this in my API Not Achieved
but this is the result that i've achieved so far Achieved
As you can see in the picture, I want to count the crime trend (highest count of a crime and the crime itself) in every location.
My questions are:
Is this even achievable/possible to get these results using just one query?
If yes, how?
If no, is there any other way to achieve these kind of results?
Thank you in advance!

Categories