Update id's for many to many mapping django - python

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.

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')

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()

Is it possible in Django to iterate the creation of a model?

I'm trying to create a model with 100 columns. Is it possible to iterate in my models.py file to create many entries at once?
This is an example of what I'm trying to accomplish.
class AgeFactor(models.Model):
'''
Used to populate a table with all the age-factor numbers
'''
gender = models.CharField(max_length=50, null=True, blank=True)
code = models.CharField(max_length=50, null=True, blank=True)
event_code = models.CharField(max_length=50, null=True, blank=True)
oc = models.DecimalField(max_digits=9, decimal_places=4)
for lp in range(101):
if lp > 4:
(f"{lp} = models.DecimalField(max_digits=9, decimal_places=4)")
Thank you.
Yes, although this is not how to do that. What you can do is use a loop mechanism with:
class AgeFactor(models.Model):
'''
Used to populate a table with all the age-factor numbers
'''
gender = models.CharField(max_length=50, null=True, blank=True)
code = models.CharField(max_length=50, null=True, blank=True)
event_code = models.CharField(max_length=50, null=True, blank=True)
oc = models.DecimalField(max_digits=9, decimal_places=4)
for lp in range(5, 101):
locals()[f'data{lp}'] = models.DecimalField(max_digits=9, decimal_places=4)
This will make a set of fields named data5, data6, data7, … data100.
That being said, it is rather "ugly", and often you can better define a many-to-one relation with a ForeignKey [Django-doc] since that makes the data more "flat".
Furthermore if I run this locally it takes some time for Django to fully evaluate the model, so I do not know how well Django scales with a large amount of fields (the for loop itself is not the problem, but Django has a lot of bookkeeping for these fields).
Finally querying can also be slow, because the database has to load all these columns, furthermore queries will be quite large, because the SELECT part will enumerate all the data… fields.

How to apply filter on Many to many field without using for loop

I Model is like
class Company(models.Model):
name = models.CharField(max_length=400, blank=True, null=True)
class Intership(models.Model):
company = models.ForeignKey(Company)
location = models.CharField(max_length=400, blank=True, null=True)
class Student(models.Model):
name = models.CharField(max_length=400, blank=True, null=True)
intership = models.ManyToManyField(Intership,null= True, blank=True)
I am looking forward to get all the student who done internship in a company with name "xyz".
i have the code
company_name = "xyz"
stds
for student in students:
interships = student.intership.all()
for intership in interships:
if intership.company.name == company_name:
stds.append(student)
Is it possible to get it all this on a single query??
You can just filter on the Students itself:
Student.objects.filter(intership__company__name='xyz')
You might want to use .distinct() here, since otherwise a Student that multiple internships in the same company will be listed multiple times:
Student.objects.filter(intership__company__name='xyz').distinct()
Note: It is internship, not intership.
Mymodel.objects.filter(username='abcd') will give list of match record
Mymodel.objects.get(pk='abcd') will return single record with matching on primary key value

Filter by presence in a model's set

I have a Customer model that has many Locations, i.e., there is a location_set attribute on the model that returns a list of locations. Each Location also has many customers, i.e., a customer_set attribute.
I have one customer instance with all of its corresponding attributes. What I want to do is return all other customers who are present in at least of the locations in the customer's location_set. Is there a clean way to do this without having to manually manipulate the queryset and make a ton of calls to the DB?
class Customer(AbstractUser):
current_location = models.ForeignKey('device.Location',
null=True, blank=True, related_name='customers_present')
default_location = models.ForeignKey('device.Location',
null=True, blank=True, related_name='default_customers')
class Location(models.Model):
name = models.CharField(max_length=50, help_text="The name of the location")
customers = models.ManyToManyField(settings.AUTH_USER_MODEL,
through='customer.Membership')
class Membership(models.Model):
customer = models.ForeignKey(Customer)
location = models.ForeignKey('device.Location')
date_joined = models.DateTimeField(auto_now_add=True)
Without your model definitions it is very difficult to provide an exact answer to your question, something like the below could work:
Customer.objects.filter(location__in=your_customer_instance.location_set.all()).exclude(pk=your_customer_instance.pk)

Categories

Resources