Question

Banging head against the wall again. I'm trying to add tags using other known fields

# models.py
class MyModel(models.Model):
    ...
    tags = models.ManyToManyField(Tag, blank=True)
    field_m2m = models.ManyToManyField('M2mModel', blank=True)
    field_fk = models.ForeignKey('FkModel', blank=True, null=True)
    ...
    def save(self, *args, **kwargs):
        for inst in self.field_m2m.all():
            self.tags.add(Tag.objects.get(name=inst.name))
        self.tags.add(Tag.objects.get(name=self.field_fk.name))
        super(MyModel, self).save(*args, **kwargs)

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

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

I am 100% sure my field_m2m and field_fk aren't empty and what's not less important: there are instances corresponding to EXISTING tags. I have other functions covering this part well. I have also tried hardcoding the strings (Tag.objects.get(name="mystring")) to be 101% sure.

Yet, no tags are assigned through admin panel. I tried to go through the steps in shell and it works there.

>>> m = MyModel.objects.get(name='something')
>>> t = Tag.objects.get(name='sometag')
>>> m.tags.add(t)
>>> m.tags.all()
[<Tag: sometag>]

How to make it work from save() method?

Also until the the model instance is created for the first time, traceback is complaining about: "<MyModel: Something>" needs to have a value for field "mymodel" before this many-to-many relationship can be used.

I guess I should save the model instance before even doing aforementioned assignments, right? How can I do it all at once?

Was it helpful?

Solution 2

Figured it out thanks to this hint: https://stackoverflow.com/a/6200451/1344854
Model save() method stays default. First I tied a tag to my FkModel and M2mModel instances. The rest of the job is done in ModelAdmin.

# models.py
class FkModel(models.Model):
    name = models.CharField(max_length=255, unique=True)
    tag = models.ForeignKey(Tag, blank=True, null=True, related_name='fk')
    ...

class M2mModel(models.Model):
    name = models.CharField(max_length=255, unique=True)
    tag = models.ForeignKey(Tag, blank=True, null=True, related_name='m2m')
    ...

# admin.py
class MyModelAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        form.save() # as I learned, without saving at this point, obj.field_m2m.all() will be empty
        tags = []
        if obj.field_m2m.all():
            tags += [m2m.tag for m2m in obj.field_m2m.all()]
        if obj.field_fk:
            tags += [obj.field_fk.tag]
        form.cleaned_data['tags'] = tags
        super(MyModelAdmin, self).save_model(request, obj, form, change)

OTHER TIPS

Seems to me that your MyModel instance must be saved into database before saving any relationships. It makes sense because for the relationships, the MyModel's id is needed. So you can change the order of the save method like this:

def save(self, *args, **kwargs):
    # notice that super class save go first.
    super(MyModel, self).save(*args, **kwargs)
    # also this cicle doesn't make sense since self is a newly
    # created instance so it won't have anythin in field_m2m.all()
    for inst in self.field_m2m.all():
        self.tags.add(Tag.objects.get(name=inst.name))
    # this should work ok if get returns any tag.
    self.tags.add(Tag.objects.get(name=self.field_fk.name))

Hope this helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top