custom query in django - django

I'm building an ecommerce website.
I have a Product model that holds info common to all product types:
class Product(models.Model):
name=models.CharField()
description=models.CharField()
categories = models.ManyToManyField(Category)
Then I have SimpleProduct and BundleProduct that have FK to Product and hold info specific to the product type. BundleProduct has a m2m field to other Products.
class SimpleProduct(Product):
some_field=models.CharField()
class BundleProduct(Product):
products = models.ManyToManyField(Product)
When displaying the catalog I'm making one query against the Product model
and then another query per product to get the additional info.
This involve a large number of queries.
I can improve it by using select_related on the simpleproduct and bundleproduct fields.
I can further improve it by using the select_reverse app for m2m fields like categories.
This is a big improvement but there are more required queries because a BundleProduct have several products which can also have relations to other products (configurable product).
Is there a way to have a single query against Product that will retrieve the m2m categories, one2one SimpleProduct and BundleProduct and the BundleProduct's products?
Will this custom query look like a django queryset with all the managers and properties?
Thanks

You can possibly take a look at the extra method of querysets. May give you the opportunity to add some additional fields. But if you want raw queries, you can use the raw method of managers, these will return a type of queryset, that will not however harness the full power of normal querysets but should be enough for your concerns. On that same page the execute method is also shown, this is for truly custom sql that can't even translate into raw querysets.

Related

How to relate two different django models into on many to many field form another one?

I'm trying to find a solution to the following problem with django models:
The app would add products to a shopping cart. The problem is that those products come from different models, and no matter where they come from, I'd like to use one ManyToManyField to incorporate them.
Suppouse I've two different kind of products from two different models:
Class ListOfProducts1(models.Model):
name= CharField()
price= IntegerField()
Class ListOfProducts2(models.Model):
name=CharField()
price=IntegerField()
The following model would be used for get the quantity and the product added(and then related to one particular order).
Here is the problem. Look to the field itemsadded, where I want to relate this field to ListOfProducts 1 and 2. I'd put both of them into the same field, but I've already know this is not possible. Just for better understanding of the problem here you have:
Class ProductsOrdered(models.Model):
itemsadded= OneToOneField(ListOfProducts1 and ListOfProducts2,on_delete=models.CASCADE)
Quantity = IntegerField()
Then the Order's model:
Class Orders(models.Model):
cart= ManyToManyField(ProductsOrdered)
The OneToOne fields can refer to only one table.
But you can use either GenericForeignKey or alternatives (which is better).
Look at this article: Avoid Django’s GenericForeignKey. It describes alternatives, such as:
nullable fields on source table
intermediate table with nullable fields
intermediate table with OneToOneFields on destination models
multi-table inheritance
multiple linked models

Optimal project organization and querysets

I have 2 models Company and Product with FK on Product:
class Product(Meta):
company = models.ForeignKey(Company, related_name='products', on_delete=models.CASCADE)
In case of a View that will gather company products what is the optimal approach(use infor form both models):
1) add the View in companies app and as queryset use:
Company.objects.prefetch_related('products').get(pk=company_pk)
2) add the View in products app and as queryset use:
Product.objects.select_related('company').filter(company=company_pk)
What about ordering can be chained with prefetch or select ?
The Django docs illustrate the difference quite well:
prefetch_related(*lookups)
Returns a QuerySet that will
automatically retrieve, in a single batch, related objects for each of
the specified lookups.
This has a similar purpose to select_related, in that both are
designed to stop the deluge of database queries that is caused by
accessing related objects, but the strategy is quite different.
select_related works by creating an SQL join and including the fields
of the related object in the SELECT statement. For this reason,
select_related gets the related objects in the same database query.
However, to avoid the much larger result set that would result from
joining across a ‘many’ relationship, select_related is limited to
single-valued relationships - foreign key and one-to-one.
select_related(*fields)
Returns a QuerySet that will “follow” foreign-key relationships,
selecting additional related-object data when it executes its query.
This is a performance booster which results in a single more complex
query but means later use of foreign-key relationships won’t require
database queries.

Pythonically filter Django queryset based on lack of reverse relationships

Suppose I have two Django models, let's say Product and Manufacturer, and wish to find Manufacturers who have no Products.
class Product(Model):
name = models.CharField()
manufacturer = models.ForeignKey(Manufacturer)
class Manufacturer(Model):
name = models.CharField()
I want to generate a set of all Manufacturer objects that are not referred to by any Product objects. I can think of many ways to get this set, but none of them seem clean or Pythonic
Given the queryset of all Products, generate a set of all Manufacturers that have products. Then take the set difference of all manufacturers with the manufacturers_with_products as described here: How to get the difference of two querysets in Django.
Filter the Manufacturer objects based on the lack of a reverse relationship with any Products. Django makes it easy to filter based on properties of reverse relationships, but I cannot find a clean way to filter based on the existence of this reverse relationship.
eg, I can trivially filter Manufacturers that have at least one Product that satisfies a condition:
models.Manufacturer.objects.filter(product__name=x)
I'd like to be able to filter something more like
models.Manufacturer.objects.exclude(product__exists)
I could probably exclude/filter Manufacturers on some axiom/tautology of Products, but that also doesn't seem very Pythonic.
This can be done using an annotation and a filter:
from django.db.models import Count
idle = Manufacturers.objects.annotate(num_products=Count('product')).filter(num_products=0)
idle will the contain a list of Manufacturers who have no products, but keep in mind they will also have a property num_products, which due to the query will be 0.

Can Tastypie handle arbitrary arrays of objects?

I'm working on a project which requires REST API. I have tried Piston but it doesn't suit my requirement as it currently allows only 1 handler per model.
Tastypie seems to be a better alternative. However, I'm stuck with the following problem. My article class is displayed according to a complex rule such as ranking and date created. To enhance server performance, I created a dummy table which records the order of all the articles so that upon user requests, the complex ordering process will not be executed. Instead, the server checks for the orders of each article from the dummy table.
With Tastypie, a query set is required. However, because I want to use the orders recorded in the dummy table, I have to use a more complex code snippet to retrieve the data.
Is there any possibility that I can return an array of article objects and Tastypie can transform them into a proper JSON format.
What you need is extending the queryset in Meta. Assuming your articles table should be ordered by some additional data your queryset would be defined for example like that:
Meta:
queryset = Article.objects.extra(select={
'ordering': 'SELECT foo FROM bar'
},).order_by('ordering')
You have to define the additional fields in your resources:
ordering = field.IntegerField(attribute="ordering", default=0, readonly=True)
The additional field should now be returned with all other fields retrieved from your queryset. Note that if you define the fields attribute in your meta you also have to add the new field there.

Django ManyToMany in one query

I'm trying to optimise my app by keeping the number of queries to a minimum... I've noticed I'm getting a lot of extra queries when doing something like this:
class Category(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=127, blank=False)
class Project(models.Model):
categories = models.ManyToMany(Category)
Then later, if I want to retrieve a project and all related categories, I have to do something like this :
{% for category in project.categories.all() %}
Whilst this does what I want it does so in two queries. I was wondering if there was a way of joining the M2M field so I could get the results I need with just one query? I tried this:
def category_list(self):
return self.join(list(self.category))
But it's not working.
Thanks!
Which, whilst does what I want, adds an extra query.
What do you mean by this? Do you want to pick up a Project and its categories using one query?
If you did mean this, then unfortunately there is no mechanism at present to do this without resorting to a custom SQL query. The select_related() mechanism used for foreign keys won't work here either. There is (was?) a Django ticket open for this but it has been closed as "wontfix" by the Django developers.
What you want is not seem to possible because,
In DBMS level, ManyToMany relatin is not possible, so an intermediate table is needed to join tables with ManyToMany relation.
On Django level, for your model definition, django creates an ectra table to create a ManyToMany connection, table is named using your two tables, in this example it will be something like *[app_name]_product_category*, and contains foreignkeys for your two database table.
So, you can not even acces to a field on the table with a manytomany connection via django with a such categories__name relation in your Model filter or get functions.