0

Since there are a few questions about m2m and DRF I'll try narrow down what specifically I'm interested in. Let's call the two models 'article' and 'publication'. Assume that:

  1. The 'publication' object already exists.

  2. The 'article' object may or may not exist. specifically:

    a) If a previous publication contained the article, then it will already be there.

    b) If not, then the article will need to be created.

  3. I want to send a post http request with the article data in the body and the publication id available from the url which will:

    a) if the article already exists, link it to the publication

    b) if the article does not exist, create it, and then link it to the publication

Going for the 'default' strategy below did not work out. I can think of two ways to approach this problem:

  1. Overriding the create method on the article serializer. However I'm scepticle of doing that since this seems like a problem that should be common and have a non-custom solution.

    1. Creating an endpoint to directly work with the 'through' model. I could then split up the process into two steps (and 2 requests) where I first get_or_create the article, and then post to the through model endpoint to create the link.

Are there any other approaches or built-in DRF solutions to this problem?

Here's where I'm at currently:

models.py

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

class Article(models.Model):
    major = models.IntegerField()
    minor = models.IntegerField()
    publication = models.ManyToManyField(Publication)

    class Meta:
        constraints = [models.UniqueConstraint(fields=['major', 'minor'], name='unique_article')]

views.py

class ArticleViewSet(viewsets.ModelViewSet):
    serializer_class = ArticleSerializer
    queryset = Article.objects.all()

serializers.py

class ArticleSerializer(serializers.ModelSerializer):
    publication = serializers.SlugRelatedField(slug_field='name', queryset=Publication.objects.all()), many=True)

    class Meta:
        model = Article
        fields = '__all__'

When posting to this endpoint I'll get a 'duplicate entry' integrity error if the article does already exist, instead of the article then just being linked.

Neil
  • 3,020
  • 4
  • 25
  • 48

1 Answers1

1

This is the way I have handled this issue in the past. If your using the Primary keys these calls are not very expensive.

pub = Publications.objects.get(id=1)


article, created = Articles.objects.get_or_create(
id=1,
defaults= {other_params:'value', param : 'value'},
) 

pub.articles.add(article)
CR Python
  • 146
  • 6
  • where do you do this? In create in the serializer? – Neil Nov 16 '19 at 19:16
  • The short answer is either one but you will need to switch to an APIView to do it in the view check out this thread it has a really detailed answer https://stackoverflow.com/questions/25026034/django-rest-framework-modelserializer-get-or-create-functionality – CR Python Nov 16 '19 at 20:05
  • Okay. Still don't think this is ideal but I've implemented it and it works. Just fix the last line (pub.articles.add(article)) and I'll accept. – Neil Nov 16 '19 at 22:56