Saving data to Django database with Views - django

I need help with one thing in my django project.
I have class games_renderer in my views.py which is connected to the path in URLs. I need this class to take three values and then store them in the SQL database in Django in the created UserGameScore model. I tried to do this using model.object.score but the application reports an error. I think the problem is that the application does not know which line to assign the value to, but I do not know where I can determine this value, using the value player_id. The Game_id value is used to specify in which column the value is to be written.
views.py
def games_renderer(request, game_id, player_id, score):
if game_id == 1:
UserGameScore.objects.create(game1_score = score)
elif game_id ==2:
UserGameScore.objects.create(game2_score = score)
elif game_id ==3:
UserGameScore.objects.create(game3_score = score)
else:
return render(request, 'result.html',{'game_id' : game_id, 'player_id' : player_id, "score" : score} )
models.py
class UserGameScore(models.Model):
user_rec = models.ForeignKey(User, on_delete=models.CASCADE)
game1_score = models.IntegerField(null=True, blank=True)
game2_score = models.IntegerField(null=True, blank=True)
game3_score = models.IntegerField(null=True, blank=True)

You can try this.
def games_renderer(request, game_id, player_id, score):
game_score = UserGameScore()
if game_id === 1:
game_score.game1_score = score
if game_id === 2:
game_score.game2_score = score
if game_id === 3:
game_score.game3_score = score
game_score.save()
You can also change your model to simplify your view.
Here is a small suggestion.
class UserGameScore(models.Model):
GAME_CHOICES = [
(ONE, '1'),
(TWO, '2'),
(THREE, '3'),
]
user_rec = models.ForeignKey(User, on_delete=models.CASCADE)
score = models.IntegerField(null=True, blank=True)
game_type = models.CharField(
max_length=2,
choices=GAME_CHOICES,
default=ONE,
)
Your view should look similar to this:
def games_renderer(request, game_id, player_id, score):
game_score = UserGameScore(user_rec=player_id, score=score, game_type=game_id )

Related

Django testing doesn't recognize dirty_fields?

One of my models uses dirty_fields. The save method detects when the field scale_mode changes. Once this happens, it goes through all the related grade objects and changes the field score for each affected grade. The goal is if VSB is swapped with VSBPLUS, then APP is swapped with APP+.
model:
class GradeBookSetup(DirtyFieldsMixin, models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=CASCADE)
VSB = 'VSB'
VSBPLUS = 'VSBPLUS'
SCALEMODE = [
('VSB', 'BEG DEV APP EXT'),
('VSBPLUS', 'BEG DEV APP APP+ EXT'),
]
scale_mode = models.CharField(
max_length=7, choices=SCALEMODE, blank=True, default=VSB)
def save(self, *args, **kwargs):
super(GradeBookSetup, self).save(*args, **kwargs)
if self.is_dirty():
dirty_fields = self.get_dirty_fields()
if 'scale_mode' in dirty_fields:
if self.scale_mode == "VSB":
n = 0
objs = Grade.objects.filter(user=self.user)
for grade in objs:
if grade.score == "APP+":
objs[n].score = "APP"
n = n + 1
Grade.objects.bulk_update(objs, ['score'])
elif self.scale_mode == "VSBPLUS":
objs = Grade.objects.filter(user=self.user)
for grade in objs:
if grade.score == "APP":
objs[n].score = "APP+"
Grade.objects.bulk_update(objs, ['score'])
class Grade(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
INCOMPLETE = 'I'
BEGINNING = 'BEG'
DEVELOPING = 'DEV'
APPLYING = 'APP'
APPLYINGPLUS = 'APP+'
EXTENDING = 'EXT'
score = models.CharField(
max_length=4, blank=True, default=INCOMPLETE)
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
assessment = models.ForeignKey(
Assessment, on_delete=models.CASCADE, null=True, blank=True)
objective = models.ForeignKey(
Objective, on_delete=models.CASCADE, blank=True)
student = models.ForeignKey(Student, on_delete=models.CASCADE)
cblock = models.ForeignKey(Classroom, on_delete=models.CASCADE, default=1)
I am writing tests for this model. Each of the test below passes except for the last test, test_scale_mode_change.
class GradeBookSetupTest(TestCase):
#classmethod
def setUpTestData(cls):
cls.user = get_user_model().objects.create_user(
username='tester',
email='tester#email.com',
password='tester123'
)
cls.gbsetup = GradeBookSetup(
user=cls.user,
scale_mode="VSBPLUS",
)
cls.gbsetup.save()
cls.student = Student.objects.create(
user=student_user,
student_first="Jim",
student_last="Smith",
nickname="Jim S",
fullname="Jim Smith",
student_number=992342,
email="992342#learn.vsb.bc.ca"
)
cls.course = Course.objects.create(
user=cls.user,
course_name="Math8",
grade_level='ELEM',
)
cls.classroom = Classroom(
user=cls.user,
classroom_name='Block 1',
course=cls.course,
)
cls.classroom.students.add(cls.student)
cls.classroom.save()
cls.objective = Objective.objects.create(
user=cls.user,
objective_name='factor.1',
mode='LA',
course=cls.course,
description="Test objective",
)
cls.assessment = Assessment(
user=cls.user,
assessment_name="Quiz1",
course=cls.course,
)
cls.assessment.objectives.add(cls.objective)
cls.assessment.save()
cls.grade = Grade.objects.create(
user=cls.user,
score='APP+',
student=cls.student,
cblock=cls.classroom,
assessment=cls.assessment,
objective=cls.objective
)
def test_gradebooksetup_creation(self):
self.assertEqual(self.gbsetup.scale_mode, "VSBPLUS")
def test_grade_creation(self):
self.assertEqual(self.grade.score, 'APP+')
self.assertNotEqual(self.grade.score, 'DEV')
def test_scale_mode_change(self):
self.gbsetup.scale_mode = 'VSB'
self.gbsetup.save()
print(self.gbsetup.scale_mode)
self.assertEqual(self.grade.score, 'APP')
What I expect to happen is that `self.gbsetup.scale_mode = 'VSB' should trigger the dirty_field in the save method. This should then change the APP+ score to APP. The print statement confirms that gbsetup == VSB. In practice, my app does perform this function correctly.
What happens though is:
self.assertEqual(self.grade.score, 'APP')
AssertionError: 'APP+' != 'APP'
I didn't show all the related models for Grade and GradeBookSetup, I don't think they are relevant to this problem. I can add these if required.
You might need to regrab the grade from the database as the change is made in the DB but the reference instance is still the one created in the setup.
def test_scale_mode_change(self):
self.gbsetup.scale_mode = 'VSB'
self.gbsetup.save()
print(self.gbsetup.scale_mode)
#we have made indirect changes to grade via the gbsetup.save() function that we need to check
self.grade.refresh_from_db()
self.assertEqual(self.grade.score, 'APP')

How to import a object of another model (A) inside model (B) in Django?

I want to create a new object in ModelB when specific condition are met in ModelA. I am new to Django so that I am unable to figure out how exactly I can achieve this.
For example I have two models(Product and ProductVariant), when specific condition on ProductVariant is met then I want to calculate new object value in Product model.
My Product model is like this:
PRODUCT_TYPE = (
('s', 'simple'),
('v', 'varaible')
)
class Products(models.Model):
name = models.CharField(max_length=250,null=True, blank=True,)
slug = models.SlugField(max_length=200, unique=True,null=True)
short_description = HTMLField()
description = HTMLField()
category = models.ForeignKey(Categories, related_name="products",on_delete=models.SET_NULL,null=True,blank=True,)
brand = models.ForeignKey(Brands,on_delete=models.CASCADE, default=None, null=True, blank=True,)
warranty_support = HTMLField()
product_type = models.CharField(choices=PRODUCT_TYPE, default='simple', max_length=50)
And my Product Attribute Model is like this:
class ProductVariant(models.Model):
product = models.ForeignKey(Products,on_delete=models.CASCADE)
variant = models.ForeignKey(ProductAttribute,on_delete=models.CASCADE, null = True, default=None)
managed_stock = models.IntegerField(choices=STOCK_MANAGED, default=0)
stock = models.IntegerField(default=None)
stock_threshold = models.IntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
sku = models.CharField(max_length= 250, default=None)
sale_price = models.DecimalField(max_digits=10, decimal_places=2)
sale_start_date=models.DateField(auto_now_add=False, auto_now=False, default=None)
sale_end_date=models.DateField(auto_now_add=False, auto_now=False,default=None)
I am trying to create regular_price and sale_price on Product model if product_type is variable and if sale_end_date is greater than today. I want to set the price from the variant which has the lowest price.
I tried doing like this on Product model:
def clean(self):
if self.product_type == 'varaible' and ProductVariant.objects.filter(product=self, variant_count__gt = 1):
self.min_price = ProductVariant.objects.filter(product=self).Min('price')
self.max_price = ProductVariant.objects.filter(product=self).Max('price')
but I am not able to achieve what I want,
How can I do this?
After some research and analysis I found solution to my problem, I am posting the solution here so that someone with similar problem could be benefited.
#property
def get_price(self):
result = dict()
variants = ProductVariant.objects.filter(product=self)
count = variants.count()
if count > 1:
min_variant = variants.order_by('price').first()
max_variant = variants.order_by('-price').first()
result['min_price'] = min_variant.price
result['max_price'] = max_variant.price
elif count == 1:
variant = variants.first()
if variant.sale_price:
result['price'] = variant.price
result['sale_price'] = variant.sale_price
sale_variant = variants.order_by('sale_price').first()
result['lowest_sale_price'] = sale_variant.sale_price
result['regular_price'] = sale_variant.price
today = datetime.date.today()
if variant.sale_start_date <= today and variant.sale_end_date >= today:
result['sale_end_date'] = variant.sale_end_date
else:
result['price'] = variant.price

Django ORM Count Without Duplicate Column

I have a table called Reading History. I want to find the average number of reads in this table. I wrote the following method for this, but I can't get the right result. Records are made in the table once (student-book records with the same values). When the same record comes, the counter value is increased by one.
For example, suppose that two different students read two different books. I expect total reads / 2 but the result I get is total reads / 4 because there are 4 rows in the table. How can I calculate this? For example, if a student reads 4 different books once, the average will be 1, but the average should be 4.
I tried to use distinct and values but I couldn't get the result I wanted. Maybe I didn't manage to use it correctly. Also I tried to use Avg. When avg didn't give the correct result, I tried to break it down and get the values myself. Normally, Avg was written in Sum.
Serializer
class ClassReadingHistoryReportSerializer(ModelSerializer):
instructor = InstructorForClassSerializer(source="instructor.user")
students = StudenListReadingHistoryReportSerializer(many=True,
source="student_list_class")
avg_read_book = serializers.SerializerMethodField()
class Meta:
model = Class
exclude = [
"created_at",
"updated_at",
"school",
]
def get_avg_read_book(self, obj):
sum_read_book = Class.objects.filter(id = obj.id).aggregate(sum_read=Sum('student_list_class__child__child_reading_history__counter'))
count_child_record = Class.objects.filter(id = obj.id).aggregate(count_child=Count('student_list_class__child__child_reading_history'))
return None
Models
class ChildProfile(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
primary_key=True,
verbose_name=AccountStrings.ChildProfileStrings.user_verbose_name,
related_name="user_child")
city = models.ForeignKey(
"country.City",
on_delete=models.SET_NULL,
null=True,
blank=True,
verbose_name=AccountStrings.ChildProfileStrings.city_verbose_name,
related_name="city_child_profiles")
hobbies = models.CharField(
max_length=500,
null=True,
blank=True,
verbose_name=AccountStrings.ChildProfileStrings.hobbies_verbose_name)
class Meta:
verbose_name = AccountStrings.ChildProfileStrings.meta_verbose_name
verbose_name_plural = AccountStrings.ChildProfileStrings.meta_verbose_name_plural
#property
def get_full_name(self):
return f"{self.user.first_name} {self.user.last_name}"
def __str__(self):
return self.get_full_name
def clean(self) -> None:
"""
This method will check if the user type is a child during creation.
"""
if self.user.user_type != 2:
raise ValidationError(AccountStrings.ChildProfileStrings.user_type_error)
class School(AbstractSchoolBaseModel):
city = models.ForeignKey(
"country.City",
on_delete=models.DO_NOTHING,
related_name="city_schools",
verbose_name=SchoolStrings.SchoolStrings.city_verbose_name)
name = models.CharField(
max_length=250,
verbose_name=SchoolStrings.SchoolStrings.name_verbose_name)
address = models.CharField(
max_length=250,
verbose_name=SchoolStrings.SchoolStrings.address_verbose_name)
website = models.CharField(
max_length=250,
verbose_name=SchoolStrings.SchoolStrings.website_verbose_name)
class Meta:
verbose_name = SchoolStrings.SchoolStrings.meta_verbose_name
verbose_name_plural = SchoolStrings.SchoolStrings.meta_verbose_name_plural
def __str__(self):
return self.name
class Class(AbstractSchoolBaseModel):
school = models.ForeignKey(
"school.School",
on_delete=models.CASCADE,
related_name="classes_school",
verbose_name=SchoolStrings.ClassStrings.school_verbose_name)
instructor = models.ForeignKey(
"account.InstructorProfile",
on_delete=models.CASCADE,
related_name="instructors_school",
verbose_name=SchoolStrings.ClassStrings.instructor_verbose_name)
name = models.CharField(
max_length=50,
verbose_name=SchoolStrings.ClassStrings.name_verbose_name)
grade = models.IntegerField(
verbose_name=SchoolStrings.ClassStrings.grade_verbose_name)
class Meta:
verbose_name = SchoolStrings.ClassStrings.meta_verbose_name
verbose_name_plural = SchoolStrings.ClassStrings.meta_verbose_name_plural
ordering = ["name", "grade"]
def __str__(self):
return f"{self.school.name} - {self.name} - Grade: {self.grade}"
def clean(self) -> None:
"""
This method checks whether the teacher trying to be assigned to the class is working in that school.
"""
if self.instructor.school != self.school:
raise ValidationError(SchoolStrings.ClassStrings.instructor_not_working_at_this_school_error)
class StudentList(AbstractSchoolBaseModel):
school_class = models.ForeignKey(
"school.Class",
on_delete=models.CASCADE,
related_name="student_list_class",
verbose_name=SchoolStrings.StudentListStrings.school_class_verbose_name
)
child = models.ForeignKey(
"account.ChildProfile",
on_delete=models.CASCADE,
related_name="student_list_children",
verbose_name=SchoolStrings.StudentListStrings.child_verbose_name)
class Meta:
verbose_name = SchoolStrings.StudentListStrings.meta_verbose_name
verbose_name_plural = SchoolStrings.StudentListStrings.meta_verbose_name_plural
def __str__(self):
return f"{self.school_class.name} : {self.child.user.first_name} {self.child.user.last_name}"
def clean(self) -> None:
result = StudentList.objects.filter(school_class = self.school_class, child = self.child)
if not self.pk and result.exists():
raise ValidationError(SchoolStrings.StudentListStrings.child_already_added_to_this_class_error)
class ReadingHistory(AbstractBookBaseModel):
IS_FINISHED = ((False,
BookStrings.ReadingHistoryStrings.is_finished_false),
(True, BookStrings.ReadingHistoryStrings.is_finished_true))
book = models.ForeignKey(
"book.Book",
on_delete=models.CASCADE,
related_name="book_reading_history",
verbose_name=BookStrings.ReadingHistoryStrings.book_verbose_name)
child = models.ForeignKey(
"account.ChildProfile",
on_delete=models.CASCADE,
related_name="child_reading_history",
verbose_name=BookStrings.ReadingHistoryStrings.child_verbose_name)
is_finished = models.BooleanField(
choices=IS_FINISHED,
verbose_name=BookStrings.ReadingHistoryStrings.is_finished_verbose_name
)
counter = models.PositiveIntegerField(
verbose_name=BookStrings.ReadingHistoryStrings.counter_verbose_name,
default=0)
class Meta:
verbose_name = BookStrings.ReadingHistoryStrings.meta_verbose_name
verbose_name_plural = BookStrings.ReadingHistoryStrings.meta_verbose_name_plural
def __str__(self):
return f"{self.child.user.first_name} {self.child.user.last_name} - \"{self.book.name}\" "
def clean(self) -> None:
result = ReadingHistory.objects.filter(child=self.child,
book=self.book)
if not self.pk and result.exists():
raise ValidationError(
BookStrings.ReadingHistoryStrings.exists_error)
I forgot to add the counter column in the BookReadingHistory table to the diagram. Diagram and Table data are shown in the images below.
Table
Diagram

Django | Decimal Field is required even tho i set it to NULL=True

im trying to play a little bit around with django but i have run into problems...
I have a Decimal Field which is not required so i set it to "blank=True" and "null=True". But it still says its required :(
I also did all the migrations.
Here is my models.py
from django.db import models
weightUnit = {
('kg' , 'kilogram'),
('g', 'gram'),
('t', 'tons'),
('n', '-'),
}
class Product(models.Model):
pname = models.CharField(
max_length=50,
)
pdesc = models.TextField(
max_length=5000,
)
pprice = models.DecimalField(
max_digits=6,
decimal_places=2,
)
psn = models.CharField(
max_length = 30,
null=True,
blank=True,
)
pweightunit = models.CharField(
choices=weightUnit,
default='n',
null=True,
blank=True,
max_length=5,
)
pweight = models.DecimalField(
null=True,
blank = True,
max_digits=10000,
decimal_places=2,
)
plimage = models.ImageField(
blank=True,
null=True,
)
Here is my forms.py
from django import forms
from .models import weightUnit
class RawProductForm(forms.Form):
name = forms.CharField(label="Name")
desc = forms.CharField(label="Beschreibung")
price = forms.DecimalField(label="Stückpreis")
sn = forms.CharField(label="Seriennummer")
weightunit = forms.ChoiceField(choices=weightUnit, label="Gewichteinheit")
weight = forms.DecimalField(label="Gewicht")
image = forms.ImageField(label="Bild")
Here is my views.py
def product_add(request):
pf = RawProductForm()
if request.method == "POST":
pf = RawProductForm(request.POST)
if pf.is_valid():
print(pf.cleaned_data)
Product.objects.create(**pf.cleaned_data)
else:
print(pf.errors)
context = {
"productform" : pf,
}
return render(request, "product_add.html", context)
You are working with a simple Form, not a ModelForm [Django-doc], so that means that it will not inspect the model at all. It will simply render a form. A ModelForm will inspect the model and construct a form based on that that you can customize.
class RawProductForm(forms.ModelForm):
class Meta:
model = Product
labels = {
'name': 'Name',
'desc': 'Beschreibung',
'price': 'Stückpreis',
'sn': 'Seriennummer',
'weightunit': 'Gewichteinheit',
'weight': 'Gewicht',
'image': 'Bild',
}
A ModelForm also has a .save(…) method [Django-doc] which creates a model object based on the data in the form and saves it to the database.

Saving many-to-many fields from the excel file in Django

I'm trying to save the student data from an excel file. I'm reading the excel file row-wise and mapping the data to the model fields. Now the problem is that there is a foreign key and a many-to-many field which I don't know how to save. Though I figured out the foreign key part but not able to solve the second part.
Here are the files.
views.py
def fileUpload(request):
if request.method=="POST":
form= UserDataUploadView(request.POST, request.FILES)
try:
excel_file= request.FILES["excel_file"]
except MultiValueDictKeyError: # In case the user uploads nothing
return redirect('failure_page')
# Checking the extension of the file
if str(excel_file).endswith('.xls'):
data= xls_get(excel_file, column_limit=10)
elif str(excel_file).endswith('.xlsx'):
data= xlsx_get(excel_file, column_limit=10)
else:
return redirect('failure_page')
studentData= data["Sheet1"]
print("Real Data", studentData)
# reading the sheet row-wise
a_list= studentData
list_iterator= iter(a_list)
next(list_iterator)
for detail in list_iterator:
# To find out empty cells
for data in detail:
if data==" ":
print('A field is empty')
return redirect('user_upload')
print("DATA: ", detail)
user=User.objects.create(
firstName = detail[6],
lastName = detail[7],
password = detail[8],
username = detail[9],
)
# instance=user.save(commit=false)
# Student.batch.add(detail[0])
student=Student.objects.create(
user = user,
email = detail[1],
rs_id = detail[2],
dob = detail[3],
address = detail[4],
age = detail[5],
)
student.save()
return render(request, 'classroom/admin/success_page.html', {'excel_data':studentData})
# iterating over the rows and
# getting value from each cell in row
# for row in worksheet.iter_rows():
# row_data= list()
# for cell in row:
# row_data.append(str(cell.value))
# excel_data.append(row_data)
# return render(request, 'classroom/admin/excel.html', {'excel_data':excel_data})
else:
form=UserDataUploadView()
return render(request, 'classroom/admin/fill_users.html', {
'form':form,
# 'excel_data':excel_data,
})
models.py
class Subject(models.Model):
school = models.CharField(max_length=50, null=True)
name = models.CharField(max_length=30)
color = models.CharField(max_length=7, default='#007bff')
def __str__(self):
return self.name
def get_html_badge(self):
name = escape(self.name)
color = escape(self.color)
html = '<span class="badge badge-primary" style="background-color: %s">%s</span>' % (color, name)
return mark_safe(html)
class Batch(models.Model):
name = models.CharField(max_length=30, unique=True)
school = models.CharField(max_length=50)
amount_of_fees = models.IntegerField(null=True)
subjects = models.ManyToManyField(Subject)
#property
def students(self):
return self.student_set.all()
def __str__(self):
return self.name
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True, default=None)
batch = models.ManyToManyField(Batch)
email = models.EmailField(null=True)
phone_number = models.CharField(max_length=10, null=True)
dob = models.DateField(blank=True, null=True, help_text="Enter in the following format : YYYY-MM-DD")
address = models.TextField(max_length=150, null=True)
age = models.IntegerField(blank=True)
image = models.ImageField(upload_to='profile_pictures', default='student_image.png', blank=True)
rs_id = models.IntegerField(blank=True,default=0)
I don't know how to put the data for batch in the excel sheet. Kindly give insight for that too.
Assuming detail[0] is the name field for the Batch model, you would do:
student_batch = Batch.objects.get(name=detail[0])
student=Student.objects.create(
user = user,
email = detail[1],
rs_id = detail[2],
dob = detail[3],
address = detail[4],
age = detail[5],
)
student.batch.add(student_batch)
student.save()
You will also need to update your Batch field on the Student model to:
batch = models.ManyToManyField(Batch, blank=True)