Django complex query with page load slow - python

I have a quiz application made with Django. I am basically loading a pretty complex query. What I am basically doing is creating a new exam with this logic:
Fetch the question from the question bank (the Question model)
I get a certain questions number from each subject and I use them to create a QuestionDraft so I can store and save each student result.
Each QuestionDraft has the AnswerDraft which I use for storing the user answered questions (each AnswerDraft is basically a carbon copy of the question bank Answer).
While I wanna load a 50-question-test everything works pretty fine but when I want to load more than that amount of question, the page is loading extremely slow.
I would like to know if, based on the models and the code I post down below, there is any way to improve the efficiency of the queries and/or the loading time.
models.py
class QuestionSubject(models.Model):
quiz_course = models.ForeignKey(QuizTheoryCourse, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
exam_questions_num = models.IntegerField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Question(models.Model):
id = models.CharField(max_length=7,
default=generate_question_id,
unique=True,
primary_key=True,
editable=False)
question_subject = models.ForeignKey(
QuestionSubject, on_delete=models.CASCADE)
text = models.TextField()
mark = models.IntegerField(default=1)
is_published = models.BooleanField(default=True)
question_bank_id = models.CharField(max_length=255, blank=True, null=True)
question_topic = models.ForeignKey(
QuestionTopic, on_delete=models.CASCADE, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
text = models.TextField()
is_correct = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class QuizProfile(models.Model):
id = models.CharField(max_length=7,
default=generate_question_id,
unique=True,
primary_key=True,
editable=False)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
subject = models.ForeignKey(
QuestionSubject, on_delete=models.CASCADE, blank=True, null=True)
total_score = models.DecimalField(default=0,
decimal_places=2,
max_digits=5)
quiz_type = models.CharField(
max_length=255, choices=quiz_type, blank=True, null=True)
right_answer = models.IntegerField(default=0)
is_completed = models.BooleanField(default=False)
is_assigned = models.BooleanField(blank=True, null=True, default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.username + ' ' + self.id
class Meta:
ordering = ('-created_at',)
class QuestionDraft(models.Model):
quiz_profile = models.ForeignKey(QuizProfile,
on_delete=models.CASCADE,
blank=True,
null=True)
question = models.ForeignKey(Question,
on_delete=models.CASCADE,
blank=True,
null=True)
text = models.TextField()
is_answered = models.BooleanField(blank=True, null=True)
is_correct = models.BooleanField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.quiz_profile) + ' ' + str(self.question)
class AnswerDraft(models.Model):
quiz_profile = models.ForeignKey(QuizProfile,
on_delete=models.CASCADE,
blank=True,
null=True)
question_draft = models.ForeignKey(QuestionDraft, on_delete=models.CASCADE)
answer = models.ForeignKey(Answer,
on_delete=models.CASCADE,
null=True,
blank=True)
text = models.TextField()
is_answered = models.BooleanField(blank=True, null=True)
is_correct = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.quiz_profile) + ' ' + str(self.answer)
class QuizDraft(models.Model):
id = models.CharField(max_length=7,
default=generate_question_id,
unique=True,
primary_key=True,
editable=False)
quiz_profile = models.ForeignKey(QuizProfile, on_delete=models.CASCADE)
question_draft = models.ForeignKey(QuestionDraft,
on_delete=models.CASCADE,
blank=True,
null=True)
def __str__(self):
return str(self.quiz_profile)
views.py
def ppl_exam_generator_util(request, subjects):
question_list = [Question.objects.filter(question_subject=subject).order_by(
'?')[:int(subject.exam_questions_num)]for subject in subjects]
quiz_profile = QuizProfile(
user=request.user, quiz_type='ppl_exam')
quiz_profile.save()
for ques in question_list:
for q in ques:
question = Question.objects.get(id=q.id)
quiz_attempt = QuizAttempt(quiz_profile=quiz_profile,
question=question)
quiz_attempt.save()
question_draft = QuestionDraft(
question=question, text=question.text, quiz_profile=quiz_profile)
question_draft.save()
quiz_draft = QuizDraft(
quiz_profile=quiz_profile, question_draft=question_draft)
quiz_draft.save()
answers = Answer.objects.filter(question__id=question.id)
shuffle(list(answers))
for ans in answers:
draft_answer = AnswerDraft(
question_draft=question_draft, answer=ans, text=ans.text, quiz_profile=quiz_profile)
draft_answer.save()
return HttpResponseRedirect(reverse('quiz:new_ppl_exam', kwargs={'pk': quiz_profile.id}))
I tried also with the connection.queries tool Django provides, but each query is pretty fast. I don't know if it's a problem with the nested forloop or something else.

Related

Annotate performance Django

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 Season(models.Model):
id = models.AutoField(primary_key=True)
competition:Competition = models.ForeignKey(to=Competition, on_delete=models.CASCADE, null=False, blank=False, related_name='seasons')
start_date = models.DateTimeField(blank=False, null=False)
end_date = models.DateTimeField(blank=False, null=False)
name = models.CharField(max_length=255, null=True, default=None)
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)
And I wrote the following annotations to a Match object:
all_matches_qs = (Match
.objects
.filter(status=1)
.select_related('home', 'away', 'competition', 'competition__country', 'season', 'opta_match')
.prefetch_related(
Prefetch("season", queryset=Season.objects.prefetch_related(
Prefetch("season_matches", queryset=Match.objects.prefetch_related(
Prefetch('statistics')))
)),
Prefetch('events', queryset=Event.objects.select_related("team", "match").filter(name__in=["Corner", "Goal", "Substitution", "Yellow Card", "Red Card"])),
)
.annotate(season_statistics_count=Count('season__season_matches__statistics'))
.annotate(goals=Count('events', distinct=True, filter=(Q(events__name='Goal') & Q(events__team=F('home')))))
)
Executing this on about 25 records takes me about 3.75 seconds whereas if I remove one of the annotations (doesn't matter which one) the time to execute drops to about 0.5 seconds.
Also, removing the filter(status=1) seems to be dropping the time to execute to near 0.3s.
I'm wondering what is causing this issue and how to ensure I can execute both annotations AND the filter without a drastic performance drop?

Serialize same level object from Many to many field

I have to serialize spare instance from spare variety many to many field.
Models.py
class SpareVariety(models.Model):
quality = models.ForeignKey(Quality, max_length=255, on_delete=models.CASCADE, null=True, blank=True)
variety_name = models.CharField(max_length=255, null=True, blank=True)
property = models.ForeignKey(SpareProperty, null=True, blank=True, on_delete=models.CASCADE)
purchase_price = models.PositiveIntegerField(help_text="in INR", blank=True, null=True)
retail_price = models.FloatField(help_text="All values in INR", blank=True, null=True)
dealer_price = models.FloatField(help_text="All values in INR", blank=True, null=True)
stock_available = models.PositiveIntegerField(blank=True, null=True,default=True)
spare_costing = models.ForeignKey(SpareCosting, on_delete=models.CASCADE, blank=True, null=True)
spare_discount = models.ForeignKey(Discount, on_delete=models.CASCADE, blank=True, null=True)
is_available = models.BooleanField(default=False)
date_added = models.DateTimeField(auto_now=True)
date_updated = models.DateTimeField(auto_now_add=True)
class Spare(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
spare_variety = models.ManyToManyField(SpareVariety, related_name='spare_varieties', null=True, blank=True)
name = models.CharField(max_length=255, help_text="Enter the name of spare (Ex:Display, Speakers)")
type = models.ForeignKey(Type, blank=True, null=True, on_delete=models.CASCADE)
date_added = models.DateTimeField(auto_now=True)
date_updated = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s' % (self.product.name, self.name)
Serialize the spare model from spare variety serializer
serializers.py
class SpareVarietySerializer(serializers.HyperlinkedModelSerializer):
spare_costing= SpareCostingSerializer(many=False, read_only=False)
spare_discount = DiscountSerializer(many=False, read_only=False)
quality = QualitySerializer(many=False, read_only=False)
property = SparePropertySerializer(many=False, read_only=False)
spare_name = serializers.CharField(read_only=True, source="spare.name")
class Meta:
model = SpareVariety
fields = ['id','quality','variety_name','purchase_price','spare_name','retail_price','property', 'dealer_price', 'stock_available','spare_costing','spare_discount','is_available', 'date_added', 'date_updated',]

django error (no such column: projects_review.project_id)

I can't open my Review table, here's the models of code:
This is the `Project` model:
class Project(models.Model):
title = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True)
demo_link = models.CharField(max_length=2000, null=True, blank=True)
source_link = models.CharField(max_length=2000, null=True, blank=True)
tags = models.ManyToManyField('Tag', blank=True)
vote_total = models.IntegerField(default=0, null=True, blank=True)
vote_ratio = models.IntegerField(default=0, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, unique=True,
primary_key=True, editable=False)
def __str__(self):
return self.title
And this is the Review model:
class Review(models.Model):
VOTE_TYPE = (
('up', 'Up Vote'),
('down', 'Down Vote'),
)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
body = models.TextField(null=True, blank=True)
value = models.CharField(max_length=200, choices=VOTE_TYPE)
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, unique=True,primary_key=True, editable=False)
def __str__(self):
return self.title
but I can't open my Review table on admin panel (OperationalError at /admin/projects/review/)
Here's the error details:
I make migrations and do migrate:

Find pk in queryset Django

I have a problem in obtaining a single id from a queryset. I post my models and views in order to be more clear:
models.py
class MissionEntry(models.Model):
student = models.ForeignKey(
Student, on_delete=models.DO_NOTHING, blank=True, null=True)
mission = models.ForeignKey(
Mission, on_delete=models.DO_NOTHING, null=True, blank=True)
log_entry = models.ForeignKey(
LogEntry, on_delete=models.DO_NOTHING, blank=True, null=True)
learning_objective = models.ForeignKey(
LearningObjective, on_delete=models.DO_NOTHING, blank=True, null=True)
grade = models.CharField(
max_length=10, choices=GRADING_VALUE, blank=True, null=True)
note = models.TextField(blank=True, null=True)
debriefing = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.mission) + ' ' + str(self.log_entry)
class Meta:
verbose_name_plural = 'Mission Entries'
class MissionEntryStatus(models.Model):
mission = models.ForeignKey(
Mission, on_delete=models.PROTECT, null=True, blank=True)
student = models.ForeignKey(Student, on_delete=models.PROTECT)
is_completed = models.BooleanField(default=False)
is_failed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class StudentMission(models.Model):
mission = models.ForeignKey(Mission, on_delete=models.PROTECT)
student_training_course = models.ForeignKey(
StudentTrainingCourse, on_delete=models.PROTECT)
mission_status = models.ForeignKey(
MissionEntryStatus, on_delete=models.PROTECT, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['mission__name']
def __str__(self):
return self.mission.name
class LogEntry(models.Model):
aircraft = models.ForeignKey(Aircraft, on_delete=models.DO_NOTHING)
adep = models.ForeignKey(
Aerodrome, on_delete=models.PROTECT, related_name='adep')
ades = models.ForeignKey(
Aerodrome, on_delete=models.PROTECT, related_name='ades')
date = models.DateField()
etd = models.TimeField()
ata = models.TimeField()
eet = models.TimeField()
function_type = models.ForeignKey(FunctionType, on_delete=models.PROTECT)
student = models.ForeignKey(
Student, on_delete=models.PROTECT, blank=True, null=True)
instructor = models.ForeignKey(
Instructor, on_delete=models.PROTECT, blank=True, null=True)
student_mission = models.ForeignKey(
'mission.StudentMission', on_delete=models.PROTECT, null=True, blank=True)
note = models.TextField(null=True, blank=True)
cross_country = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
views.py
def student_mission_list(request, pk):
student = Student.objects.get(id=pk)
student_training_course = StudentTrainingCourse.objects.filter(
student_id=student.id)
missions = StudentMission.objects.filter(
student_training_course_id__in=student_training_course)
mission_entry = MissionEntry.objects.filter(student_id=student)
log_entry = LogEntry.objects.filter(student_mission_id__in=missions)
print(log_entry)
context = {
'student': student,
'missions': missions,
'mission_entry': mission_entry,
}
return render(request, 'mission/student_mission_list.html', context)
In fact, what I need to do, is to obtain a single value for the log_entry. The problem is that, obviously, I am retrieving multiple values of log_entry. But I would like to get the single pk of the log_entry.
Any suggestion? Should I remodel the models.py file?
try this:
log_entry = LogEntry.objects.get(student_mission_id__in=missions)
print(log_entry.id)

How to join two different ORM query to return them as one JSON object

I have been trying to output get result of both author name and book name is the same json dict, I got the all the books written by an author but I want author name before books too and here is the relevant code:
Models
class Books(models.Model):
bid = models.BigIntegerField(primary_key=True)
bname = models.CharField(max_length=200, blank=True, null=True)
bdescription = models.TextField(blank=True, null=True)
def __str__(self):
return self.bname
class Authors(models.Model):
aid = models.AutoField(primary_key=True)
aname = models.CharField(max_length=200, blank=True, null=True)
adescription = models.TextField( blank=True, null=True)
def __str__(self):
return self.aname+"\n"
class Bookauth(models.Model):
bid = models.ForeignKey(Books, on_delete=models.DO_NOTHING, db_column='bid', blank=True, null=True)
aid = models.ForeignKey(Authors, on_delete=models.DO_NOTHING, db_column='aid', blank=True, null=True)
Views
def authbook(request):
l = request.GET.get('bks', '')
r = Books.objects.filter(bookauth__aid = l).values('bname')
s = Authors.objects.filter(aid = l).values('aname')
return JsonResponse({s: list(r)})
this code obviously gives an error regarding s not being a string.

Categories

Resources