0

I am building an API with Django that allows an external app to send posts to my API an then it inserts the received data into my database.

Here are my simplified models:

class Region(models.Model):
    name = models.CharField(max_length=250)

class Event(models.Model):
    name = models.CharField(max_length=250)
    region = models.ManyToManyField(Region)

I receive from the api some new Event data like this:

data = {
    "name": "First Event",
    "region": "4"  # this is the ID of the region
}

I'm trying to create a new event like this:

Event.objects.create(**data)

But I get the following error:

Direct assignment to the forward side of a many-to-many set is prohibited. Use region.set() instead. 

The problem is that I can not use new_event.region.add(4) cause region is a string and, since this is an API, the field is always retrieved as a json key.

I read this question https://stackoverflow.com/questions/8360867/update-many-to-many-field-in-django-with-field-name-in-variable#= which is asking exactly the same, but the given answer is not working cause when I write:

new.__getattribute__(relation_name) = my_authors

as suggested there, i get the following error, which makes sense:

SyntaxError: can't assign to function call

So how can I add a manytoMany field value by the fieldName?

  • 1
    Your approach wont work even if you mokey-patch it, It just can't happen in a single statement, at least two are needed - 1 to create the instance and 2 to attach the m2m fields. I would suggest to take a look at [DRF](https://www.django-rest-framework.org/) and use [Serializers](https://www.django-rest-framework.org/api-guide/serializers/) in order to achieve what you want. – Todor Mar 31 '20 at 12:41
  • @Todor it can work. Check my answer – Wariored Mar 31 '20 at 13:09
  • @Todor I don't need to execute in in 1 single statement, i just need to make it work. I'm already using DRF but serializers don't allow me to update a many2many relation by the fieldname – Sergio Ferrer Sánchez Mar 31 '20 at 13:42
  • If you are using DRF, maybe show your code, show the serializers, etc. What you are writing makes no sens: """The problem is that I can not use new_event.region.add(4) cause region is a string and, since this is an API, the field is always retrieved as a json key.""" 1) I don't see how `region` is a string here if `new_event` is a model instance. 2) JSON support integers not only strings. 3) `new_event.region.add(4)` should work even if 4 is a string, but it can be converted to int too. – Todor Mar 31 '20 at 15:02
  • @Todor It probably makes no totally sense cause it is a simplified version of the whole software. To clarify, I need to do this https://stackoverflow.com/questions/8360867/update-many-to-many-field-in-django-with-field-name-in-variable#_=_ but the answer there does not work. Notice that the field to update (in this case, "region" is the field) will always be stored in a variable as a string. This time is only region cause this is a simplified version, but there are a lot of fields, so I need to update the field passed as argument in the post data – Sergio Ferrer Sánchez Mar 31 '20 at 15:05
  • 1
    have you tried with a simple: `getattr(new_event, "region").add(4)` – Todor Mar 31 '20 at 15:07
  • @Todor This is exactly the answer I was looking for!! Thank you so much. If you write an answer post I'll vote for it. Thanks again!! – Sergio Ferrer Sánchez Mar 31 '20 at 15:13

1 Answers1

5

The given answer in the linked question does not work, because it's nine years old and back then assigning m2m field used to be like this: new_event.region = [4]

However this has been deprecated long time ago, and now the syntax is:

new_event.region.set(4)

that's exactly why you get the following error:

Direct assignment to the forward side of a many-to-many set is prohibited. Use region.set() instead.`

Because what you are actually trying to do is:

```new_event.region = 4`

Of course this won't work.

However the rest of the code should be fine (i.e. the __getattribute__ part). So what you need to do is call the set/add method of what __getattribute__ returns. i.e.

new_event.__getattribute__("region").add(4)

but instead of using the __getattribute__ dunder method, you can simplify it to:

getattr(new_event, "region").add(4)

Todor
  • 15,307
  • 5
  • 55
  • 62