How to Create an Object for a Django Model with a Many to Many Field

How to create an object for a Django model with a many to many field?

You cannot create m2m relations from unsaved objects. If you have the pks, try this:

sample_object = Sample()
sample_object.save()
sample_object.users.add(1,2)

Update: After reading the saverio's answer, I decided to investigate the issue a bit more in depth. Here are my findings.

This was my original suggestion. It works, but isn't optimal. (Note: I'm using Bars and a Foo instead of Users and a Sample, but you get the idea).

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1)
foo.bars.add(bar2)

It generates a whopping total of 7 queries:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1
SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2
INSERT INTO "app_foo" ("name") VALUES ()
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1 AND "app_foo_bars"."bar_id" IN (1))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1 AND "app_foo_bars"."bar_id" IN (2))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

I'm sure we can do better. You can pass multiple objects to the add() method:

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1, bar2)

As we can see, passing multiple objects saves one SELECT:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1
SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2
INSERT INTO "app_foo" ("name") VALUES ()
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1 AND "app_foo_bars"."bar_id" IN (1, 2))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

I wasn't aware that you can also assign a list of objects:

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars = [bar1, bar2]

Unfortunately, that creates one additional SELECT:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1
SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2
INSERT INTO "app_foo" ("name") VALUES ()
SELECT "app_foo_bars"."id", "app_foo_bars"."foo_id", "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE "app_foo_bars"."foo_id" = 1
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1 AND "app_foo_bars"."bar_id" IN (1, 2))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

Let's try to assign a list of pks, as saverio suggested:

foo = Foo()
foo.save()
foo.bars = [1,2]

As we don't fetch the two Bars, we save two SELECT statements, resulting in a total of 5:

INSERT INTO "app_foo" ("name") VALUES ()
SELECT "app_foo_bars"."id", "app_foo_bars"."foo_id", "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE "app_foo_bars"."foo_id" = 1
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1 AND "app_foo_bars"."bar_id" IN (1, 2))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

And the winner is:

foo = Foo()
foo.save()
foo.bars.add(1,2)

Passing pks to add() gives us a total of 4 queries:

INSERT INTO "app_foo" ("name") VALUES ()
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1 AND "app_foo_bars"."bar_id" IN (1, 2))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

How to add an object to a manytomany field in django

For a ManyToManyField, it makes not much sense to use a Select widget [Django-doc], since that selects only a single element, a SelectMultiple widget [Django-doc] is constructed to select mulitple objects.

You should also take a look to the HTML form whether it indeed submits values for the contact field. You can do that by inspecting the browser data.

Nevertheless, even if you get this working, by using commit=False, the form can not save the many-to-many field. Indeed, in order to save records in a many-to-many field, first the Group object should be saved, since the junction table [wiki] has ForeignKeys to the Contact and Group, and thus these records need to be created first. It is also better not to construct a new Form in case the form is not valid, since then the errors will be reported when you render that form:

def create_group(request):
if request.method == 'POST':
form = CreateGroupForm(request.POST)
if form.is_valid():
form.save() # ← no commit=False
return redirect('name-of-some-view')
# do not create a new form.
else:
form = CreateGroupForm()
return render(request, "create-group.html", { 'form' : form})


Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.

How to add value to a many-to-many field in Django

You create a Meeting and than populate it with Teachers and Students, so:

s1 = Student.objects.create(firstname='Foo', last_name='Bar')
s2 = Student.objects.create(firstname='Qux', last_name='Qux')
t1 = Teacher.objects.create(
firstname='Some',
last_name='One',
email='someone@something.com'
)

m = Meeting.objects.create()
m.student.add(s1, s2)
m.teacher.add(t1)

For more information, see the Many-to-many relationships section of the documentation.

Based on the exception you shared in the question, you created the Meeting model before you made primary keys of type UUIDField. We can fix this problem by:

  1. removing the Meeting model;
  2. run makemigrations;
  3. create a new Meeting model;
  4. run makemigrations again; and
  5. run migrate to update the database.

How do you add one object to a Django Model that has a ManyToManyField?

The * in the arguments converts a list to individual args. For example-

lst = [1,2,3,4,5]

function(*lst)

can be just read as

function(1,2,3,4,5)

You have used follower.followers.add(*user). Hence, user must be an iterable to be unpacked and passed as a list or arguments. But user is a single User object.

You should just use follower.followers.add(user) in this case.

How to add id (instead of object) in ManyToManyField of Django model?

We can use method add (Django Docs):

Adds the specified model objects to the related object set.


dept = Department.objects.create(name=name)
dept.employee.add(*[1, 2])

Or method set(Django Docs):

Replace the set of related objects


dept.employee.set([1, 2])


Note that add(), create(), remove(), clear(), and set() all
apply database changes immediately for all types of related fields. In
other words, there is no need to call save() on either end of the
relationship.

Django: How to efficiently add objects to ManyToManyFields of multiple objects?

You can bulk create a record for each combination with a single query:

biology_classes = Class.objects.filter(name__contains='Biology')
linuses = Student.object.filter(first_name='Linus')

StudentClass = Student.classes.through
items = [
StudentClass(student=student, class=class)
for student in linuses
for class in biologoy_classes
]

StudentClass.objects.bulk_create(items)

This thus will make a total of three queries:

  1. one query to fetch the relevant Classes;
  2. one query to fetch the relevant Students; and
  3. one query to create all connections between the Classes and Students.

How to add data to a ManyToMany field model?

You have to use add() function to add new object to ManyToManyField. Remember also, that with filter you get QuerySet. To get object you should ust get or even get_object_or_404, but look yourself for that :)

...
tmp = Department.objects.get(title=self.superior.title)
tmp.subsidiary.add(self.superior)
...

You don't need to save it.

Django - Retrieve all manytomany field objects related to a specific model

You'll want to retrieve objects through the Category model.
You can filter on the related name on the Category model.

Try Category.objects.filter(reviews__isnull=False) or Category.objects.filter(news__isnull=False)

A Django model that only has many to many fields?

You can not directly assign a value to a ManyToManyField, but use the .add(…) method [Djang-doc] as the error indicates:

from django.shortcuts import get_object_or_404

item = get_object_or_404(Listing, pk=self.kwargs['pk'])

watchlist = Watchlist.objects.create()
watchlist.user.add(request.user)
watchlist.listing.add(item)


Note: It is often better to use get_object_or_404(…) [Django-doc],
then to use .get(…) [Django-doc] directly. In case the object does not exists,
for example because the user altered the URL themselves, the get_object_or_404(…) will result in returning a HTTP 404 Not Found response, whereas using
.get(…) will result in a HTTP 500 Server Error.



Note: Since a ManyToManyField refers to a collection of elements,
ManyToManyFields are normally given a plural name. You thus might want
to consider renaming user to users.



Related Topics



Leave a reply



Submit