Model
class Person(models.Model):
GENDER = (
('M','Male'),
('F','Female'),
)
first_name = models.CharField("First Name", max_length=100)
last_name = models.CharField("Last Name",max_length=100)
middle_name = models.CharField("Middle Name", max_length=100, blank=True)
suffix_name = models.ManyToManyField(SuffixName, verbose_name="Suffix Name",null=True, blank=True)
gender = models.CharField(max_length=1, choices=GENDER)
department = models.ManyToManyField(Department, verbose_name="Department",null=True, blank=True)
def __unicode__(self):
return (u'%s') % (self.last_name.upper() + ', ' + self.first_name + ' ' + self.middle_name)
class Department(models.Model):
department_desc = models.CharField('Department', max_length=100,unique=True)
department_acronym = models.CharField('Department Acronym', max_length=20,blank=True,help_text="Add acronym if any, not required")
location = models.CharField('Location',max_length=100,blank=True)
localnumber = models.CharField('Local Number',max_length=30,blank=True)
active = models.BooleanField('Active',default=True)
def __unicode__(self):
return self.department_desc
How can I convert this raw SQL to a Django query?
SELECT pp.first_name, pd.department_desc, pd.localnumber
FROM person_person as pp
INNER JOIN person_person_department as ppd on pp.id = ppd.person_id
INNER JOIN person_department as pd on pd.id = ppd.department_id
Use QuerySet.values or QuerySet.values_list:
Person.objects.values('first_name', 'department__department_desc', 'department__localnumber')
# -> Returns a ValuesQuerySet that yields dictionaries when iterated.
Person.objects.values_list('first_name', 'department__department_desc', 'department__localnumber')
# -> Returns a ValuesQuerySet that yields tuples when iterated.
Fetching on Person will automatically fetch the related objects, so your query is:
people = Person.objects.all()
Then, you can do the following:
for person in people:
print('Person: {0}'.format(person.first_name))
for dept in person.department_set.all():
print('Desc: {0}, Local Number: {1}'.format(dept.department_desc,
dept.localnumber))
If you want to restrict the returned values, use values_list.
Related
I have model like this:
class Users(models.Model):
pin = models.CharField(max_length=16, unique=True)
name = models.CharField(max_length=64)
surname = models.CharField(max_length=64, blank=True, null=True)
patronymic = models.CharField(max_length=64, blank=True, null=True)
def get_full_name(self):
name = self.name
surname = self.surname
if self.surname is None:
surname = ''
if self.name is None:
name = ''
return str(name) + ' ' + str(surname)
I want to write a test case for get_full_name method.
Currently I wrote something like this:
class TestUsersModel(TestCase):
def setUp(self):
self.data1 = Users.objects.create(name='Bob', pin='1')
self.data2 = Users.objects.create(surname='Alex', pin='2')
def test_users_model_entry(self):
data = self.data1
self.assertTrue(isinstance(data, Users))
def test_users_full_name_only_name(self):
data = self.data1.get_full_name()
self.assertEquals(data,'Bob ')
def test_users_full_name_only_surname(self):
data = self.data2.get_full_name()
self.assertEquals(data,' Alex')
but when I check with coverage it says me that I have to write test case for this:
What I am missing here?
#sevdimali - Firstly, it is a bad idea to name a field as name and not first_name when you have the other field as surname. Also, if you decide to use first_name then the other field should be last_name. Consistency matters.
class Users(models.Model):
pin = models.CharField(max_length=16, unique=True)
name = models.CharField(max_length=64,null=True)#or else you cannot test #object creation without name
surname = models.CharField(max_length=64, blank=True, null=True)
patronymic = models.CharField(max_length=64, blank=True, null=True)
Now, to test your get_full_name(). How about we do something like this?
def setUp(self):
self.user = Users.objects.create(name='test', surname='world',pin='1234')
self.user_without_surname = Users.objects.create(name='test',pin='12345')
self.user_without_name=Users.objects.create(surname='world',pin='123456')
def test_get_full_name_with_valid_name_and_surname(self):
expected = str(self.user.name) + ' ' + str(self.user.surname)
actual = 'test world'
self.assertEquals(expected,actual)
def test_get_full_name_with_empty_surname(self):
expected = str(self.user.name) + ' ' +''
actual = 'test '
self.assertEquals(expected,actual)
def test_get_full_name_with_empty_name(self):
expected = '' + ' ' +str(self.user.surname)
actual = ' world'
self.assertEquals(expected,actual)
Key things to refactor-
IMO, the Model name should not be plural
Name doesn't make sense. Be explicit. first_name,middle_name,last_name if needed
Think about null columns and the consequences they might have. You don't want to have a user without even the first_name, last_name, email, and other essential fields.
I am learning django or a month now. I created a model called "Leave" to mark employees leave. Then I created a model called "Salarie".In this I need to create a field like "Total_Leave" which will show an employees leave count in a month.( In january 2020 how many leave a particular employee took) ( If i mention the "Employee_Name" as "Samantha" in salary model, it need to show samantas' leave in the particular month,year)
I tried to do it like this and tried some coding but nothing worked.
#property
def Total_Leave(self):
return self.
Can anyone explain me how to do that please?
Models.py
class Leave(models.Model):
Leave_ID = models.CharField(max_length=5, primary_key=True,default=" ")
Employee_Name = models.ForeignKey('Employee_Detail',models.DO_NOTHING)
Dept_Name = models.ForeignKey('Department', models.DO_NOTHING)
l = (
("Paid","Paid"),("Non-Paid","Non-Paid")
)
Leave_Type = models.CharField(max_length=10, choices= l, default="Non-Paid")
m = (
("January","January"),("February","February"),("March","March"),("April","April"),("May","May"),("June","June"),("July","July"),("August","August"),("September","September"),("October","October"),("November","November"),("December","december")
)
Month = models.CharField(max_length=10, choices= m)
Year = models.IntegerField(max_length=4)
Start_Date = models.DateField()
End_Date = models.DateField(null=True, blank = True)
Reason = models.CharField(max_length=200)
s = (
("Accepted","Accepted"),("Pending","Pending"),("Canceled","Canceled")
)
Status = models.CharField(max_length=10, choices= s, default="Pending")
def __str__(self):
return str(self.Employee_Name)
class Salarie(models.Model):
Salarie_ID = models.CharField(max_length=5,primary_key=True,default=" ")
Employee_Name = models.ForeignKey('Employee_Detail', models.DO_NOTHING)
Dept_Name = models.ForeignKey('Department', models.DO_NOTHING)
Basic_Salary = models.IntegerField(default=0)
Extra_Hours = models.IntegerField(default=0)
#property
def Extra_Payment(self):
return self.Extra_Hours * 350
Bonus = models.IntegerField(default=0)
#property
def Total_Payment(self):
return self.Extra_Payment + self.Basic_Salary + self.Bonus
m = (
("January","January"),("February","February"),("March","March"),("April","April"),("May","May"),("June","June"),("July","July"),("August","August"),("September","September"),("October","October"),("November","November"),("December","december")
)
Month = models.CharField(max_length=10, choices= m)
Year = models.IntegerField(max_length=4)
Status = models.BooleanField()
Paid_Date = models.DateField(null=True,blank=True)
def __str__(self):
return str(self.Employee_Name)
class Employee_Detail(models.Model):
Employee_ID = models.CharField(primary_key=True, max_length=6)
Employee_Name = models.CharField(unique=True, max_length=30)
Primary_Phone = models.IntegerField(unique=True, max_length=10)
p = (
("Manager","Manager"),("Supervisor","Supervisor"),("Employee","Employee")
)
Position = models.CharField(max_length=15, choices= p, default="Employee")
Address = models.TextField(max_length=500)
Email = models.EmailField(max_length=50, unique=True)
def __str__(self):
return str(self.Employee_Name)
You can do this by using annotate or aggregate
If you want leave count for all the employees then
from django.db.models import Count
employees = Employee_Detail.objects.filter(leave__Status='Accepted').annotate(leave_count=Count('leave'))
you can access this by <employee Obj>.leave_count
If you want leave count for all employee order_by Month then
employees = Employee_Detail.objects.filter(leave__Status='Accepted').order_by('leave__Month').annotate(leave_count=Count('leave')).values('leave_Month','leave_count')
And last you want leave count for a particular employee for a particular month than in your employee model write #property function like this
#property
def get_leave_count(self):
leaves = Employee_Detail.objects.filter(id=self.id,leave__Status='Accepted').aggregate(leave_count=Count('leave'))
return leaves['leave_count']
for month and year wise provide month and year and then
month = 'January' #For example
leaves = Employee_Detail.objects.filter(id=employee_id,leave__Month=month,leave__Status='Accepted').aggregate(leave_count=Count('leave'))
#{'leave_count':3}
year = '2020' #For example
leaves = Employee_Detail.objects.filter(id=employee_id,leave__Year=year,leave__Status='Accepted').aggregate(leave_count=Count('leave'))
#{'leave_count':13}
I tried something like this, maybe of some use.
def total_leave(emp_name,month,year):
leaves = Leave.objects.filter(Emp_name= emp_name,Month=month,Year=year)
Count=0
for leave in leaves:
if leaves.status== "Accepted":
Count +=1
return Count
Like if you only need to get leave count for the year, just use year as a parameter for function.
I'm trying to get the number of products that have each category, but each category is in turn parent of other categories, so I want to know how many children have that category and their daughter categories, I have simplified the query to the maximum in the following way, but in the django debug I keep showing that I have 66 querys duplicates.
How can I eliminate these duplications?
With the first line of views.py, he managed to get the number of products in a category, but the problem is essentially to tell him to return me from the category and his daughters.
models.py
class Categoria(models.Model):
nombre = models.CharField(max_length=200)
slug = models.SlugField(max_length=100)
padre = models.ForeignKey('self', blank=True, null=True,
related_name='cat_padre')
pub_date = models.DateTimeField('date published',
auto_now_add=True)
upd_date = models.DateTimeField('date updated', auto_now=True)
def __str__(self):
return self.nombre + ' ' + self.pais.iso
class Producto(models.Model):
nombre = models.CharField(max_length=200)
slug = models.SlugField(max_length=100)
categoria = models.ForeignKey(Categoria)
views.py
cats = Categoria.objects.annotate(num_productos=Count('producto')).filter(pais__iso=pais, padre__isnull=True).order_by('-num_productos')
for c in cats:
num_p = Producto.objects.filter(categoria__padre=c).count()
c.num_productos += num_p
contexto = {
'categorias_padre': cats,
}
return render(request, 'web/pruebitas/product.html', contexto)
Django debug:
SELECT COUNT(*) AS "__count" FROM "web_producto" INNER JOIN "web_categoria" ON ("web_producto"."categoria_id" = "web_categoria"."id") WHERE "web_categoria"."padre_id" = '790'
Duplicated 62 times.
Conexión: default
/home/luis/PycharmProjects/lco_web/web/middleware.py in __call__(29)
response = self.get_response(request)
/home/luis/PycharmProjects/lco_web/web/views.py in index(11)
return categoria(request, '', '/')
/home/luis/PycharmProjects/lco_web/web/views.py in categoria(170)
'categorias': categorias(pais, categoria.id if categoria else 0),
/home/luis/PycharmProjects/lco_web/web/views.py in categorias(29)
num_p = Producto.objects.filter(categoria__padre=c).count()
You could achieve this by using reverse lookup to child categories using cat_padre and Count with distinct=True.
cats = Categoria.objects.annotate(
num_productos=Count('cat_padre__producto', distinct=True) +
Count('producto', distinct=True)).filter(
pais__iso=pais, padre__isnull=True).order_by('-num_productos')
P.S. I have tested that on fake data so please compare what you have got earlier with what returns this query.
class Office(models.Model):
person = models.ForeignKey(Person, verbose_name="Person")
department = models.ForeignKey(Department, verbose_name="Department")
office_desc = models.CharField('Office', max_length=100,unique=True)
office_acronym = models.CharField('Office Acronym', max_length=20,blank=True,help_text="Add acronym if any, not required")
location = models.CharField('Location',max_length=100,blank=True)
trunkline = models.CharField('Trunk Line',max_length=30,blank=True)
directline = models.CharField('Direct Line',max_length=30,blank=True)
localnumber = models.CharField('Local Number',max_length=30,blank=True)
telefax = models.CharField('Telefax',max_length=30,blank=True)
active = models.BooleanField('Active',default=True)
class Department(models.Model):
department_desc = models.CharField('Department', max_length=100,unique=True)
department_acronym = models.CharField('Department Acronym', max_length=20,blank=True,help_text="Add acronym if any, not required")
active = models.BooleanField('Active',default=True)
class Person(models.Model):
GENDER = (
('M','Male'),
('F','Female'),
)
first_name = models.CharField("First Name", max_length=100)
last_name = models.CharField("Last Name",max_length=100)
middle_name = models.CharField("Middle Name", max_length=100, blank=True)
salutation = models.ForeignKey(Salutation, verbose_name="Salutation", null=True, blank=True) #
suffix_name = models.ManyToManyField(SuffixName, verbose_name="Suffix Name",null=True, blank=True) #
job_title = models.ManyToManyField(JobTitle, verbose_name="Job Title",null=True, blank=True) #
gender = models.CharField(max_length=1, choices=GENDER,default='Male')
birthdate = models.DateField('Birthdate',null=True,blank=True)
image = models.ImageField('Image',upload_to='persons',blank=True)
email = models.EmailField('Email',blank=True)
street = models.CharField('Street',max_length=100, blank=True)
brgy = models.CharField('Barangay',max_length=100, blank=True)
town_city = models.CharField('Town/City',max_length=100, blank=True)
zip_code = models.IntegerField('ZIP Code',null=True, blank=True)
department = models.ManyToManyField(Department, verbose_name="Department",null=True, blank=True) #
office = models.ManyToManyField(Office, verbose_name="Office", null=True, blank=True) #
sql_query
select pd.department_desc, pp.last_name, o.office_desc from person_person as pp
INNER JOIN person_person_department as ppd on pp.id = ppd.person_id
INNER JOIN person_department as pd on pd.id = ppd.id
INNER JOIN person_office as o on o.department_id = pd.id
where pd.department_desc = 'Executive'
views code:
per = Person.objects
qry_name = per.values_list('salutation__salutation_desc','first_name','middle_name','last_name', 'office__office_desc', 'office__location','office__localnumber','office__trunkline','office__directline','office__telefax').filter(department__department_desc='Executive')
Result: query result includes person with different department
Expected result: only person with Executive department
If I query directly from the database I get the correct result but when I translate the query into django code it's returning different queryset. Query returns ok if certain person has a single office but if a person has multiple office thats where the inconsistency starts .Query returns list with different description other than what was specified. Am I doing the translation of the query to python right?
A closer representation of the sql query would be:
from django.db.models import F
qry_name = Person.objects.filter(
department__department_desc='Executive',
office__department=F('department')
).values_list(<..FIELDS..>)
Let know if this works.
I have some things I need to use custom SQL for. Just to start, I don't want to get into the discussion of why we're using custom SQL. That's old and irrelevant here.
Anyway, with this custom SQL, I'm populating the model and attempting to populate a related/sub model. That all seems to go fine, until that related model is accessed within a view or template. If I do that, django initiates another SQL query, which is totally unnecessary as I already got that data out with the first query.
Here are example, trimmed up models:
class Preferredname(models.Model):
preferredname_id = models.CharField(primary_key=True, max_length=1)
name = models.CharField(max_length=255)
class PersonCustom(models.Manager):
def getSpecial(self):
from django.db import connection, transaction
curse = DictCursor(connection.cursor())
curse.execute("SELECT * FROM person WHERE special = 't'")
result_list = []
for row in curse.fetchall():
i = self.model(
firstname = row['firstname'],
middlename = row['middlename'],
nickname = row['nickname'],
lastname = row['lastname'],
suffix = row['suffix'],
)
i.preferredname = Preferredname()
i.preferredname.preferredname_id = row['preferredname_id']
result_list.append(i)
return result_list
class Person(models.Model):
person_id = models.IntegerField(primary_key=True)
firstname = models.CharField(max_length=255, blank=True)
middlename = models.CharField(max_length=255, blank=True)
lastname = models.CharField(max_length=255, blank=True)
nickname = models.CharField(max_length=255, blank=True)
suffix = models.CharField(max_length=255, blank=True)
preferredname = models.ForeignKey(Preferredname)
objects = PersonCustom()
def get_preferred_name(self):
try:
if self.preferredname.preferredname_id == 'N':
pref = self.nickname
elif self.preferredname.preferredname_id == 'M':
pref = self.middlename
else:
pref = self.firstname
except ObjectDoesNotExist:
if self._preferred_name == 'N':
pref = self.nickname
elif self._preferred_name == 'M':
pref = self.middlename
else:
pref = self.firstname
name = "%s %s %s" % (pref, self.lastname, self.suffix)
return name.strip()
def set_preferred_name(self, val):
self._preferred_name = val
preferred_name = property(get_preferred_name, set_preferred_name)
As an example:
>>> p = Person.objects.getSpecial()
>>> p[0].preferred_name
Since the get_preferred_name() method accesses self.preferredname.preferredname_id(which in theory, should already be populated) it makes another query for preferredname.
Am I off base here, or should this work as I intend? If I'm off here, is there a way to populate related models from within a custom manager?
Rather than assigning your object to i.preferredname, try setting it to i._preferredname_cache, which is the internal object Django uses to cache foreign key lookups. That should work.