0

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?

gwaramadze
  • 4,523
  • 5
  • 30
  • 42

2 Answers2

2

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!

Paulo Bu
  • 29,294
  • 6
  • 74
  • 73
  • Thanks, it helps with the second issue. I still cannot assign tags. – gwaramadze Jun 05 '13 at 19:33
  • Sorry but your question is quite big, what's the first issue? I can't find it, can you edit and be more specific? – Paulo Bu Jun 05 '13 at 19:46
  • In the snipped you provided, that part below super doesn't bring any results. self.tags.add doesn't add anything – gwaramadze Jun 05 '13 at 19:53
  • Ok MyModel instance is getting saved to the database but still without any tags. Are you positive sure that There are tags in your database? Also you cicle throug self.field_m2m.all() but loop is invalid because the instance is new so there will be no tags objects associated to MyModel instance yet. – Paulo Bu Jun 05 '13 at 20:02
  • 100% sure there are tags with exactly the same 'name' (otherwise exception is thrown - I tried hardcoding something fake). self.field_m2m isn't empty, I add some items before hitting save. Same with field_fk. – gwaramadze Jun 05 '13 at 20:11
  • Try to add this line again at the end of the method, to save the object twice and see if like that it works: `super(MyModel, self).save(*args, **kwargs)` – Paulo Bu Jun 05 '13 at 20:29
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/31285/discussion-between-remy-g-and-paulo-bu) – gwaramadze Jun 05 '13 at 20:52
0

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)
Community
  • 1
  • 1
gwaramadze
  • 4,523
  • 5
  • 30
  • 42