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.
Related
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.
I am using a form to retrieve a TextField. I then parse each line of the textfield to create students and save a Student object. Finally I add students to a Classroom object. This worked when student was a foreignkey to classroom. I changed this relationship to a ManyToMany relationship and now I get the error: Cannot add "<Student: name>": the value for field "student" is None.
Models
class Student(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, primary_key=True)
student_first = models.CharField(max_length=30)
student_last = models.CharField(max_length=30)
nickname = models.CharField(max_length=31)
fullname = models.CharField(max_length=60)
attend = models.BooleanField(default=True)
do_not_pick = models.BooleanField(default=False)
student_number = models.IntegerField()
email = models.EmailField(max_length=50)
class Classroom(models.Model):
"""The gradebook is split into courses, classes and students"""
classroom_name = models.CharField(max_length=10)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
students = models.ManyToManyField(Student)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
View
def addmultistudent(request, classroom_id):
"""Add multiple students at once."""
classblock = get_object_or_404(Classroom, pk=classroom_id)
context = {'classblock': classblock}
if request.method == 'POST':
form = StudentInputForm(request.POST)
if form.is_valid():
s = form.save()
input_list = []
input_list = s.name_list.split('\n')
# the for returns a list of students with student number, fisrt_name, last_name in each line
for line in input_list:
# remove the carriage return
line = line.strip('\r')
# removes double quotes
line = line.translate({ord(c): None for c in '"'})
# ignore the first line which is a header starting with "Pupil"
if not line.startswith("Pupil"):
# get rid of the commas from the csv file
all_names = line.split(",")
# pop the studnet number
sn = all_names.pop(0)
# make sure the student not already been added
if not Student.objects.filter(student_number=sn).exists():
first = all_names.pop(0)
last = ""
for name in all_names:
last = last + name + " "
last = last[:-1]
# create the object and then add the attributes
email_add = sn + "#bc.ca"
nick = first + last[0]
full = first + " " + last
new_student = Student(student_last=last, student_first=first,
student_number=sn, nickname=nick, fullname=full, email=email_add)
print(new_student)
print(classblock)
new_student.save()
classblock.students.add(new_student)
# attach student to a user
if not User.objects.filter(username=sn):
user = User.objects.create_user(sn, email_add)
user.last_name = last
user.first_name = first
user.save()
# if the student has been added, we just need to add them to the new classroom
else:
new_student = Student.objects.get(student_number=sn)
classblock.students.add(new_student)
form = StudentInputForm(None)
context['form'] = form
return render(request, "gradebook/addmultistudent.html", context)
else:
context['form'] = form
return render(request, "gradebook/addmultistudent.html", context)
The print(new_student) and print(classblock) print the expected objects in the terminal, and later when I go to the admin page I can see that they exist. If I then go into the django shell, I can type the commands:
c1 = Classroom.objects.all().first()
s1 = Student.objects.all().first()
c1.students.add(s1)
And the student s1 does get added to the classroom. FWIW I'm testing this with only one student and classroom object so far, so s1 and c1 correspond to objects referred to in my error.
Is this a timing error, where I can't add an object to a m2m relationship immediately after saving it?
Need to save user before saving student
Student has a user OneToOne field. If I save the user object first, the new_student.save() works.
# attach student to a user
if not User.objects.filter(username=sn):
user = User.objects.create_user(sn, email_add)
user.last_name = last
user.first_name = first
user.save()
new_student.save()
classblock.students.add(new_student)
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.
I know this is probably very basic stuff but I'm really having a tough time figuring it out. I have an app that stores sports forecasts for users, so they input the home team, it's score and the visiting team with it's score. The idea is for the app to determine whether the result was a home win, a tie or an away win by comparing the scores on both input boxes.
This is my view:
def inicio(request):
if request.method == "POST":
form = Pronosticos(request.POST)
if form.is_valid():
pronostico = PartidosUsuarios.objects.get_or_create(idUsuario=request.user, idPartido=request.POST.get("idPartido", ""), PaisL=request.POST.get("PaisL", ""), Local=request.POST.get("Local", ""), Visita=request.POST.get("Visita", ""), PaisV=request.POST.get("PaisV", ""), Capturado="Si")
if pronostico.Local > pronostico.Visita:
pronostico.Resultado = "Local"
elif pronostico.Visita > pronostico.Local:
pronostico.Resultado = "Visita"
elif pronostico.Local == pronostico.Visita:
pronostico.Resultado = "Empate"
return render(request, "brasil/inicio.html")
partidos_fifa = PartidosFifa.objects.order_by("Partido")[:64]
context = {"partidos_fifa": partidos_fifa}
return render(request, "brasil/inicio.html", context)
The form:
class Pronosticos(ModelForm):
class Meta:
model = PartidosUsuarios
fields = ["idPartido", "PaisL", "Local", "Visita", "PaisV"]
and the model:
class PartidosUsuarios(models.Model):
idUsuario = models.OneToOneField(User)
idPartido = models.CharField(max_length=20)
PaisL = models.CharField(max_length=250)
Local = models.IntegerField(max_length=11, default=0)
Visita = models.IntegerField(max_length=11, default=0)
PaisV = models.CharField(max_length=250)
Resultado = models.CharField(max_length=250)
Puntos = models.IntegerField(max_length=11, default=0)
Capturado = models.CharField(max_length=10, default="No")
def __unicode__(self):
return unicode(self.idPartido)
The field in question aka where i want the result to be stored is called "Resultado"
Everything works as it should except for this part where i've been getting this error:
AttributeError at /inicio/
'tuple' object has no attribute 'Local'
It's complaining about this line:
if pronostico.Local > pronostico.Visita:
Any help will be greatly appreciated, thanks!
The problem here is the return value from get_or_create(). From the documentation:
Returns a tuple of (object, created), where object is the retrieved or created object and created is a boolean specifying whether a new object was created.
So try this instead:
pronostico, _ = PartidosUsuarios.objects.get_or_create(...)
class EmployeeRating(models.Model):
rating_1 = models.IntegerField(default = 0)
rating_2 = models.IntegerField(default = 0)
rating_3 = models.IntegerField(default = 0)
rating_4 = models.IntegerField(default = 0)
rating_4 = models.IntegerField(default = 0)
total = models.IntegerField(default = 0)
Using a Model Form to take the values, automatically assign values to 'total'.
1. Don't repeat yourself
It seems like a bad idea to have a field in your model containing the total of some other fields in the same model. It would be all too easy to update one and not the other, leaving the fields inconsistent.
So my suggestion is to drop the total field and compute it when you need it.
You can add a method to the EmployeeRating model that computes it in Python:
class EmployeeRating(models.Model):
# ...
#property
def total_rating(self):
return self.rating_1 + self.rating_2 + self.rating_3 + self.rating_4
And if you need to query on the total, you could use extra():
EmployeeRating.objects.extra(
where = ['rating_1 + rating_2 + rating_3 + rating_4 > 10'])
2. Normalize!
The reason why this is awkward is that your model is not fully normalized. If you have multiple ratings attached to an EmployeeRating record, the natural way to implement them is to have a separate table. If you did this, your model would look like this:
class EmployeeRating(models.Model):
# ... other fields, e.g. foreign key to Employee model.
class Rating(models.Model):
employee_rating = models.ForeignKey(EmployeeRating, related_name='ratings')
rating = models.IntegerField()
# ... other fields, e.g. who performed the evaluation, or when it was done.
and then you can use annotate() when you need to get the total:
from django.db.models import Sum
EmployeeRating.objects.annotate(total_rating = Sum('ratings__rating'))
class EmployeeRatingForm(forms.ModelForm):
class Meta:
model = EmployeeRating
def __init__(self, *args, **kwargs):
super(EmployeeRatingForm, self).__init__(*args, **kwargs)
self.fields['total'].widget = forms.HiddenInput()
def save(self, commit=True):
rating = super(EmployeeRatingForm, self).save(commit=False)
rating1 = self.cleaned_data['rating_1']
rating2 = self.cleaned_data['rating_2']
rating3 = self.cleaned_data['rating_3']
rating4 = self.cleaned_data['rating_4']
rating.total = rating1 + rating2 + rating3 + rating4
rating.save()