Django: Saving multiple ManyToMany fields within a transaction - python

this is the representation of my models:
class B(models.Model):
"""I'm a dummy model, so doesn't pay atention of what I do"""
name = models.CharField(max_length=250)
class A(models.Model):
name = models.CharField(max_length=250)
many_b = models.ManyToManyField(B)
Now, suppose I have a list of B objects. And a single A object that will be related to that Bs. Something like this:
a = A.objects.get(id=1)
list_of_b = [B<name='B1'>,B<name='B2'>,B<name='B3'>,]
The way I relate them now is this:
for b_object in list_of_b:
a.many_b.add(b_object)
Is there any way to add all the B objects in a single transaction? Maybe in a single method, like:
a.many_b.addList(b) #This doesn't exist

From the docs:
>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)
So if you have a list, use argument expansion:
a.many_b.add(*list_of_b)

I guess what you want is a kind of bulk insert right?
As far as I know this is just available in the Django TRUNK not in 1.3!
check it out some tutorial:
http://www.caktusgroup.com/blog/2011/09/20/bulk-inserts-django/

Related

How to use prefetch_related on two M2M values?

I want to prefetch_related to two level of M2M values,
Here is my models.py
class A(models.Model):
name = models.CharField(max_length=40)
b = models.ManyToManyField('B')
class B(models.Model):
name = models.CharField(max_length=40)
c = models.ManyToManyField('C')
class C(models.Model):
name = models.CharField(max_length=40)
d = models.ManyToManyField('D')
And my ORM is
a_obj = A.objects.all().prefetch_related('a__b__c')
And I am trying to access the values like below,
Method A:
for each_obj in a_obj:
print(each_obj.a__b__c)
Method B:
for each_obj in a_obj:
print(each_obj.a.all())
Method A throws an error saying No such value a__b__b for A found
Method B doesn't throw any error, but the number of queries increases to the length of a_obj.
Is there a way to access a__b__c in a single query?
You load both the related B and C models with .prefetch_related(…) [Django-doc]:
a_objs = A.objects.prefetch_related('b__c')
But here .prefetch_related(…) does not change how the items look, it simply loads items. You thus can access these with:
for a in a_objs:
for b in a.b.all():
for c in b.c.all():
print(f'{a} {b} {c}')
You this still access the items in the same way, but here Django will already load the objects in advance to prevent extra queries.

Check if object has relation with other in ManyToMany field and aggregate field

I have a couple models as follows:
class Student(models.Model):
name = models.CharField()
classes = models.ManyToManyField(Class, related_name="students")
class Class(models.Model):
nombre = models.CharField
What I need is a way such that, when querying students in certain class, instead of just returnig the students enrolled in that class, it returns a QuerySet of all the students, each one with an aggregated field indicating if such student is enrolled in that class, e.g.:
[{'name':'student A', 'enrolled_in_physics':True}, {'name':'student B', 'enrolled_in_physics':False}]
I think it can be achieved through F() expressions along with ExpressionWrapper, but have no idea of how implement them; additionaly, the documentation and the examples are not very noob-friendly. Any help is appreciated, thanks!.
EDIT: Ok, I think using the word "list" is not the correct one, I need a normal QuerySet, such that let me do something like this:
student_a = query[0]
student_a.name
>>>'A'
student_a.enrolled_in_physics
>>>True
UPDATED
You could try defining a method within the student class:
class Student(models.Model):
name = models.CharField()
classes = models.ManyToManyField(Class, related_name="students")
def enrolled_in_class(self,class_name):
if len(Class.objects.filter(nombre=class_name,students__in=self)) > 0:
return True
else:
return False
students = Student.objects.all()
student_a = students[0]
student_a.name
student_a.enrolled_in_class('physics')
Original Answer:
Edit: Sorry misread question, I'll try and get an answer together for what you actually wanted: A list of all students, and binary true false if they are enrolled in a specific class.
Alright, I'm reading this as a:
I need a query that returns a list of dictionaries, indicating the student, and enrollment in a class.
#Assume: class_name = 'physics'
def get_class_list(class_name):
filtered_class = Class.object.get(nombre=class_name)
student_names = Students.objects.filter(classes__contains=filtered_class).values_list('name',flat=True)
class_list = []
for name in student_names:
class_list = {}
class_list['name'] = name
enrolled_class_string = 'enrolled_in_' + class_name
class_list[enrolled_class_string] = True
return class_list
This will give you a list of dictionaries with the keys named 'name', and 'class__name'

Django ORM : Categorizing a list query on multiple foreign keys

My title may seem vague, but I'm sorry to save I have no other idea on how to phrase this. Assuming my model structure looks like this:
class Restaurant(models.Model):
name = models.CharField(...necessary stuff...)
class Cuisine(models.Model):
name = models.CharField(...necessary stuff...)
# thai chinese indian etc.
class Food(models.Model):
restaurant = models.ForeignKey(Restaurant, related_name='restaurant')
cuisine = models.ForeignKey(Cuisine, related_name='cuisine')
name = models.CharField(...)
What I want is a list of objects of Food of a specific restaurant. But the Food objects need to be under their respective Cuisine, so that I can easily access the Food through the context. Is it possible to achieve this in any way?
My current query:
q = Cuisine.objects.prefetch_related('cuisine')
q = q.filter(cuisine__restaurant_id=restaurant.id) # say restaurant.id=1
# here restaurant is the object which I have retrieved
Well, what it does is it filters the cuisines available to the restaurant, but lists all food within those cuisine. I want only the food available in the restaurant. I think I am missing something in the way I built my models, but I'm not certain. It would be really helpful if someone could point me to the right direction. Thanks.
Food.objects.filter(restuarant_id=1, cuisine_id__in=selected_cuisine_ids)
Here, selected_cuisine_ids is the list of IDs of whichever cuisines needed
In my opinion, you should use ManyToManyField with through argument. So your models should be like:
class Restaurant(models.Model):
name = models.CharField(...necessary stuff...)
cuisines = models.ManyToManyField(Restaurant, through='Food', related_name='restaurants')
class Cuisine(models.Model):
name = models.CharField(...necessary stuff...)
# thai chinese indian etc.
class Food(models.Model):
restaurant = models.ForeignKey(Restaurant, related_name='restaurant')
cuisine = models.ForeignKey(Cuisine, related_name='cuisine')
name = models.CharField(...)
In this way, your query would be like this:
Cuisine.objects.filter(restaurants__id=1)

Many to many relation. ORM Django

class Toy(models.Model):
name = models.CharField(max_length=20)
desc = models.TextField()
class Box(models.Model):
name = models.CharField(max_length=20)
proprietor = models.ForeignKey(User, related_name='User_Box')
toys = models.ManyToManyField(Toy, blank=True)
How to create a view that add Toy to Box?
def add_this_toy_to_box(request, toy_id):
You can use Django's RelatedManager:
A “related manager” is a manager used in a one-to-many or many-to-many related context. This happens in two cases:
The “other side” of a ForeignKey relation. That is:
class Reporter(models.Model):
...
class Article(models.Model):
reporter = models.ForeignKey(Reporter)
In the above example, the methods below will be available on the manager reporter.article_set.
Both sides of a ManyToManyField relation:
class Topping(models.Model):
...
class Pizza(models.Model):
toppings = models.ManyToManyField(Topping)
In this example, the methods below will be available both on topping.pizza_set and on pizza.toppings.
These related managers have some extra methods:
To create a new object, saves it and puts it in the related object set. Returns the newly created object:
create(**kwargs)
>>> b = Toy.objects.get(id=1)
>>> e = b.box_set.create(
... name='Hi',
... )
# No need to call e.save() at this point -- it's already been saved.
# OR:
>>> b = Toy.objects.get(id=1)
>>> e = Box(
... toy=b,
... name='Hi',
... )
>>> e.save(force_insert=True)
To add model objects to the related object set:
add(obj1[, obj2, ...])
Example:
>>> t = Toy.objects.get(id=1)
>>> b = Box.objects.get(id=234)
>>> t.box_set.add(b) # Associates Box b with Toy t.
To removes the specified model objects from the related object set:
remove(obj1[, obj2, ...])
>>> b = Toy.objects.get(id=1)
>>> e = Box.objects.get(id=234)
>>> b.box_set.remove(e) # Disassociates Entry e from Blog b.
In order to prevent database inconsistency, this method only exists on ForeignKey objects where null=True. If the related field can't be set to None (NULL), then an object can't be removed from a relation without being added to another. In the above example, removing e from b.entry_set() is equivalent to doing e.blog = None, and because the blog ForeignKey doesn't have null=True, this is invalid.
Removes all objects from the related object set:
clear()
>>> b = Toy.objects.get(id=1)
>>> b.box_set.clear()
Note this doesn't delete the related objects -- it just disassociates them.
Just like remove(), clear() is only available on ForeignKeys where null=True.
Reference: Relevant Django doc on handling related objects
Django automatically creates reverse relations on ManyToManyFields, so you can do:
toy = Toy.objects.get(id=toy_id)
toy.box_set.add(box)

Search by a property of a reference

I have the following models:
class Station(db.Model):
code = db.StringProperty(required=True)
name = db.StringProperty(required=True)
class Schedule(db.Model):
tripCode = db.StringProperty(required=True)
station = db.ReferenceProperty(Station, required=True)
arrivalTime = db.TimeProperty(required=True)
departureTime = db.TimeProperty(required=True)
How can I order programatically all the Schedules by Station's name?
Something like Schedule.all().order('station.name')
You will to de-normalize your models or sort the results in memory:
Schedule.all().fetch(100).sort(key=lambda s: s.station.name)
(code not tested)
After use sort i think you need to fetch all entities:
Schedule.all().fetch (100).sort(key=lambda s: s.station.name)
May be you can also use collection name.
But i think the jbochi answer is better :)
[x.schedule_set.get () for x in Station.all ().order ('name')]

Categories

Resources