Queryset takes too long to populate data - django-views

I have a django view that generates a form depending on POST or GET. It works well with little data on the Student model but stagnates when the data gets big. Is there a remedy I can apply??
Here's the view
def My_View(request,pk):
if request.method == 'POST':
try:
form = NewIssueForm(request.POST,school= request.user.school,pk=pk,issuer = request.user)
if form.is_valid():
name = form.cleaned_data['borrower_id'].id
form.save(commit=True)
books = Books.objects.get(id=pk)
Books.Claimbook(books)
return redirect('view_books')
messages.success(request,f'Issued successfully')
except Exception as e:
messages.warning(request,f"{e}")
return redirect('view_books')
else:
form = NewIssueForm(school= request.user.school,pk=pk,issuer = request.user)
return render(request, 'new_issue.html', {'form': form})
Here's the form
class NewIssueForm(forms.ModelForm):
def __init__(self,*args, pk,school,issuer, **kwargs):
super(NewIssueForm, self).__init__(*args, **kwargs)
booqs = Books.objects.filter(school=school).get(id=pk)
self.fields['issuer'].initial = issuer
self.fields['borrower_id'].Student.objects.filter(school = school)
self.fields['book_id'].label = str(booqs.book_name) + " - " + str(booqs.reg_no)
self.fields['book_id'].initial = pk
class Meta:
model = Issue
fields = ['issuer','book_id','borrower_id','due_days']
widgets = {
'book_id':forms.TextInput(attrs={"class":'form-control','type':''}),
'issuer':forms.TextInput(attrs={"class":'form-control','type':'hidden'}),
'borrower_id': Select2Widget(attrs={'data-placeholder': 'Select Student','style':'width:100%','class':'form-control'}),
'due_days':forms.TextInput(attrs={"class":"form-control"}),
Student model
class Student(models.Model):
school = models.ForeignKey(School, on_delete=models.CASCADE)
name = models.CharField(max_length=200,help_text="The student's name in full")
now = datetime.datetime.now()
YEAR = [(str(a), str(a)) for a in range(now.year-5, now.year+1)]
year = models.CharField(max_length=4, choices = YEAR,help_text='The year the student is admitted in school')
student_id = models.CharField(max_length=40,help_text = "This is the student's admission number")
klass = models.ForeignKey(Klass,on_delete=models.CASCADE)
stream = models.ForeignKey(Stream,on_delete=models.CASCADE)
graduated = models.BooleanField(default=False,help_text = "Tick the box to mark the student as graduated")
prefect = models.BooleanField(default=False,help_text = "Tick the box to select the student as a prefect")
def __str__(self):
return "%s - %s - F%s %s"%(self.student_id,self.name,self.klass,self.stream)
class Meta:
verbose_name_plural = 'Students'
unique_together = ("school", "student_id",)
indexes = [models.Index(fields=['student_id']),
models.Index(fields=['name', ]),

The reason this happens is because when you render the Student, it will need to fetch the related Klass and Stream objects, and will make a query per Student object. This problem is called an N+1 problem since fetching n student requires one query to fetch all the Students, and then one query per Student for the Klasses, and one query per Student for the Streams.
You can select the data when you select the students with:
self.fields['borrower_id'].Student.objects.filter(
school=school
).select_related('klass', 'stream')
Depending on the __str__ implementations of other models, you might have to select data along with these models as well.

Can't guess your model but it seems like select_related may play a trick.

Related

Django how to iterate a form and save multiple objects?

I have a modelform (model: Student) with a TextField where the user enters several names at once. My intention is for my view to parse this text input (getting a first name, last name, and then coming up with a nickname) and loop through the lines, saving a new student each time through the loop. However, only the last time through the loop is a student saved.
In the past I have solved this problem by using a custom save method in my model but I wanted to try doing it all in the view. I saw some posts in stackoverflow such as this one where it seemed that others were able to iterate through a loop and save objects.
models.py
class Student(models.Model):
classroom = models.ForeignKey(Classroom, on_delete=models.CASCADE)
student_first = models.CharField(default='John', max_length=30)
student_last = models.CharField(default='Smith', max_length=30)
nickname = models.CharField(default='JohnS', max_length=31)
attend = models.BooleanField(default=True)
do_not_pick = models.BooleanField(default=False)
multistudentinput = models.TextField(blank=True, default='')
def __str__(self):
return self.nickname
forms.py
class MultiStudentForm(ModelForm):
class Meta:
model = Student
fields = ('multistudentinput',)
views.py
def addmultistudent(request, classroom_id):
classblock = Classroom.objects.get(id=classroom_id)
if request.method == 'POST':
form = MultiStudentForm(request.POST)
if form.is_valid():
student = form.save(commit=False)
# save the classroom that the student belongs to
student.classroom = classblock
student_list = []
student_list = student.multistudentinput.split('\n')
for line in student_list:
line = line.strip('\r')
all_names = line.split()
num_names = len(all_names)
if num_names == 2:
last = all_names[1]
student.student_last = last
else:
last = ' '
student.student_last = ''
student.nickname = all_names[0] + last[:1]
student.student_first = all_names[0]
print(student)
student.save()
form = MultiStudentForm(None)
context = {'form': form}
return render(request, "gradebook/addmultistudent.html", context)
else:
context = {'form': form}
return render(request, "gradebook/addmultistudent.html", context)
else:
form = MultiStudentForm(None)
context = {'form': form}
return render(request, "gradebook/addmultistudent.html", context)

How to fix QuerySet' object has no attribute issue?

I need to create the new object or just update if already existing. I receive: QuerySet' object has no attribute "seat". Don't know what I'm doing wrong.
models:
class rows_and_seats(models.Model):
movie = models.ForeignKey(Movies, on_delete=models.CASCADE)
row = models.CharField(max_length = 1)
number = models.IntegerField()
def __str__(self):
return f'{self.movie}'
class Reservation(models.Model):
customer = models.ForeignKey(User, on_delete=models.CASCADE)
movie = models.ForeignKey(Movies, on_delete=models.CASCADE)
seat = models.ManyToManyField(rows_and_seats)
ordered = models.DateTimeField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), blank=True, null=True)
def __str__(self):
return f'{self.customer.username}:{self.movie.title}:{self.ordered}'
views
#login_required
def buy_seats(request, pk):
if request.method == "POST" and request.session.get("seats"):
seats = request.session.pop("seats")
movie = Movies.objects.get(pk=pk)
customer = User.objects.get(pk=request.user.id)
for s in seats:
user_reserved_seats = rows_and_seats.objects.get(movie=movie, row=s[:1], number=int(s[2:]))
reservation_check = Reservation.objects.filter(customer=customer, movie=movie)
if reservation_check.exists():
reservation_check.seat.add(user_reserved_seats)
else:
new_reservation = Reservation.objects.create(customer=customer, movie=movie)
new_reservation.seat.add(user_reserved_seats)
messages.success(request,"You have succesfully reserved the seats.")
return redirect("home")
return redirect("home")
My goal is to keep rows_and_seat in manyTomany in order to display only one reservation of user in admin panel, instead of the list of repeating itself titles.
You can access the value after the exists() check:
if reservation_check.exists():
reservation_check.first().seat.add(user_reserved_seats)
else:
new_reservation = Reservation.objects.create(customer=customer, movie=movie)
new_reservation.seat.add(user_reserved_seats)
Maybe you can use something like get_or_create:
user_reserved_seats = rows_and_seats.objects.get(movie=movie, row=s[:1], number=int(s[2:]))
reservation, created = Reservation.objects.get_or_create(
customer=customer, movie=movie,
)
reservation.seat.add(user_reserved_seats)
Also you might be looping over the seats too many times, maybe you can add all the seats in only one assignment.

Django adding data form multiple forms of one model to database

I have one model Measurement, two forms MeassurementSystolicPressureForm and MeassurementDiastolicPressureForm. I want to make a view that allows user to add both of them to the database. Each has fields: username, measurement_date, value, measurement_type. When I fill forms on my webpage two records are added to the db, each has a good username and measurement_type, but measurement_date and value are the same for both records. Can you help me spotting what I'm doing wrong?
Here is my code:
models.py
class Measurement(models.Model):
value = models.IntegerField()
measurement_type = models.CharField(max_length=6, default='measurement', blank=True)
username = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
measurement_date = models.DateTimeField(default=datetime.now, editable=True)
forms.py
class MeassurementSystolicPressureForm(ModelForm):
class Meta:
model = Measurement
fields = ['value', 'measurement_date']
class MeassurementDiastolicPressureForm(ModelForm):
class Meta:
model = Measurement
fields = ['value', 'measurement_date']
views.py
def new_measurement(request):
if request.method == 'POST':
form_SP = MeassurementSystolicPressureForm(request.POST or None)
form_DP = MeassurementDiastolicPressureForm(request.POST or None)
if form_CS.is_valid() or form_CR.is_valid():
temp_S = form_SP.save(commit=False)
temp_S.username = request.user
temp_S.measurement_type = 'syspres'
temp_S.save()
temp_D = form_DP.save(commit=False)
temp_D.username = request.user
temp_D.measurement_type = 'diapres'
temp_D.save()
return redirect('/')
else:
form_SP = MeassurementSystolicPressureForm()
form_DP = MeassurementDiastolicPressureForm()
args = {'form_SP': form_SP, 'form_DP': form_DP}
return render(request, 'measurements.html', args)
If for example I submit data for:
Systolic Pressure:
value: 120
date: 2019-01-15 16:15:32
Diastolic Pressure:
value: 80
date: 2019-01-15 15:00:00`
In my database I have two records:
username: Julka, measurement_type:
syspres, value: 80, date: 2019-01-15 15:00:00
username: Julka, measurement_type: diapres, value: 80, date: 2019-01-15 15:00:00
I have no idea what to do.
In an HttpRequest object, the GET and POST attributes are instances of django.http.QueryDict. This type alone cannot determine which form was submitted. Your forms have the same fields, so then one form is valid, other form valid too. That's why you have measurement_date and value are the same for both records. To solve this problem, you can add additional hidden fields to your forms and look at them from which form was sent. Some like this:
class MeassurementSystolicPressureForm(ModelForm):
flag_Systolic = forms.IntegerField()
class Meta:
model = Measurement
fields = ['value', 'measurement_date']
def __init__(self, *args, **kwargs):
super(MeassurementSystolicPressureForm, self).__init__(*args, **kwargs)
self.fields['flag_Systolic'].widget = forms.HiddenInput()
class MeassurementDiastolicPressureForm(ModelForm):
flag_Diastolic = forms.IntegerField()
class Meta:
model = Measurement
fields = ['value', 'measurement_date']
def __init__(self, *args, **kwargs):
super(MeassurementDiastolicPressureForm, self).__init__(*args, **kwargs)
self.fields['flag_Diastolic'].widget = forms.HiddenInput()
and in your views:
def new_measurement(request):
if request.method == 'POST':
if 'flag_Systolic' in request.POST:
form_SP = MeassurementSystolicPressureForm(request.POST)
if form_SP.is_valid():
temp_S = form_SP.save(commit=False)
temp_S.username = request.user
temp_S.measurement_type = 'syspres'
temp_S.save()
return redirect('/')
elif 'flag_Diastolic' in request.POST:
form_DP = MeassurementDiastolicPressureForm(request.POST)
if form_DP.is_valid():
temp_D = form_DP.save(commit=False)
temp_D.username = request.user
temp_D.measurement_type = 'diapres'
temp_D.save()
return redirect('/')
else:
form_SP = MeassurementSystolicPressureForm()
form_DP = MeassurementDiastolicPressureForm()
args = {'form_SP': form_SP, 'form_DP': form_DP}
return render(request, 'measurements.html', args)
I know maybe it is too late but it might be helpful for other people facing the same problem.
One easier solution would be creating the object in the View and passing it to both forms:
from .models import Measurement
def new_measurement(request):
user=request.user #the authenticated user
if request.method == 'POST':
measurement=Measurement(username=user)
form_SP = MeassurementSystolicPressureForm(request.POST or None, instance=measurement)
form_DP = MeassurementDiastolicPressureForm(request.POST or None, instance=measurement)
if form_CS.is_valid() or form_CR.is_valid():
form_CS.save()
form_CR.save()
return redirect('/')
else:
form_SP = MeassurementSystolicPressureForm()
form_DP = MeassurementDiastolicPressureForm()
args = {'form_SP': form_SP, 'form_DP': form_DP}
return render(request, 'measurements.html', args)

demo application issue with models and queryset

I am trying to make one simple application but seems like facing issue. I have created many to many object between student and course and has also define dept.
My model is mentioned below:
class Course(models.Model):
courseId = models.AutoField(primary_key=True)
courseName = models.CharField(max_length=100)
enrolledStu = models.IntegerField(max_length=3)
students = models.ManyToManyField(Student)
dept = models.ForeignKey(Dept, on_delete=models.CASCADE)
def __str__(self):
return '%s %s %s %s' % (self.courseName,self.enrolledStu,self.students,self.dept)
class Dept(models.Model):
deptId = models.AutoField(primary_key=True)
deptName = models.CharField(max_length=100)
def __str__(self):
return '%s %s' % (self.deptId, self.deptName)
class Student(models.Model):
stuName = models.CharField(max_length=100)
stuCity = models.CharField(max_length=100)
stuPhone = models.IntegerField(max_length=10)
stuNationality = models.CharField(max_length=50)
stuCreatedt = models.DateTimeField(default=timezone.now)
def __str__(self):
return '%s %s %s' % (self.stuName,self.stuCity,self.stuNationality)
my form is :
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ('stuName','stuCity','stuPhone','stuNationality','stuCreatedt')
class CourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ('courseId','courseName','enrolledStu','students','dept')
class DeptForm(forms.ModelForm):
class Meta:
model = Dept
fields = ('deptId','deptName')
I have displayed list of course , students and dept in html template now i am trying to edit it with code :
def edtStudent(request,pk):
course = Course.objects.filter(pk=1).prefetch_related('students').select_related('dept').get()
if request.method =="POST":
form = CourseForm(request.POST,instance=Course)
if form.is_valid():
course = form.save(commit=False)
print(form.cleaned_data)
course.courseName = request.POST['courseName']
course.enrolledStu = request.Post['enrolledStu']
course.save()
course.save_m2m()
return redirect('liststudent')
else:
print(course.__dict__)
print(course.students)
#form = CourseForm()
#return render(request, 'stuApp/edtStudent.html', {'form':form})
#form = CourseForm(instance=course[0]) worked
form = CourseForm(instance=course)
return render_to_response('stuApp/edtStudent.html', {'form': form})
so instead of getting one student i am getting all students for course.. Seems like my query is incorrect. Can you help with that..
Another question i have is how can i print values for objects in many to many relationship.. for now if i print course object i am getting results like
{'_state': <django.db.models.base.ModelState object at 0x000000000457ED68>, 'courseId': 1, 'courseName': 'Account', 'enrolledStu': 1, 'dept_id': 1, '_dept_cache': <Dept: 1 Finance>, '_prefetched_objects_cache': {'students': <QuerySet [<Student: Mamta Mumbai Indian>]>}}
..
so from Query Set i would like to take only student name...
Jordan
I have follow up question for this.. when i try to edit course for html template it gets the values which are correct ,but for student its gets all attribute like studentname , city , natinality etc.. which wrong.. i want only student name from student and display it in template.
I am using following query to get course related information.
course = Course.objects.filter(pk=1).prefetch_related('students').select_related('dept').get()
getting results from query but for student it gets all attribute
Now in my views i am using this code:
course = Course.objects.filter(pk=1).prefetch_related('students').select_related('dept').get()
form = CourseForm(instance=course)
return render_to_response('stuApp/edtStudent.html', {'form': form})
i figured out how to get value for student name instead of all atributes.
course.students = course.students.value_list('stuName', flat=True)
but i am not sure how can i set above value to form and display it in template..
this where i am failing..

Working with List of numbers in Django

I have a loop of buttons, ranging from 1-100 (this varies, could be 1-50), if a user selects any of the button it populates an input field e.g 32, 47, 84, 69.
So i have a django view that saves it to the database but it is saved in the database in this format [u'32', u'47', u'84', u'69'] which i know will be regarded as one entity, but i need the numbers to be regarded as separate entity, so that any number that is in the database, wont be selected by any other user.
def game_details(request, id):
template_name = 'app_agent_games/game_details.html'
get_game_id = get_object_or_404(gamesModel, id=id)
context = {
'game_details': get_game_id,
'range': range(1, get_game_id.people + 1)
}
if request.POST:
guesses = request.POST.get('guessed_number', False)
splited = guesses.split(',')
counter = request.POST.get('count_guess', False)
update_game = PlayGameForm(request.POST)
obj = PlayGame()
obj.guessed_number = splited
obj.total_stake = int(get_game_id.amount) * int(counter)
obj.game_id = get_object_or_404(gamesModel, id=id)
obj.agent_id = get_object_or_404(CustomAgent, user_id=request.user.user_id)
obj.save()
return HttpResponseRedirect('/games/details/'+id)
return render(request, template_name, context)
The model structure:
class PlayGame(models.Model):
agent_id = models.ForeignKey(CustomAgent, related_name='agent_game')
game_id = models.ForeignKey(gamesModel, related_name='agent_game_id')
guessed_number = models.CharField(max_length=100)
total_stake = models.IntegerField(default=0)
The model field that saves the list is guessed_number
Sounds like you need a slightly more evolved data model:
class PlayGame(models.Model):
agent_id = models.ForeignKey(CustomAgent, related_name='agent_game')
game_id = models.ForeignKey(gamesModel, related_name='agent_game_id')
total_stake = models.IntegerField(default=0)
class GameNumber(models.Model):
game = models.ForeignKey(PlayGame)
number = models.IntegerField()
user = models.ForeignKey('YourUser', blank=True, null=True)
Then, for example, you create all your numbers 1-100 for your game, and if a user guessed it, set the user FK to be the user who guessed it, else keep the user FK null.