Django foreignkey and tables one to many relationship - python

I am new to Django and python and I have been following the Django tutorial to build a personal project.
So far I have been able to do it without major problems, but have got stuck in the creation of the models.py when I need more than one table and need to build relationship with them.
I have two tables: one related to a product list and another one related to average prices for those types of products
I basically want to get the records of table 2 and add the average price for them which is stored in table 1, and the join must be done using 3 fields: model, year and size.
When I query only the products table (2), I do it as
latest_products = Products.objects.all().order_by('-date_added')
How could I get also the average price for each product from the average price table inside this query?
I have already read the documentation and many posts here but still have this mess in my
table 1 (average price)
class Avg_price(models.Model):
model = models.CharField(max_length=50, blank=True)
size= models.IntegerField(blank=True, null=True)
year= models.IntegerField(blank=True, null=True)
price= models.IntegerField(blank=True, null=True)
table 2 (product list)
class Products(models.Model):
product_id =models.IntegerField(blank=True, null=True)
location = models.CharField(max_length=200, blank=True)
date_added= models.DateTimeField(blank=True, null=True)
status = models.CharField(max_length=50, blank=True)
model = models.CharField(max_length=50, blank=True)
size= models.IntegerField(blank=True, null=True)
year= models.IntegerField(blank=True, null=True)
price= models.IntegerField(blank=True, null=True)
avg_price=models.ForeignKey(Avg_price)
Sorry if this questions might sound dumb... and thanks for any help!

Once you have a Products instance, you also have their related Avg_price. Check this out:
>>> my_product = latest_products[0]
>>> print my_product.avg_price.year
"prints my_product's price's year"
Alternatively, you could use .select_related queryset method:
products_prices = Products.objects.all().select_realted('avg_price')
Does that answer your question?

Related

What's the most efficient way to retrieve django queryset with the hightest number of posts for a related name?

I'm currently working on a website where advertisements will be posted to display vehicles for sale and rent. I would like to retrieve a queryset that highlights only one car brand (i.e. Audi) which has the highest number of posts for the respective model. Example:
Displaying the Audi brand because it has the highest number of related posts.
My question is, what's the most efficient way of doing this? I've done some work here but I'm pretty sure this is not the most efficient way. What I have is the following:
# Algorithm that is currently retrieving the name of the brand and the number of related posts it has.
def top_brand_ads():
queryset = Advertisement.objects.filter(status__iexact="Published", owner__payment_made="True").order_by('-publish', 'name')
result = {}
for ad in queryset:
# Try to update an existing key-value pair
try:
count = result[ad.brand.name.title()]
result[ad.brand.name.title()] = count + 1
except KeyError:
# If the key doesn't exist then create it
result[ad.brand.name.title()] = 1
# Getting the brand with the highest number of posts from the result dictionary
top_brand = max(result, key=lambda x: result[x]) # Returns for i.e. (Mercedes Benz)
context = {
top_brand: result[top_brand] # Retrieving the value for the top_brand from the result dict.
}
print(context) # {'Mercedes Benz': 7} -> Mercedes Benz has seven (7) related posts.
return context
Is there a way I could return a queryset instead without doing what I did here or could this be way more efficient?
If the related models are needed, please see below:
models.py
# Brand
class Brand(models.Model):
name = models.CharField(max_length=255, unique=True)
image = models.ImageField(upload_to='brand_logos/', null=True, blank=True)
slug = models.SlugField(max_length=250, unique=True)
...
# Methods
# Owner
class Owner(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
telephone = models.CharField(max_length=30, blank=True, null=True)
alternate_telephone = models.CharField(max_length=30, blank=True, null=True)
user_type = models.CharField(max_length=50, blank=True, null=True)
payment_made = models.BooleanField(default=False)
expiring = models.DateTimeField(default=timezone.now)
...
# Methods
# Advertisement (Post)
class Advertisement(models.Model):
STATUS_CHOICES = (
('Draft', 'Draft'),
('Published', 'Published'),
)
owner = models.ForeignKey(Owner, on_delete=models.CASCADE, blank=True, null=True)
name = models.CharField(max_length=150, blank=True, null=True)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE, blank=True, null=True)
publish = models.DateTimeField(default=timezone.now)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='Draft')
...
# Other fields & methods
Any help would be greatly appreciated.
Since you need brands, let's query on Brand model:
Brand.objects.filter(advertisement__status__iexact="Published").\
filter(advertisement__owner__payment_made=True).\
annotate(published_ads=Count('advertisement__id')).\
order_by('-published_ads')
However, even in your proposed solution, you can improve a little bit:
Remove the order_by method from your queryset. It doesn't affect the final result but adds some overhead, especially if your Advertisement model is not indexed on those fields.
Every time you call ad.brand you are hitting the database. This is called the N+1 problem. You are in a loop of n, you make n extra db access. You can use select_related to avoid such problems. In your case: Advertisement.objects.select_related('brand')...
Did you try the count method?
from django.db.models import Count
Car.objects.annotate(num_views=Count('car_posts_related_name')).order_by('num_views')

How do I call a specific field in a model into another model?

I am new to django. I wanted to know if it was possible to call a specific field in a model into another model.
The goal is to update a the quantity of a specific product in the database and generate a receipt for it.
Here's the example:
models.py:
class Product(models.Model):
name = models.CharField(max_length=300, null=True)
price = models.FloatField(null=True)
quantity = models.IntegerField(default='0', blank=True, null=True)
class UpdateStock(models.Model):
date = models.DateTimeField(default=timezone.now)
wayBill_Number = models.CharField(max_length=300, null=True)
product_Name = models.ForeignKey(Product.name, null=True, on_delete= models.SET_NULL)
quantity = models.ForeignKey(Product.quantity, null=True, on_delete= models.SET_NULL)
I just want to select the product and the quantity to update in the database but I am unable to do so.
A ForeignKey is supposed to refer to another model, not to one of its fields.
So the first thing you have to do it to edit your UpdateStock model this way :
class UpdateStock(models.Model):
date = models.DateTimeField(default=timezone.now)
wayBill_Number = models.CharField(max_length=300, null=True)
product = models.ForeignKey('Product', null=True, on_delete=models.SET_NULL) # Here
quantity = models.IntegerField(default='0', blank=True, null=True) # Here
So you can bound your product to your stock_update when you create it:
stock_update = UpdateStock.objects.create(..., product=your_product_obj, ...)
And you will then be able to access any product's field from this stock_update:
product = stock_update.product
print(product.quantity)
product.quantity = product.quantity + stock_update.quantity
print(product.quantity)
product.save()
But I strongly advise you to read more about many-to-one relationships in the docs.

Is there a way to query reverse in django models?

I know this question has been answered in different ways but i'm still unable to see the clear picture.
I have the following tables with following relationships:
class Category(models.Model):
name = models.CharField(max_length=100, null=False, blank=False)
def __str__(self):
return self.name
class SubCategory(models.Model):
sub_name = models.CharField(max_length=100, null=False, blank=True, default='None')
category = models.ManyToManyField(Category, default=1)
class Product(models.Model):
name = models.CharField(max_length=150, null=False, blank=False)
brand = models.CharField(max_length=100, null=False, blank=False)
price = models.FloatField(null=False, blank=False)
weight = models.CharField(max_length=100,null=False, blank=False)
sub_category = models.ForeignKey(SubCategory, on_delete=models.SET_DEFAULT, default=13)
category = models.ForeignKey(Category, on_delete= models.CASCADE)
I am trying to solve two queries as follows:
Fetch all the category and subcategories to a specific category where the brand is given. Display structure that i'm making is Brand(Men->Shirts,Pants etc. Women->Shirts,Pants etc).
NOTE: Each brand can sell products of multiple categories and subcategories.
Fetch all the subcategories where the category name must be taken out from the result of Category.objects.all(). Display structure that i'm making here is Men(its sub categories) , Women(its sub categories)
Let us take it step by step
Get all products for a specific brand
Product.objects.filter(brand=brand)
Now we want to list down the categories for this match. We'll get the ids of categories instead
Product.objects.filter(brand=brand).values_list("category_id", flat=True)
Now let us get the corresponding category objects
queryset = Product.objects.filter(brand=brand).values_list("category_id", flat=True)
categories = Category.objects.filter(id__in=queryset)
Note: If you just want to fetch the category names, you can do
Product.objects.filter(brand=brand).values_list("category__name", flat=True).distinct()

Update id's for many to many mapping django

I have two models with many to many mapping:
class Child_details(models.Model):
name = models.CharField(max_length=200, default="")
gender = models.CharField(max_length=2, choices=GENDER_CHOICES)
dob = models.DateField(null=True, blank=True)
images_history = models.ManyToManyField(Image_history, blank=True)
class Image_history(models.Model):
image_type = models.CharField(max_length=100, blank=True, null=True, choices=PAGE_TYPES)
image_link = models.CharField(max_length=100, blank=True, null=True)
Now that, I have two different objects for Child_details: child_obj1 and child_obj2. Since child_obj2 is a duplicate of child_obj1, I wanted to delete that object and before doing that I have to refer all image_history of child_obj2 to child_obj1.
I tried doing:
for image in child_obj2.image_history.all():
image.child_details_id=child_obj1.id
#but, I can't do the above one because there is no child_details_id in image_history model
Django creates a third table with child_details_id, image_history_id.
I wanted to change child_details_id in the third table created by django for many to many mapping.
Is it correct doing that and If so how can I?
I got it. Basically what I've done is I iterated through child_obj2's images_history and made entries in child_obj1's image_history.
for child_image_history in child_obj2.images_history.all():
image_history = Image_history(image_type=child_image_history.image_type, image_name=child_image_history.image_name)
image_history.save()
child_obj1.images_history.add(image_history)
child_image_history.delete()
And deleted the previous entries of child_obj2 which is not required.

How to insert JSON data to database in Django?

I've built a scraper that gets product data from different shopping websites.
When I run python scraper.py the program will print a JSON object containing all the data like this:
{ 'ebay': [ { 'advertiser': 'ebay',
'advertiser_url': 'https://rover.ebay.com/rover/1/711-53200-19255-0/1?ff3=2&toolid=10041&campid=5338482617&customid=&lgeo=1&vectorid=229466&item=302847614914',
'description': '30-Day Warranty - Free Charger & Cable - '
'Easy Returns!',
'main_image': 'https://thumbs1.ebaystatic.com/pict/04040_0.jpg',
'price': '290.0',
'title': 'Apple iPhone 8 Plus Smartphone AT&T Sprint '
'T-Mobile Verizon or Unlocked 4G LTE'}
]}
I want this data to be added to the database automatically every time I run the scraper.
Here's my database structure:
models.py
class Product(models.Model):
similarity_id = models.CharField(max_length=255, blank=True, null=True)
name = models.CharField(max_length=255, blank=True, null=True)
url = models.SlugField(blank=True, unique=True, allow_unicode=True)
advertiser_url = models.TextField(blank=True, null=True)
main_image = models.TextField(blank=True, null=True)
second_image = models.TextField(blank=True, null=True)
third_image = models.TextField(blank=True, null=True)
old_price = models.FloatField(default=0.00)
price = models.FloatField(default=0.00)
discount = models.FloatField(default=0.00)
currency = models.CharField(max_length=255, default="$")
description = models.TextField(blank=True, null=True)
keywords = models.CharField(max_length=255, blank=True, null=True)
asin = models.CharField(max_length=80, blank=True, null=True)
iban = models.CharField(max_length=255, blank=True, null=True)
sku = models.CharField(max_length=255, blank=True, null=True)
seller = models.CharField(max_length=255, blank=True, null=True)
free_shipping = models.BooleanField(default=False)
in_stock = models.BooleanField(default=True)
sold_items = models.IntegerField(default=0)
likes_count = models.IntegerField(default=0)
category = models.CharField(max_length=255, blank=True, null=True)
sub_category = models.CharField(max_length=255, blank=True, null=True)
reviews_count = models.IntegerField(default=0)
rating = models.FloatField(default=0)
active = models.BooleanField(default=True)
is_prime = models.BooleanField(default=False)
created_on = models.DateTimeField(auto_now_add=True)
advertiser = models.CharField(max_length=255, blank=True, null=True)
objects = ProductManager()
class Meta:
verbose_name_plural = "products"
def __str__(self):
return self.name
Add this to scrapper.py:
import path.to.model
product = Product()
product.<key> = <value> #Where key is the field and value is the value you need to fill
and after you assign every field, add
product.save()
Trick
If all the keys in the json response match the fields in the model, you can do:
for k, v in response.items():
setattr(product, k, v)
product.save()
That will save you a lot of lines and time :)
I work with json a lot; I have API caching where I receive a lot of json-based API data and I want to store it in a database for querying and caching. If you use postgres (for instance), you will see that if has extensions for json. This means that you can save json data in a special json field. But better, there are sql extensions that let you run queries on the json data. That is, postgres has "no sql" capabilities. This lets you work with json natively. I find it very compelling and I recommend it highly. It is a learning curve because it uses non-traditional sql, but heck, we have stackoverflow.
see: https://django-postgres-extensions.readthedocs.io/en/latest/json.html
here is a little example:
product_onhand_rows = DearCache.objects.filter(
object_type=DearObjectType.PRODUCT_AVAILABILITY.value).filter(
dear_account_id=self.dear_api.account_id).filter(jdata__Location=warehouse).filter(jdata__SKU=sku).all()
in this example, I have the json stored in a field jdata.
jdata__Location accesses the key Location in the json.
It nests and so on. For advanced queries, I resort to sql
select object_type,last_modified, jdata
from cached_dear_dearcache
where object_type = 'orders'
and jdata->>'Status' in ('ESTIMATING','ESTIMATED')
order by last_modified;
and there's more, you can 'unroll' lists (this is what I would call a complicated example, my json has lists of invoices, each of which has a list of lines...)
/* 1. listing invoice lines. We have to iterate over the array of invoices to get each invoice, and then inside the invoice object find the array of lines */
select object_type,last_modified, jsonb_array_elements(jsonb_array_elements(cached_dear_dearcache.jdata#>'{Invoices}')->'Lines') as lines,
jsonb_array_elements(cached_dear_dearcache.jdata#>'{Invoices}')->'InvoiceDate' as invoice_date,
jsonb_array_elements(cached_dear_dearcache.jdata#>'{Invoices}')->'InvoiceNumber' as invoice_number
from cached_dear_dearcache
where object_type = 'orders' order by last_modified;
Your approach is to convert the json data to a traditional sql model. That will work too. It's not very flexible ... if the json "schema" changes, your database schema may need to change. Philosophically, I think it is better to go with the flow, and use the json extensions, this is the best of both worlds. Performance is good, by the way.

Categories

Resources