Question: django admin does not add objects into manytomany fields

Question

django admin does not add objects into manytomany fields

Answers 1
Added at 2017-01-05 07:01
Tags
Question

I have a model to save user post:

class Tag(models.Model):
    name = models.CharField(max_length=255, unique=True)

class Post(models.Model):
    user = models.ForeignKey(User)
    body = models.TextField()
    tags = models.ManyToManyField(Tag, blank=True)
    pub_date = models.DateTimeField(default=timezone.now)
    activity = GenericRelation(Activity, related_query_name="posts")

Now whenever I save a new Post object, I would like to save the Post object's body into the Post object's tags field. I am doing this using the signals:

@receiver(post_save, sender=Post)
def create(sender, instance, **kwargs):
    if kwargs.get('created', True):
        tag_list = [Tag.objects.create(name=word) for word in instance.body.split()]
        print "from signals!: instance body: %s" % instance.body
        instance.tags.add(*tag_list)

    if not kwargs.get('created', False):
        tag_list = [Tag.objects.create(name=word) for word in instance.body.split()]
        print "already signals!: instance body: %s" % instance.body
        instance.tags.add(*tag_list)

So if I save a new Post object in the admin, it does create a new Post object, also create tag objects inside tag_list. But... it does not add the tags from tags_list into the object's many to many field (i.e. post_object.tags).

However, if I create a new Post object from shell, it does create a new Post object, create tag objects inside tag_list, and also adds the tags into the post.objects tag field.

What am I doing wrong here? It seems the problem is in django admin.

Answers
nr: #1 dodano: 2017-01-05 12:01

After searching a lot, I finally solved my problem. So, it turns out that if you try to do something to the M2M field in the admin, the change will get wiped out by the clear() automatically.

So, I had to make a new model admin class:

class PostAdmin(admin.ModelAdmin):
    class Meta:
        model = Post

    def save_model(self, request, obj, form, change):
        print "from save_model"
        obj.save()

    def save_related(self, request, form, formsets, change):
        super(PostAdmin, self).save_related(request, form, formsets, change)
        obj = form.instance
        if obj.body:
            tag_list = [Tag.objects.get_or_create(name=word)[0] for word in obj.body.split() if word.startswith("#")]
            obj.tags.add(*tag_list)
            print("from save_related")
            obj.save()

admin.site.register(Post, PostAdmin)

Also I changed how I was making list of tags as @Prakhar pointed out from create() to get_or_create(). Note the indexing [0] during get_or_create(), to get the instance and not the created.

signals.py:

@receiver(post_save, sender=Post)
def create(sender, instance, **kwargs):
    if kwargs.get('created', True):
        tag_list = [Tag.objects.get_or_create(name=word)[0] for word in instance.body.split() if word.startswith("#")]
        print "from signals!: instance body: %s" % instance.body
        instance.tags.add(*tag_list)
    else:
        tag_list = [Tag.objects.get_or_create(name=word)[0] for word in instance.body.split() if word.startswith("#")]
        print "already signals!: instance body: %s" % instance.body
        instance.tags.add(*tag_list)
Source Show
◀ Wstecz