2

I want to be able to update a many-to-many field on a model, when the name of the field is stored in a variable. This is relatively easy for a normal field (with kwargs in the constructor), or even a foreign key, but not so for a many-to-many field. Here's the situation:

class Book(models.Model):
    title = models.CharField(max_length=30)
    authors = models.ManyToManyField(Author)

field_name = 'title'
new = Book(**{field_name:'My Book'}) # This sets title to mybook
new.save()
my_authors = Author.objects.filter(name='Ben') # Get some authors
field_name = 'authors' 
new.XXXXXX = my_authors # Add them

How to do the XXXXXX bit is what I'm asking! It isn't accepted in the kwargs like other fields, as you need a primary key first and that can't happen until it's been saved. Any ideas? Any help very greatly appreciated! It must be possible - the django admin must do it some how but I can't find where.

*Edit - I'm ideally looking for a solution that doesn't use __getattribute or __setattr - though they do work, they're not ideal. Also, I can't alter the model in any way when doing this, that's somebody else's code. Thanks!

Thanks guys. Ben

Ben
  • 6,687
  • 2
  • 33
  • 46

2 Answers2

4

Reading your answer, I think that this is you are looking for:

relation_name = 'authors'
new.__getattribute__(relation_name) = my_authors

With out parametrize relation name django n:m doc say:

new.authors = my_authors
dani herrera
  • 48,760
  • 8
  • 117
  • 177
  • Thanks, I meant to say in my question that I know you can do it with new.__setattr__(field, my_authors) - but I wanted to avoid using __setattr__ as it uses underlying python methods of objects that shouldn't really be accessed unless absolutely necessary! Thanks. – Ben Dec 02 '11 at 18:56
  • then, this is not a solution for you? You can write a custom method on your model and that method can access to setattr. – dani herrera Dec 02 '11 at 18:58
  • Yes, this does work - but I was trying to avoid using either getattribute or setattribute. Also, I can't change the models in anyway - they're not my code. – Ben Dec 02 '11 at 19:01
  • I think they are not a way to parametrize method name without use underlying python methods. Perhaps you can write a wraper to make this code centralized at one point for future refactoring. – dani herrera Dec 02 '11 at 19:11
  • I was afraid this might be the case! Although, there must be a way, because the django-admin must use a simliar method when saving models etc. Unless the admin code uses setattr as well? I guess I'll dig more into that code! – Ben Dec 02 '11 at 19:41
  • It seems that admin use intensively underlying methods: func = getattr(self.__class__, action).... – dani herrera Dec 02 '11 at 19:49
  • Then perhaps that is the way to go...! – Ben Dec 02 '11 at 19:57
  • I don't know. I have not enought django and python level to assert this. Sorry. Perhaps you can repost the query with new requeriments (like avoid __medthods) – dani herrera Dec 02 '11 at 19:59
0

This assumes that the authors are not being created, but being added to the M2M field for the book.

my_authors = Author.objects.filter(name='Ben') # Get some authors

# This will append all authors from my_authors to the M2M field.
for a in my_authors:
    new.authors.add(a) 

Is this what you are looking for?

Furbeenator
  • 8,106
  • 4
  • 46
  • 54
  • 1
    Thanks, but no - the word 'authors' is stored in a variable, so I can't do new.authors.add(). Also, you can just do new.authors.add(my_authors) in this instance - it accepts a list! Thanks though :) – Ben Dec 02 '11 at 18:54
  • Could you do something like this? I haven't tested, but you may be able to create a dictionary like a switch statement. `fields = {'authors': new.authors, 'title': new.title}` and `fields[field_name].add(my_authors)` – Furbeenator Dec 02 '11 at 19:41