16

I have a method that works, but it seems very clumsy, and I would think there is a better way to do this.

I have a Model that relates a user on my site (a twitter clone for learning purposes) to a list of other users.

Right now when I create a new user, I want to initialize that list with the user as a member of the list.

My model is:

class FollowerList(models.Model)
    follower = models.ForeignKey(User,related_name="follower")
    followed = models.ManyToManyField(User,related_name="followed")

The code in my view that I'm using right now is

user = User.objects.get(username=uname)
flst = FollowerList()
flst.follower = user
flst.save()
flst.followed.add(user)
flst.save()

It seems to me like there should be a method for creating this without calling save() twice, but I can't seem to find it in the docs or anywhere else.

Daniel Holmes
  • 1,952
  • 2
  • 17
  • 28
Matt Phillips
  • 11,249
  • 10
  • 46
  • 71

3 Answers3

15

You don't need to call save after the many2many.add()

You could also shorten the code to 2 lines:

flst = FollowerList.objects.create(follower=user)
flst.followed.add(user)
Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245
  • The issue when I do this is that I get an error saying the FollowerList object must have a primary key before it can add the ManyToManyField instances to it. And the only way I can see to get that is to instantiate it first and save it then add the other field. – Matt Phillips Dec 23 '10 at 06:13
  • Well, FollowerList.objects.create(follower=user) will give it a primary key. It does the save. – Yuji 'Tomita' Tomita Dec 23 '10 at 06:15
  • 1
    @jperelli, yes, sprinkled throughout the topics on ManyToMany (google it) and a detailed reference here https://docs.djangoproject.com/en/dev/ref/models/relations/#related-objects-reference – Yuji 'Tomita' Tomita Feb 11 '12 at 20:37
4

Yuji's answer is correct. You can not add an object to a M2M field until it has been saved. I wanted to mention a shorter way to create instances though.

user = User.objects.get(username=uname)
flst = FollowerList(follower=user) #use key word args to assign fields
flst.save()
flst.followed.add(user)
# don't need to save after adding an object to many to many field.

I find that syntax slightly nicer than creating an empty instance and assigning fields. Though the objects.create() method (mentioned by Yuki) is nicer still.

Josh Smeaton
  • 47,939
  • 24
  • 129
  • 164
2

A late answer to this: you could also override the constructor (__init__) as follows:

class FollowerList(models.Model):
    follower = models.ForeignKey(User,related_name="follower")
    followed = models.ManyToManyField(User,related_name="followed"

    def __init__(*args, followed=[], **kwargs):
        super(FollowerList, self).__init__(*args, **kwargs)
        self.save()
        for user in followed:
            self.followed.add(user)

ie here I've explicitly handled the followed keyword argument in the __init__ function, while passing all other args and kwargs on to the default constructor. The call to save makes sure that the object has been registered and can thus be used in an m2m relationship.

This then allows you to do create FollowerList with one line, eg

flst = FollowerList(follower=user, followed=[user,])

Alternatively, as pointed out by Johannes, saving a model in the __init__ is not expected. The preferred approach would be to create a Manager method - see here for details: https://docs.djangoproject.com/en/1.9/topics/db/managers/ and then to create a FollowerList:

fl = FollowerList.objects.create(*args, followed, **kwargs)
jmetz
  • 12,144
  • 3
  • 30
  • 41
  • 1
    not really sure if saving the model in the init method is a good idea though. i would not expect that to happen if i create an instance of a new object (if i want to have it saved to the database straight i'd rather call objects.create()) – Johannes Lerch Mar 25 '15 at 14:55