Optimization of querys in django eliminating querys duplicates - django

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.

Related

Saving data to Django database with Views

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 )

get unique values from query_set Django

I'm trying to get unique results from s query_set.
Example.
ID | NOMBRE | CLASIFICACION
1 Escoba Limpieza
2 Trapeador Limpieza
3 Tornillo Herramienta
I want to get "Limpieza and Herramienta" only. currently I'm getting "Limpieza, Limpieza and Herramineta"
my views.py:
class ItemListView(ListView):
model= Items
def get_context_data(self,**kwargs):
context = super(ItemListView, self).get_context_data(**kwargs)
context['clasificacion'] =Items.objects.order_by('nombre').distinct('clasificacion')
return context
my models.py
from django.db import models
# Create your models here.
class Items(models.Model):
nombre = models.CharField(max_length=250)
descripcion = models.CharField(max_length=250)
codigo_proveedor = models.CharField(max_length=250)
clasificacion = models.CharField(max_length=250)
c_minima = models.IntegerField()
c_actual = models.IntegerField()
proveedor = models.ForeignKey('Proveedores',on_delete=models.CASCADE)
active = models.BooleanField()
def __str__(self):
return self.nombre + ' ----- ' +
self.clasificacion + ' ----- ' +
str(self.c_actual)
class Proveedores(models.Model):
nombre = models.CharField(max_length=250)
telefono = models.CharField(max_length=250)
direccion1 = models.CharField(max_length=250)
direccion2 = models.CharField(max_length=250, null=True)
active = models.BooleanField()
def __str__(self):
return self.nombre
Thanks for the help!
Will's response is correct, but I think you want to do the order_by() operation first to ensure you are getting the correct results.
context['clasificacion'] =(Items
.objects
.order_by('clasification', 'nombre')
.distinct('clasificacion'))
context['clasificacion'] = Items.objects.order_by('clasificacion', 'nombre').distinct('clasificacion')
distinct() must match the leftmost order_by(). So making the column you use in distinct() the first column in the order_by() makes it work.

Django ORM - filter with another table like LEFT JOIN adding a column

Well i got a problem with Django i want to acces to all the "Reaccion" but add a column (like in the image) if exists a relationship with "ReaccionPost" including "id_post" ,'status', and id_reaccion, but if it's not exist, in the colum should be a 0 por '' and if exists should be 1 or another true statement,
Thank you for reading my issue.
https://drive.google.com/file/d/0B3suBvZgwQ0JVmtxb1pzbmxGZW8/view?usp=sharing
class Reaccion(models.Model):
nombre = models.CharField(max_length=40)
descripcion = models.CharField(max_length=300)
icono = models.CharField(max_length=300)
def __unicode__(self):
return self.nombre
class Post(models.Model):
usuario= models.ForeignKey(User)
tipo= models.ForeignKey(Tipo)
creacion = models.DateTimeField(auto_now_add=True)
status = models.ForeignKey(Estado,default=1)
def __unicode__(self):
return self.titulo
class ReaccionPost(models.Model):
ESTADO = (
(1, 'Activo'),
(2, 'Inactivo'),
)
id_post= models.ForeignKey(Post)
usuario = models.ForeignKey(User)
id_reaccion = models.ForeignKey(Reaccion)
creacion = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=ESTADO,default=1)
def __unicode__(self):
return str(self.usuario) +" "+ str(self.id_post) +" "+ str(self.id_reaccion)

Converting raw SQL to Django QuerySet API

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.

Subtract models.DateField to get number of days

I have a simple model that tracks work leave requests:
class LeaveRequest(models.Model):
employee = models.ForeignKey(UserProfile)
supervisor = models.ForeignKey(UserProfile, related_name='+', blank=False, null=False)
submit_date = models.DateField(("Date"), default=datetime.date.today)
leave_type = models.CharField(max_length=64, choices=TYPE_CHOICES)
start_date = models.DateField(("Date"))
return_date = models.DateField(("Date"))
total_days = models.IntegerField()
notes = models.TextField(max_length=1000)
def __unicode__ (self):
return u'%s %s' % (self.employee, self.submit_date)
class Admin:
pass
class Meta:
ordering = ['-submit_date']
In the view I need a function to calculate the number of days requested. Secondarily, I'll need a method to count only weekdays, but for now I've got the following:
def leave_screen(request, id):
records = LeaveRequest.objects.filter(employee=id)
total_days = LeaveRequest.return_date - LeaveRequest.start_date
tpl = 'vacation/leave_request.html'
return render_to_response(tpl, {'records': records })
which produces a attribute error
type object 'LeaveRequest' has no attribute 'return_date
any suggestions?
In total_days, you are calling the model and not the instance of that model - records - that you created.
If you want to view just a single Leave record, you would need to pass the id of the LeaveRequest
def leave_screen(request, id):
records = LeaveRequest.objects.get(id=id)
total_days = records.return_date - records.start_date
tpl = 'vacation/leave_request.html'
return render_to_response(tpl, {'records': records })
The answer that suggests using it as a property will work but I think I'll prefer keeping it as a field and just computing it at the time of insert.
class LeaveRequest(models.Model):
employee = models.ForeignKey(UserProfile)
supervisor = models.ForeignKey(UserProfile, related_name='+', blank=False, null=False)
submit_date = models.DateField(("Date"), default=datetime.date.today)
leave_type = models.CharField(max_length=64, choices=TYPE_CHOICES)
start_date = models.DateField(("Date"))
return_date = models.DateField(("Date"))
total_days = models.IntegerField()
notes = models.TextField(max_length=1000)
def __unicode__ (self):
return u'%s %s' % (self.employee, self.submit_date)
def save(self, *args, **kwargs):
self.total_days = (self.return_date - self.start_date).days
super(LeaveRequest, self).save(*args, **kwargs)
class Admin:
pass
class Meta:
ordering = ['-submit_date']
This way when you put in the logic for excluding weekends you are saving computation to calculate the days everytime at the time of listing all leave requests.
I wouldn't have 'total_days' as a field in the LeaveRequest class, but rather as a property.
class LeaveRequest(models.Model):
(other fields)
#property
def total_days(self):
oneday = datetime.timedelta(days=1)
dt = self.start_date
total_days = 0
while(dt <= self.return_date):
if not dt.isoweekday() in (6, 7):
total_days += 1
dt += oneday
return totaldays
# view function
def leave_screen(request, id):
# get leave request by id
leavereq = LeaveRequest.objects.get(id=id)
return render_to_response("vacation/leave_request.html", {"leavereq": leavereq})
# template code
...
<body>
{{ leavereq.total_days }}
</body>