Mohsen Amiri
Mohsen Amiri

Reputation: 175

making a retrieve api for a manytomany field

I am making an API with django rest framework and what it's supposed to do is, it has to return the movies that a certain celebrity has acted or directed in.

And I dont know how to do that. because the film model has manytomany fields called filmActor and filmDirector and I want to preferably make this api by extending the ModelSerializer class.

# Models.py

class Celebrity(models.Model):
    celebID = models.AutoField(primary_key=True)
    nameOf = models.CharField(max_length=100, unique=True)
    details = models.TextField(null=True)

class Film(models.Model):
    filmID = models.AutoField(primary_key=True)
    title = models.CharField(max_length=150)
    filmActor = models.ManyToManyField(Celebrity, related_name='actor')
    filmDirector = models.ManyToManyField(Celebrity, related_name='director')

# Serializers.py
class CelebrityRetrieveSerializer(serializers.ModelSerializer):
    # TO BE DONE

Upvotes: 1

Views: 488

Answers (3)

Abdulaziz
Abdulaziz

Reputation: 1

models.py

class Actor(models.Model):
    image = models.ImageField("Image", upload_to='cast/')
    name = models.CharField("Name", max_length=255)
    type = models.CharField('Type', max_length=255, default='Actor')
    description = models.TextField('About')
    slug = models.SlugField("Link", unique=True)

class Meta:
    verbose_name = "Actor"
    verbose_name_plural = "Cast"

def current_path(self):
    return "/Actor/" + str(self.name) + "/"

def get_absolute_url(self):
    return reverse('actor_detail_url', kwargs={'slug': self.slug})

def __str__(self):
    return self.name

class Movie(models.Model):
    poster = models.ImageField(
    'Poster', blank=True, upload_to='posters/')
    movie = models.FileField("Movie Copy", upload_to="movies/")
    title = models.CharField('Title', max_length=255)
    description = models.TextField('Description')
    genre = models.ManyToManyField(
    Genre, blank=True, verbose_name='Genre')
    director = models.ManyToManyField(
        Director, blank=True, verbose_name='Director')
    cast = models.ManyToManyField(
        Actor, blank=True, verbose_name='Cast')
    imdb_rating = models.ForeignKey(
        IMDb_Rating, on_delete=models.CASCADE, null=True, verbose_name="IMDb Rating")
    rotten_tomatoes_rating = models.ForeignKey(
        Rotten_Tomatoes_Rating, on_delete=models.CASCADE, null=True, verbose_name="Rotten Tomatoes Rating")
    other_rating = models.ForeignKey(
        Other_Rating, on_delete=models.CASCADE, null=True,     verbose_name="Other Rating")
    budget = models.CharField('Budget', max_length=255,
                          default="20 million", blank=True)
    box_office = models.CharField(
        'Box Office', max_length=255, default="1.50 billion", blank=True)
    composers = models.ManyToManyField(
        Composer, blank=True, verbose_name="Composers")
    date = models.DateField('Release Date', default=timezone.now)
    country = CountryField(blank_label='Select Country')
    Quality = models.CharField("Quality", max_length=255, default="720p")
    runtime = models.CharField("Runtime", max_length=255, default="1h 20m")
    publication = models.BooleanField("Publication", default=True)
    slug = models.SlugField('Link', unique=True)

class Meta:
    verbose_name = 'Movie'
    verbose_name_plural = 'Movies'

def get_absolute_url(self):
    return reverse('movie_detail_url', kwargs={'slug': self.slug})

def __str__(self):
    return self.title

movie_detail.html

<div class="cast-column">
            {% for actor in movie.cast.all %}
            <a href="{% url 'actor_detail_url' actor.slug %}">
                <div class="actor" style="{% if not forloop.first %} margin-left: 8px; {% endif %}">
                    <div class="actor-picture">
                        <img src="{{actor.image.url}}" alt="{{actor.name}}">
                    </div>
                    <div class="actor-n_r">
                        <div class="actor-name">{{actor.name}}</div>
                        <div class="actor-role">{{role.role}}</div>
                    </div>
                </div>
            </a>
            {% endfor %}
        </div>

Upvotes: 0

Mohsen Amiri
Mohsen Amiri

Reputation: 175

The answer that @ThomasGth gave, works only if you have 1 foreign-key or many-to-many field.

to do this with two foreign keys, you have to name the field in your serializers.py, the name that was given to the related_name field in your models.py. so the code should be something like this:

class CelebrityRetrieveSerializer(serializers.ModelSerializer):
    class FilmSerializer(serializers.ModelSerializer):
        class Meta:
            model = Film
            fields = ('title',)
    
    actor = FilmSerializer(read_only=True, many=True,)
    director = FilmSerializer(read_only=True, many=True,)

    class Meta:
        model = Celebrity
        fields = '__all__'

You also have to make sure to give a value to related_name that does not conflict with the other names you have used in your code. for example if you set it to related_name = Film it could cause problems.

Upvotes: 1

ThomasGth
ThomasGth

Reputation: 870

You can access many-to-many related objects thanks to "_set". Something like this should work:

class FilmSerializer(serializers.ModelSerializer):

    class Meta:
        model = Film
        fields = ('filmID', 'title')

class GenreRetrieveSerializer(serializers.ModelSerializer):
    film_set = FilmSerializer(many=True)
    
    class Meta:
        model = Genre
        fields = '__all__'

Upvotes: 1

Related Questions